【问题标题】:UTC Timestamp comparisonUTC 时间戳比较
【发布时间】:2016-07-26 11:13:58
【问题描述】:

我正在开发一个客户端-服务器自定义应用程序(将在 linux 上运行),我发送的其中一个帧包含一个时间戳(即发送帧的时间)。

为了使我的应用程序可靠,我使用gmtime 在客户端生成时间。我在比利时,所以现在客户端 VM 的时间比 UTC 时间晚 2 小时(因为夏季时间)。

在服务器端,我首先将收到的字符串转换为time_t 类型。我这样做是为了使用difftime 函数来查看时间戳是否太旧。 然后,我再次使用gmtime 生成时间戳(以UTC 时间表示),并将其转换为time_t

然后我比较两个time_t 来查看时间差。

我在服务器端的时间转换有问题。 我使用和客户端一样的代码,但是输出的gmtime不一样...


客户端:函数生成时间戳,并将其导出为字符串(time_str):

    std::string getTime()
    {
    time_t rawtime;
    struct tm * timeinfo;
    char buffer[80];

    time (&rawtime);              // Get time of the system
    timeinfo = gmtime(&rawtime);  // Convert it to UTC time

    strftime(buffer,80,"%d-%m-%Y %H:%M:%S",timeinfo);
    std::string time_str(buffer); // Cast it into a string
    cout<<"Time Stamp now (client) : "<<time_str<<endl;

    return time_str;
    }

它会产生这个(在当地时间 9 点 33 分):

现在时间戳:06-04-2016 07:33:30


服务器端: 获取时间戳、生成 newx 时间戳并进行比较的功能:

bool checkTimeStamp(std::string TimsStamp_str, double delay)
{
    cout<<"TimeStamp recieved: "<<TimsStamp_str<<endl;
    /* Construct tm from string */
    struct tm TimeStampRecu;
    strptime(TimsStamp_str.c_str(), "%d-%m-%Y %I:%M:%S", &TimeStampRecu);
    time_t t_old = mktime(&TimeStampRecu);

    /* Generate New TimeStamp */
    time_t rawtime;
    struct tm * timeinfo;
    time (&rawtime);  // Get time of the system
    timeinfo = gmtime(&rawtime);  // convert it to UTC time_t
    time_t t2 = mktime(timeinfo);  // Re-Cast it to timt_t struct
    /* Convert it into string (for output) */
        char buffer[80];
        strftime(buffer,80,"%d-%m-%Y %H:%M:%S",timeinfo);
        std::string time_str(buffer); // Cast it into a string
        cout<<"Time Stamp now (server) : "<<time_str<<endl;

    /* Comparison */
    double diffSecs = difftime(t2, t_old);
    cout<<diffSecs<<endl;
    bool isTimeStampOK;
    if (diffSecs < delay)
        isTimeStampOK = true;
    else
        isTimeStampOK = false;

return isTimeStampOK;
}

它产生了这个(在比利时的 9 点 33 分):

收到的时间戳:06-04-2016 07:33:30

现在时间戳(服务器):06-04-2016 08:33:31


为什么服务器时间 (8h33) 既不是本地时间 (9h33),也不是 UTC 时间 (7h33)?

我在它的世代中犯了错误吗?我不明白在哪里,因为这些是与客户端完全相同的代码......

【问题讨论】:

  • 我忘了说这两个虚拟机是本地时间,运行CentOS。

标签: c++ linux timestamp utc localtime


【解决方案1】:

您的代码中有几个错误,有些是您的错,有些不是。这里最大的问题是 C &lt;time.h&gt; API 非常糟糕、令人困惑、不完整且容易出错,以至于像这样的错误几乎是强制性的。稍后会详细介绍。

第一个问题是这一行:

struct tm TimeStampRecu;

它创建一个未初始化的tm,然后将其传递给strptimestrptime 可能不会填写TimeStampRecu 的所有字段。您应该像这样对TimeStampRecu 进行零初始化:

struct tm TimeStampRecu{};

下一个问题:

strptime(TimsStamp_str.c_str(), "%d-%m-%Y %I:%M:%S", &TimeStampRecu);

%I 表示的 12 小时时间在没有 AM/PM 说明符的情况下是不明确的。我怀疑这只是一个 type-o,因为它是你唯一使用它的地方。

下一个问题:

