• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

c++11 - C++ Add months to chrono::system_clock::time_point

[复制链接]
菜鸟教程小白 发表于 2022-6-1 20:09:54 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题

How can I add months to a chrono::system_clock::time_point value?

Thank you!



Best Answer-推荐答案


Overview

This is a very interesting question with several answers. The "correct" answer is something you must decide for your specific application.

With months, you can choose to do either chronological computations or calendrical computations. A chronological computation deals with regular units of time points and time durations, such as hours, minutes and seconds. A calendrical computation deals with irregular calendars that mainly serve to give days memorable names.

The Chronological Computation

If the question is about some physical process months in the future, physics doesn't care that different months have different lengths, and so a chronological computation is sufficient:

  • The baby is due in 9 months.

  • What will the weather be like here 6 months from now?

In order to model these things, it may be sufficient to work in terms of the average month. One can create a std::chrono::duration that has precisely the length of an average Gregorian (civil) month. It is easiest to do this by defining a series of durations starting with days:

days is 24 hours:

using days = std::chrono::duration
    <int, std::ratio_multiply<std::ratio<24>, std::chrono::hours::period>>;

years is 365.2425 days, or 146097/400 days:

using years = std::chrono::duration
    <int, std::ratio_multiply<std::ratio<146097, 400>, days::period>>;

And finally months is 1/12 of years:

using months = std::chrono::duration
    <int, std::ratio_divide<years::period, std::ratio<12>>>;

Now you can easily compute 8 months from now:

auto t = system_clock::now() + months{8};

Important note: This computation does not preserve the time of day, or even the day of the month.

The Calendrical Computation

It is also possible to add months while preserving time of day and day of month. Such computations are calendrical computations as opposed to chronological computations.

After choosing a calendar (such as the Gregorian (civil) calendar, the Julian calendar, or perhaps the Islamic, Coptic or Ethiopic calendars — they all have months, but they are not all the same months), the process is:

  1. Convert the system_clock::time_point to the calendar.

  2. Perform the months computation in the calendrical system.

  3. Convert the new calendar time back into system_clock::time_point.

You can use Howard Hinnant's free, open-source date/time library to do this for a few calendars. Here is what it looks like for the civil calendar:

#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;
}

For grins I just ran this, and compared it with now + months{8} and got:

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

This gives a rough "feel" for how the calendrical computation differs from the chronological computation. The latter is perfectly accurate on average; it just has a deviation from the calendrical on the order of a few days. And sometimes the simpler (latter) solution is close enough, and sometimes it is not. Only you can answer that question.

The Calendrical Computation — Now with timezones

Finally, you might want to perform your calendrical computation in a specific timezone. The previous computation was UTC.

Side note: system_clock is not specified to be UTC, but the de facto standard is that it is Unix Time which is a very close approximation to UTC.

You can use Howard Hinnant's free, open-source timezone library to do this computation. This is an extension of the previously mentioned datetime library.

The code is very similar, you just need to convert to local time from UTC, then to a local calendar, do the computation then back to local time, and finally back to 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();
}

Updating our results I get:

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

The time is an hour later (UTC) because I preserved the local time (11:17am) but the computation started in daylight saving time, and ended in standard time, and so the UTC equivalent is later by 1 hour.

I used current_zone() to pick up my current location, but I could have also used a specific time zone (e.g. "Asia/Tokyo").

C++20 Update

As I write this update, technical work has ceased on C++20, and it looks like we will have a new C++ standard later this year (just administrative work left to do to complete C++20).

The advice in this answer translates well to C++20:

  1. For the chronological computation, std::chrono::months is supplied by <chrono> so you don't have to compute it yourself.

  2. For the UTC calendrical computation, loose #include "date.h" and use instead #include <chrono>, and drop using namespace date, and things will just work.

  3. For the time zone sensitive calendrical computation, loose #include "tz.h" and use instead #include <chrono>, drop using namespace date, and replace make_zoned with zoned_time, and you're good to go.

回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注0

粉丝2

帖子830918

发布主题
阅读排行 更多
广告位

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap