C++ std::tm 从 std::chrono::time_point 转换后返回错误的值



TL;DR:如何使用std::chrono::system_clock::time_point仅基于某些参数进行比较(例如,我只想要小时、分钟和秒,而不是日、月等)。

另外:将std::chrono::system_clock::time_point转换为std::tm后,std::tm.tm_hours包含的值比最初输入到std::chrono::system_clock::time_point的值高一。


我让std::chrono::system_clock::time_point发挥作用的理论方法:

typedef std::chrono::system_clock::time_point TimePoint;
TimePoint MainWindow::createTimePoint(int h, int m)
{
TimePoint createdTime = std::chrono::system_clock::time_point{std::chrono::hours(h) + std::chrono::minutes(m)};
time_t tt = std::chrono::system_clock::to_time_t(createdTime);
tm timeExtracted = *localtime(&tt);
std::cout << "input:tt" << "H = " << h << ", M = " << m << std::endl; 
std::cout << "timeExtracted:t" << "H = " << timeExtracted.tm_hour << ", M = " << timeExtracted.tm_min << std::endl; 
return createdTime;
}

如果我运行这个,timeExtracted小时总是输入h的 +1 .

为什么会这样?以及如何解决这个问题?我浏览了其他一些显示这一点的帖子,但他们无法帮助我。可能也是因为这个:

我认为当我创建一个TimePoint时,日、月等也会设置为随机值或启动到某个值。关键是:我希望它们始终是相同的值,以便我的TimePoint(转换后)基本上显示以下内容:

timeExtracted.tm_sec = 0
timeExtracted.tm_min = m
timeExtracted.tm_hour = h
timeExtracted.tm_mon = 0
timeExtracted.tm_wday = 0
timeExtracted.tm_mday = 0
timeExtracted.tm_yday = 0
timeExtracted.tm_year = 0
timeExtracted.tm_isdst = 0

我如何使用 std::chrono 的比较操作来比较其中两个TimePoint,但只比较小时和分钟。


如果我的问题不清楚,对不起,已经很晚了。我明天早上再检查。谢谢。

我将开始一个答案,但这不会是一个完整的答案,因为我还不确定完整的问题。 但是,我可以提供帮助。

TimePoint createdTime = system_clock::time_point{hours(h) + minutes(m)};

(我剪掉了std::chrono::限定符,以便更容易阅读和讨论)

这将创建一个时间戳,即 1970-01-01 hh:mm:00 UTC。 简而言之,system_clock::time_point正在测量自 1970 年新年 UTC 以来的持续时间(以微秒或纳秒等单位)。 从技术上讲,以上是一个近似值,system_clock不计算闰秒,但我们可以(并且应该)暂时忽略这个细节。

这:

tm timeExtracted = *localtime(&tt);

将根据计算机对本地时区的设置引入 UTC 偏移校正。 时区调整规则(希望)将基于您所在地区 1970 年生效的规则。

有一些技术和库可以获取system_clock::time_point并将其分解为诸如{year, month, day, hours, minutes, seconds, microseconds}之类的领域。 但是,这种转换还取决于您是否希望这些字段采用 UTC、本地时间或其他任意时区。

如果需要,第一步是应用与某个时区关联的 UTC 偏移量。 如果您的{h, m}输入在将其放入system_clock::time_point之前可能需要 UTC 偏移量调整,如果目的是{h, m}表示本地时间而不是 UTC。

更新:商店营业时间示例

此示例将使用我的免费开源时区库,因为我觉得它更容易使用,并且允许更具可读性和表现力的代码。

此示例将一个system_clock::time_point作为输入,并将其与一周中每天的开盘/收盘时间列表进行比较,并确定输入时间是在与输入时间t关联的工作日的一天中的时间范围之内还是之外。 假定商店营业时间是相对于商店的本地时区声明的,该时区也是为运行此代码的计算机设置的当前时区。

#include "date/tz.h"
#include <algorithm>
#include <cassert>
#include <chrono>
bool
is_store_open_at(std::chrono::system_clock::time_point tp)
{
using namespace date;
using namespace std::chrono;
struct day_schedule
{
weekday wd;
minutes open;
minutes close;
};
// hours are expressed in terms of local time
static constexpr day_schedule store_hours[]
{
// week day   open-time   close-time
{Monday,    0h,                0h},  // closed all day
{Tuesday,   8h,               18h},
{Wednesday, 8h,               18h},
{Thursday,  8h,               18h},
{Friday,    8h,               18h},
{Saturday,  8h,         15h+30min},
{Sunday,    9h+30min,         15h}
};
auto local_tp = current_zone()->to_local(tp);
auto local_day = floor<days>(local_tp);
auto local_time_of_day = local_tp - local_day;
weekday local_weekday{local_day};
auto ds = std::find_if(std::begin(store_hours), std::end(store_hours),
[local_weekday](day_schedule const& x)
{
return x.wd == local_weekday;
});
assert(ds != std::end(store_hours));
return ds->open <= local_time_of_day && local_time_of_day < ds->close;
}
#include <iostream>
int
main()
{
std::cout << is_store_open_at(std::chrono::system_clock::now()) << 'n';
}

该函数首先定义一些方便的数据结构来存储一周中每天的打开和关闭时间。day_scheduleopenclose成员以当地时间测量"自午夜以来的分钟数"。

输入时间tp以 UTC 为单位,因为它的类型是system_clock::time_point。 目前C++标准没有规定,但将在明年的C++20中指定。

zoned_seconds用于根据调用current_zone()获得的计算机时区设置,将UTC时间t转换为本地时间。 我已将t截断为秒以简化一些语法。 这不是绝对必要的。我已经编辑了使用稍微简单的语法来消除zoned_secondszoned_seconds在其他示例中非常有用,但是在这个示例中,麻烦大于其价值。auto local_tp = current_zone()->to_local(tp)是将 UTC 转换为本地时间点的更简单方法。

local_tp是一个被认为是"本地时间"的chrono::time_point,与与system_clock相关的chrono::time_point族不同。 这样做的好处是,如果本地时间和 UTC 时间意外混合,则为编译时错误。

local_days只是local_tp截断以days精度。 它仍然是一个chrono::time_point,只是一个粗略的,指向当地时区所描述的一天的开始。

自当地午夜以来的持续时间简直是local_tp - local_day.

可以通过将local_day转换为类型weekday来获取星期几(由本地时区定义)。 这是与tp相关的一周中的当地日期。

现在,store_hours搜索与local_weekday匹配的条目是一件简单的事情。

如果商店处于或超过open时间且尚未达到close时间,local_time_of_day则商店将开放。

如果"商店营业时间"以 UTC 而不是本地时间指定,则此程序有所简化,但仍然相似。

最新更新