【问题标题】:How to tell if it's before a configured time in C++?如何判断它是否在 C++ 中配置的时间之前?
【发布时间】:2021-12-13 02:50:16
【问题描述】:

我正在尝试找到查看当前时间是否早于指定时间的最佳方法。假设我想看看是不是在 14:32 之前。在 C++ 中执行此操作的最佳方法是什么?理想情况下,我可以构建一些代表 14:32 的时间对象,然后将其与当前时间作为某个对象进行比较。 这就是我现在正在做的事情。相当混乱,使用 3 种不同的时间表示。

int hour_ = 14;
int min_ = 32;
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t tt = std::chrono::system_clock::to_time_t(now);
std::tm utc_tm = *gmtime(&tt);
if ((utc_tm.tm_hour < hour_) || (utc_tm.tm_hour == hour_ && utc_tm.tm_min < min_) ) {
    std::cout << "It's before " << hour_ << ":" << min_ << std::endl;
}

【问题讨论】:

  • 如果你使用 std::chrono::hours 和 std::chrono::minutes 来存储小时和分钟会不会更容易?
  • std::chrono::time_point 定义了通常的不等式运算符,因此将所有内容都转换为该格式应该使比较本身变得微不足道。
  • @Useless 这会比我目前的解决方案更好,因为它只使用两个时间库就可以缩小范围。但是无论如何我将如何在 14:32 创建一个 time_point?
  • 给定一个本地时间t,当t在当天出现两次时会发生什么?当t 根本不出现时会发生什么?我可以看到这些问题的一些合理答案。
  • 我将为 C++20 添加一个答案,在我写这篇文章时,只有 VS 正在发布。如果 C++20 不是一个选项,我将指出如何将 C++20 解决方案转换为我的库。由于它使用时区,因此需要进行一些安装。由于我目前还有其他义务,我的答案是一个小时后......

标签: c++ chrono time-t


【解决方案1】:

这是在 C++20 中实现的方法。稍后我将展示如何将其转换为使用适用于 C++11/14/17 的 free, open-source C++20 chrono preview library

#include <chrono>

bool
is_now_before(std::chrono::minutes local_config_tod)
{
    using namespace std::chrono;

    auto tz = current_zone();
    auto now = system_clock::now();

    auto local_day = floor<days>(zoned_time{tz, now}.get_local_time());
    auto utc_config = zoned_time{tz, local_day + local_config_tod}.get_sys_time();
    return now < utc_config;
}

该参数的类型为minutes,它将被解释为以分钟为单位的本地时间。例如 14:32 由 minutes{872} 表示。这种表示是紧凑的(一个整数),将{hours, minutes} 转换为minutes 很简单(如下所示)。

current_zone() 获取计算机的当前本地时区。此函数中需要此信息两次,因此最好只获取一次。这不仅可以保存结果,还可以避免移动设备中本地时区从您下方(多个呼叫之间)发生变化的问题。

接下来通过system_clock 获得当前时间(仅一次)。这给出了 UTC 中的当前时间。

现在我们有一个选择:

  1. 我们可以用 UTC 进行比较,或者
  2. 我们可以在当地时间进行比较。

在 UTC 偏移量在当前本地日期发生变化(例如开启或关闭夏令时)的极端情况下,以 UTC 进行比较不太容易出错。

要将本地配置时间 (local_config_tod) 转换为 UTC time_point,首先必须找出当前本地日期。通常,这可能与当前的 UTC 日期不同。所以当前的UTCnow必须先转换为本地时间,然后截断为days-precision:

auto local_day = floor<days>(zoned_time{tz, now}.get_local_time());

现在可以通过简单地将 local_daylocal_config_tod 相加来创建本地 time_point。然后可以将此本地 time_point 转换回 UTC(time_point 基于 system_clock 但具有 seconds 精度):

auto utc_config = zoned_time{tz, local_day + local_config_tod}.get_sys_time();

上面的代码行为您处理了极端情况。如果没有从本地时间到 UTC 的唯一(一对一)映射,则抛出异常。异常类型的.what() 将详细描述此映射如何不明确或不存在。

假设上面的映射没有抛出异常,你可以简单的比较这两个UTCtime_points:

return now < utc_config;

这种比较的精度与您的system_clock 的精度相同(通常为微秒到纳秒)。

可以这样练习:

int hour_ = 14;
int min_ = 32;
using namespace std::chrono;
auto b = is_now_before(hours{hour_} + minutes{min_});

如果 14 和 32 是文字(并且您使用的是 C++14 或更高版本),则可以将其缩短为:

auto b = is_now_before(14h + 32min);

如果您使用的是 C++17 之前的标准,zoned_time 结构将需要显式模板参数:

auto local_day = floor<days>(zoned_time<system_clock::duration>{tz, now}.get_local_time());
auto utc_config = zoned_time<minutes>{tz, local_day + local_config_tod}.get_sys_time();

如果您想使用free, open-source C++20 chrono preview library,请添加#include "date/tz.h"using namespace date;Some installation is required.

如果您想避免在local_day + local_config_tod 没有与UTC 的唯一映射的情况下出现异常,也可以对is_now_before 进行细微更改。但您必须决定以下事项:我要与local_day 的第一个或第二个local_config_tod 进行比较(以防UTC 偏移已减少)。

哎呀!配置时间已经是 UTC 了吗?

在重新阅读您的问题时,我突然想到我可能误读了您的问题。如果 14:32 是 UTC,那么事情会变得非常非常简单!与其删除显示本地 14:32 解释的答案,我认为最好添加此内容,以便未来的读者可以选择任一解决方案。

假设配置是 UTC 时间,那么时区根本不起作用:

#include <chrono>

bool
is_now_before(std::chrono::minutes utc_config_tod)
{
    using namespace std::chrono;

    auto now = system_clock::now();

    auto utc_day = floor<days>(now);
    return now < utc_day + utc_config_tod;
}

UTC 的当前日期是:

auto utc_day = floor<days>(now);

现在配置日期时间只是utc_day + utc_config_tod。这简直太简单了。

如果你不能使用 C++20,free, open-source C++20 chrono preview library 现在也更简单了,因为它只是标题,根本不需要安装 no。只需#include "date/date.h" 并添加using namespace date;

【讨论】:

    【解决方案2】:

    在 C++ 中,我们可以使用日期/时间函数中的 mt_structure(此处的文档:https://en.cppreference.com/w/cpp/chrono/c/tm)这是我将如何打印日期,并检查它是否已经过了某个时间

    #include <iostream>
    #include <ctime>
    #include <chrono>
    using namespace std;
    
    int main()
    {
      time_t t = time(0);   // get time now
        tm* now = localtime(&t);
        cout << (now->tm_year + 1900) << '-' 
            << (now->tm_mon + 1) << '-'
            <<  now->tm_mday << ", " 
            << now->tm_hour << ":" << now->tm_min
            << "\n";
    
      int hour = 7, minute = 30;
      if((now->tm_hour > hour) || (now->tm_hour == hour && now->tm_min >= minute))
        cout << "it's past 7:30\n";
      else 
        cout << "it's not past 7:30";
    }
      
    

    打印:

    2021-10-27, 20:40
    it's past 7:30
    

    【讨论】:

    • 这与我的代码基本相同。希望在 C++ 中有一种更优雅的方式来执行此操作。感谢您的输入:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多