计算两个时间戳之间的差异时,相同的函数输出不同的结果

Same function outputs different results when calculating difference between two timestamps

我目前遇到一个奇怪的问题,即相同的函数输出不同的结果。该函数应该计算提供的日期和当前时间之间的时差。因为这个函数应该以毫秒为单位工作,所以我的函数目前看起来像这样:

int calcDelay(std::string dropTime) {

    struct tm tm;
    std::istringstream iss(dropTime);
    iss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
    time_t time = mktime(&tm);

    SYSTEMTIME t;
    GetSystemTime(&t);
    struct tm tm1;
    memset(&tm1, 0, sizeof(tm1));
    tm1.tm_year = t.wYear - 1900;
    tm1.tm_mon = t.wMonth - 1;
    tm1.tm_mday = t.wDay;
    tm1.tm_hour = t.wHour - 1;
    tm1.tm_min = t.wMinute;
    tm1.tm_sec = t.wSecond;
    time_t time2 = mktime(&tm1);

    //std::cout << "Input:" << dropTime << " Output:" << (int)(difftime(time, time2) * 1000) - t.wMilliseconds << std::endl;
    int retVal = (int)(difftime(time, time2) * 1000) - t.wMilliseconds;
    return retVal;
}

提供的日期 (dropTime) UTC/GMT 并且 WinAPI 函数 GetSystemTime 也应该 return UTC 时间。

我有两个不同的线程调用这个函数。当第一个线程调用此函数时,它 return 是正确的时间差。但是,当我的另一个线程使用完全相同的输入调用此函数时,它 return 的值恰好大了 3600000 毫秒 - 这正好等于一小时的时间。

这个错误的原因是什么?

编辑:这个错误似乎是由 get_time 函数引起的。即使使用相同的字符串(2021-05-25T21:03:04)来解析时间,它有时会增加一个小时,有时却不会...... 难道get_time函数根本不能跨多线程使用?

感谢所有帮助。

t.wHour - 1 不正确。 tmSYSTEMTIME 结构都使用从 0...23 开始的小时。

在 C++20 中,您的 calcDelay 可以大大简化。 free, open-source, header-only library1 中存在此功能的预览,它适用于 C++11/14/17。

#include "date/date.h"
#include <chrono>
#include <sstream>

int calcDelay(std::string dropTime) {

    using std::chrono::milliseconds;
    date::sys_time<milliseconds> time;
    std::istringstream iss(dropTime);
    iss >> date::parse("%Y-%m-%dT%H:%M:%S", time);

    auto time2 = date::floor<milliseconds>(std::chrono::system_clock::now());
    return (time - time2).count();
}

正如你在问题中所说,输入是UTC,当前时间是UTC。不涉及时区。与“C 版本”不同的是,此版本可选地支持毫秒精度输入:

std::cout << calcDelay("2021-05-26T00:41:01.568") << '\n';

输出:

12456

将上述 calcDelay 移植到 C++20:

  • 放弃 #include "date/date.h"
  • date::更改为std::chrono::(3个地方)

您还可以(可选)将解析字符串从 "%Y-%m-%dT%H:%M:%S" 简化为 "%FT%T"

也是可选的,您可以通过返回 std::chrono::milliseconds 而不是 int 来提高客户端代码中的类型安全性。


1 完全披露:我是这个库的主要作者。我不会从这项工作中寻求任何经济利益。但有时如果我不完全公开这些信息,人们会感到不安。

根据 std::get_time API,I/O 操纵器 std::get_time 使用 I/O 的 std::time_get 面流的 locale 将文本输入转换为 std::tm 对象。也许同一进程中的所有线程都具有相同的本地语言环境,这是默认行为。所以GetSystemTime(&t);没有问题。
下面的代码是API的例子:

#include <iostream>
#include <sstream>
#include <locale>
#include <iomanip>
 
int main()
{
    std::tm t = {};
    std::istringstream ss("2011-Februar-18 23:12:34");
    ss.imbue(std::locale("de_DE.utf-8"));
    ss >> std::get_time(&t, "%Y-%b-%d %H:%M:%S");
    if (ss.fail()) {
        std::cout << "Parse failed\n";
    } else {
        std::cout << std::put_time(&t, "%c") << '\n';
    }
}