计算两个时间戳之间的差异时,相同的函数输出不同的结果
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
不正确。 tm
和 SYSTEMTIME
结构都使用从 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';
}
}
我目前遇到一个奇怪的问题,即相同的函数输出不同的结果。该函数应该计算提供的日期和当前时间之间的时差。因为这个函数应该以毫秒为单位工作,所以我的函数目前看起来像这样:
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
不正确。 tm
和 SYSTEMTIME
结构都使用从 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';
}
}