gmtime  time_t -> tm  UTC to UTC
mktime  tm -> time_t  local to UTC

也就是说,mtkime 根据运行它的计算机的本地时间来解释输入 tm。你需要的是:

timegm  tm -> time_t  UTC to UTC

很遗憾,timegm 不是标准 C(或 C++)。幸运的是,它可能仍然存在于您的系统中。

通过这些更改,我认为您的代码将按预期运行。


如果您使用 C++11,这里有一个更安全的日期/时间库(免费/开源):

https://github.com/HowardHinnant/date

这是您的代码翻译成使用这个更高级别的库:

std::string getTime()
{
    using namespace std::chrono;
    auto rawtime = time_point_cast<seconds>(system_clock::now());
    auto time_str = date::format("%d-%m-%Y %H:%M:%S", rawTime);
    std::cout<<"Time Stamp now (client) : "<<time_str<< '\n';

    return time_str;
}

bool checkTimeStamp(std::string TimsStamp_str, std::chrono::seconds delay)
{
    using namespace std::chrono;
    std::cout<<"TimeStamp recieved:       "<<TimsStamp_str<< '\n';
    /* Construct tm from string */
    std::istringstream in{TimsStamp_str};
    time_point<system_clock, seconds> t_old;
    date::parse(in, "%d-%m-%Y %H:%M:%S", t_old);

    /* Generate New TimeStamp */
    auto rawtime = time_point_cast<seconds>(system_clock::now());
    auto time_str = date::format("%d-%m-%Y %H:%M:%S", rawTime);
    in.str(time_str);
    time_point<system_clock, seconds> t2;
    date::parse(in, "%d-%m-%Y %H:%M:%S", t2);
    cout<<"Time Stamp now (server) : "<<time_str<<endl;

    /* Comparison */
    auto diffSecs = t2 - t_old;
    cout<<diffSecs.count()<<endl;
    bool isTimeStampOK;
    if (diffSecs < delay)
        isTimeStampOK = true;
    else
        isTimeStampOK = false;

    return isTimeStampOK;
}

【讨论】:

    【解决方案2】:

    时间戳是一个绝对值。它不依赖于时区或 DST。它表示自过去某个固定时刻以来的秒数。无论服务器的时区是什么,time() 返回的值都是一样的。

    您的代码不会生成时间戳,而是生成日期格式以供人类使用。

    我建议您在内部使用时间戳并将其格式化为仅在 UI 中人类可读的日期。但是,如果您需要在应用程序的各个组件之间将日期作为字符串传递,也可以将时区放入其中。

    我会这样写你的代码(只使用时间戳):

    // The client side
    time_t getTime()
    {
        return time(NULL);
    }
    
    
    // The server side
    bool checkTimeStamp(time_t clientTime, double delay)
    {
        time_t now = time(NULL);
    
        return difftime(now, clientTime) < delay;
    }
    

    更新

    如果您必须使用字符串在客户端和服务器之间进行通信,那么您所要做的就是更新用于将时间戳格式化为日期的格式字符串(在客户端)并将日期解析回时间戳(在服务器上) ),以包括您用于格式化日期的时区。

    这意味着将%Z 添加到代码中任何位置的格式中。使用"%d-%m-%Y %H:%M:%S %Z"。顺便说一句,您现在发布的代码在对strptime() 的调用中显示为"%d-%m-%Y %I:%M:%S",这将在下午给您带来另一个问题。

    备注

    我总是使用"%Y-%m-%d %H:%M:%S %Z",因为它明确且独立于语言环境。 06-04-2016 可以解释为April 6June 4,具体取决于读者的母语。

    【讨论】:

    • 我必须在客户端和服务器之间传递字符串。而要将time_t 转换为字符串,我必须经过struct tm,它由localtimegmtime 生成。这就是错误的来源。
    • 我用您的代码所需的修复更新了答案(继续使用字符串)。
    • 我已将您的更改考虑在内,但错误仍然存​​在...服务器生成的时间戳为CEST,但此时间戳中指示的小时不是 CEST 时间。 .. 我得到了这个:2016-04-06 10:00:25 CEST (在 CEST 11:00 运行时)...
    猜你喜欢
    • 2016-09-07
    • 2012-07-09
    • 2017-07-22
    • 2023-03-20
    • 1970-01-01
    • 2020-08-20
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多