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

时间:2019-08-29 23:25:23

标签: c++ time chrono

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:\t\t" << "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进行比较,而仅比较小时和分钟。


如果我的问题不清楚,很抱歉,已经是深夜了。我第二天早上再检查一次。谢谢。

1 个答案:

答案 0 :(得分:1)

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

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

(我已经裁剪了std::chrono::限定词,以便于阅读和讨论)

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

此:

tm timeExtracted = *localtime(&tt);

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

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

非常的第一步是根据需要应用与某个时区关联的UTC偏移量。如果目的是{h, m}代表本地时间而不是UTC,那么您的system_clock::time_point输入可能需要先进行UTC偏移调整,然后再输入{h, m}

更新:营业时间示例

该示例将使用我的free, open-source time zone library,因为我觉得它使用起来更容易,并且允许更易读和更具表现力的代码。

此示例将一个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';
}

该函数从定义一些方便的数据结构开始,以存储一周中每一天的开放和关闭时间。 open的{​​{1}}和close成员在当地时间测量“午夜以来的分钟数”。

由于输入时间day_schedule的类型为tp,因此以UTC表示。 C ++标准目前尚未指定此功能,但明年的C ++ 20将会指定。

system_clock::time_point用于根据通过调用zoned_seconds获得的计算机时区设置将UTC时间t转换为本地时间。我将current_zone()缩短到几秒钟以简化某些语法。这不是严格必要的。我已经编辑为使用稍微简单的语法来消除tzoned_seconds在其他示例中可能确实有用,但是在此示例中,麻烦多于其应有的价值。 zoned_seconds是将UTC转换为本地时间点的更简单方法。

auto local_tp = current_zone()->to_local(tp)是被认为是“本地时间”的local_tp,它不同于与chrono::time_point关联的chrono::time_point族。这样做的好处是,如果本地时间和UTC时间意外混合,则是编译时错误。

system_clock被简单地local_days截断为local_tp精度。它仍然是days,只是粗略的一个,指向当地时间段所描述的一天的开始。

从当地午夜开始的持续时间仅为chrono::time_point

可以通过将local_tp - local_day转换为类型local_day来获取星期几(由当地时区定义)。这是与weekday关联的星期几。

现在,只需搜索tp来查找与store_hours匹配的条目。

如果local_weekdaylocal_time_of_day时间或更早并且尚未达到open时间,则商店将打开。

如果在“ UTC”而不是当地时间中指定了“营业时间”,则此程序会有所简化,但仍然相似。