问题描述
我目前面临一个奇怪的问题,即相同的函数输出不同的结果。该函数应该计算提供的日期和当前时间之间的时差。由于此函数应该以毫秒为单位工作,因此我的函数目前如下所示:
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
不正确。 tm
和 SYSTEMTIME
结构都使用从 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';
}
}