C++ Add months to chrono::system_clock::time_point

左心房为你撑大大i 提交于 2019-12-10 16:17:11

问题


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

Thank you!


回答1:


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/400days:

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 average 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").



来源:https://stackoverflow.com/questions/43010362/c-add-months-to-chronosystem-clocktime-point

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!