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

问题描述

我目前面临一个奇怪的问题,即相同的函数输出不同的结果。该函数应该计算提供的日期和当前时间之间的时差。由于此函数应该以毫秒为单位工作,因此我的函数目前如下所示:

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,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 也应返回 UTC 时间。

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

这个错误的原因是什么?

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

感谢所有帮助。

解决方法

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

#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 完全披露:我是这个库的主要作者。我不追求从这项努力中获得任何经济利益。但有时如果我不完全披露这些信息,人们会感到不安。

,

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

,

根据 std::get_time API,I/O 操纵器 std::get_time 使用 I/O 流的区域设置std::time_get 方面将文本输入转换为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';
    }
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...