C++向chrono :: system_clock :: time_point添加月份

Ant*_*cca 4 c++ std c++11 c++-chrono

如何在chrono :: system_clock :: time_point值中添加月份?

谢谢!

How*_*ant 6

概观

这是一个非常有趣的问题,有几个答案."正确"答案是您必须根据具体应用决定的.

几个月后,您可以选择按时间顺序计算日历计算.按时间顺序计算处理常规单位的时间点和持续时间,例如小时,分钟和秒.日历计算处理不规则的日历,主要用于提供令人难忘的名字.

时间计算

如果问题是关于未来几个月的物理过程,物理学并不关心不同的月份有不同的长度,因此按时间顺序计算就足够了:

  • 宝宝将在9个月内到期.

  • 从现在起6个月后天气会怎样?

为了对这些事物进行建模,按平均月份工作可能就足够了.我们可以创建一个std::chrono::duration具有精确的平均公历(民用)个月的长度.最简单的方法是通过定义一系列持续时间来开始days:

days 是24小时:

using days = std::chrono::duration
    <int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;
Run Code Online (Sandbox Code Playgroud)

years是365.2425 days,或一四六零九七/400 days:

using years = std::chrono::duration
    <int, std::ratio_multiply<std::ratio<146097, 400>, days::period>>;
Run Code Online (Sandbox Code Playgroud)

最后months1/12个years:

using months = std::chrono::duration
    <int, std::ratio_divide<years::period, std::ratio<12>>>;
Run Code Online (Sandbox Code Playgroud)

现在,您可以在8个月后轻松计算:

auto t = system_clock::now() + months{8};
Run Code Online (Sandbox Code Playgroud)

重要说明: 此计算不会保留一天中的时间,甚至一个月中的某天.

Calendrical Computation

还可以在保留一天中的时间一天中的某个时间的同时添加月份.与时间顺序计算相反,这种计算是日历计算.

选择日历(例如格里高利(民间)日历,朱利安日历,或者伊斯兰,科普特或日历的日历 - 它们都有几个月,但它们不是完全相同的月份),过程是:

  1. 转换system_clock::time_point为日历.

  2. 在日历系统中执行月计算.

  3. 将新日历时间转换回system_clock::time_point.

您可以使用Howard Hinnant的免费开源日期/时间库为几个日历执行此操作.这是民间日历的样子:

#include "date.h"

int
main()
{
    using namespace date;
    using namespace std::chrono;

    // Get the current time
    auto now = system_clock::now();
    // Get a days-precision chrono::time_point
    auto sd = floor<days>(now);
    // Record the time of day
    auto time_of_day = now - sd;
    // Convert to a y/m/d calendar data structure
    year_month_day ymd = sd;
    // Add the months
    ymd += months{8};
    // Add some policy for overflowing the day-of-month if desired
    if (!ymd.ok())
        ymd = ymd.year()/ymd.month()/last;
    // Convert back to system_clock::time_point
    system_clock::time_point later = sys_days{ymd} + time_of_day;
}
Run Code Online (Sandbox Code Playgroud)

对于笑容我只是跑了这个,并与之比较now + months{8}并获得:

now   is           2017-03-25 15:17:14.467080
later is           2017-11-25 15:17:14.467080  // calendrical computation
now + months{8} is 2017-11-24 03:10:02.467080  // chronological computation
Run Code Online (Sandbox Code Playgroud)

这给出了历法计算与时间计算的不同之处的粗略"感觉".后者平均完全准确; 它只是偏离了几天的平均值.有时更简单(后一种)的解决方案足够接近,有时则不然.只有能回答这个问题.

Calendrical Computation - 现在有了时区

最后,您可能希望在特定时区执行日历计算.以前的计算是UTC.

附注: system_clock未指定为UTC,但事实上的标准是它是Unix时间,它非常接近UTC.

您可以使用Howard Hinnant的免费开源时区库来完成此计算.这是前面提到的日期时间库的扩展.

代码非常相似,您只需要从UTC转换为本地时间,然后转换为本地日历,然后计算然后返回本地时间,最后返回system_clock::time_point(UTC):

#include "tz.h"

int
main()
{
    using namespace date;
    using namespace std::chrono;

    // Get the current local time
    auto lt = make_zoned(current_zone(), system_clock::now());
    // Get a days-precision chrono::time_point
    auto ld = floor<days>(lt.get_local_time());
    // Record the local time of day
    auto time_of_day = lt.get_local_time() - ld;
    // Convert to a y/m/d calendar data structure
    year_month_day ymd{ld};
    // Add the months
    ymd += months{8};
    // Add some policy for overflowing the day-of-month if desired
    if (!ymd.ok())
        ymd = ymd.year()/ymd.month()/last;
    // Convert back to local time
    lt = local_days{ymd} + time_of_day;
    // Convert back to system_clock::time_point
    auto later = lt.get_sys_time();
}
Run Code Online (Sandbox Code Playgroud)

更新我们的结果我得到:

now   is           2017-03-25 15:17:14.467080
later is           2017-11-25 15:17:14.467080  // calendrical: UTC
later is           2017-11-25 16:17:14.467080  // calendrical: America/New_York
now + months{8} is 2017-11-24 03:10:02.467080  // chronological computation
Run Code Online (Sandbox Code Playgroud)

时间是一小时后(UTC),因为我保留了当地时间(上午11:17)但计算在夏令时开始,并以标准时间结束,因此UTC等效时间晚1小时.

我曾经current_zone()拿起我当前的位置,但我也可以使用特定的时区(例如"Asia/Tokyo").

  • 古罗马历法有 365 天。儒略历将其修改为 365 天,每 4 年有 1 个闰年(365.25 天)。格里高利历在 1582 年修改了儒略历,跳过闰年被 100 整除的年份,但不是 400。即 1700、1800 和 1900 不是闰年,但 1600 和 2000 是。这里模拟的是公历:每 400 年正好有 146097 天:400*365 + 400/4 - 3。 (2认同)
  • 没有。C++20 有一个 _proleptic_ 民用日历:https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar 这与 ISO 8601 一致。此政策回避了各国在不同日期从儒略历切换到公历的问题。但是,用户可以编写自己的日历,与 C++20 `&lt;chrono&gt;` 互操作。我写了几个例子作为概念证明。这是其中之一:https://howardhinnant.github.io/date/julian.html (2认同)