admin管理员组

文章数量:1122832

I want to convert a UTC year, month, day to a std::chrono::time_point (and also display it).

#include <chrono>
using namespace std::chrono;

void DisplayDate(int y, int m, int d) {//e.g. 2024, 11, 22
  time_point<system_clock> tp;
  //tp = system_clock::now(); // this works as expected. 
  tp = system_clock::time_point(years(y-1970) + months(m-1) + days(d-1));// this doesn't
  std::wstring s = std::format(L"{:%Y-%m-%d %H:%M:%S}", zoned_time{current_zone(), tp});
  wprintf(s.c_str());
}

Here DisplayDate(2024,11,22) yields 2024-11-21 04:07:48.0000000, but I would expect 2024-11-22 00:00:00.0000000 and I am at a loss (especially) where the 04:07:48 comes from and how to fix it. Especially the strange offset of 7:48 puzzles me, even after hours of looking into this.

I could use the std::tm detour from How to get chrono time_point from year, month, day, hour, minute, second, millisecond? but I want to understand how to do this directly.

I want to convert a UTC year, month, day to a std::chrono::time_point (and also display it).

#include <chrono>
using namespace std::chrono;

void DisplayDate(int y, int m, int d) {//e.g. 2024, 11, 22
  time_point<system_clock> tp;
  //tp = system_clock::now(); // this works as expected. 
  tp = system_clock::time_point(years(y-1970) + months(m-1) + days(d-1));// this doesn't
  std::wstring s = std::format(L"{:%Y-%m-%d %H:%M:%S}", zoned_time{current_zone(), tp});
  wprintf(s.c_str());
}

Here DisplayDate(2024,11,22) yields 2024-11-21 04:07:48.0000000, but I would expect 2024-11-22 00:00:00.0000000 and I am at a loss (especially) where the 04:07:48 comes from and how to fix it. Especially the strange offset of 7:48 puzzles me, even after hours of looking into this.

I could use the std::tm detour from How to get chrono time_point from year, month, day, hour, minute, second, millisecond? but I want to understand how to do this directly.

Share Improve this question edited Nov 23, 2024 at 6:10 Remy Lebeau 594k34 gold badges495 silver badges834 bronze badges asked Nov 23, 2024 at 1:46 Dominik WeberDominik Weber 576 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 5
time_point<system_clock> tp;

The above is correct and fine. But just fyi, here is another way to say the same thing:

system_clock::time_point tp;

Which you choose is just a matter of style and readability for you.


tp = system_clock::time_point(years(y-1970) + months(m-1) + days(d-1));

This doesn't do what you think it does.

years (note plural) is a duration with the length of the average civil year: 365.2425 days.

months (note plural) is a duration with the length of the average civil month: 30.436875 days. This is exactly 1/12 of years.

What you intend is to convert the {y, m, d} triple to a year_month_day data structure, and then convert that to a days-precision time_point based on system_clock, which will subsequently implicitly convert to a system_clock::time_point. Sounds complicated. It is not:

tp = sys_days{year{y}/m/d};

The subexpression year{y} creates a type std::chrono::year from y. The offset is year 0. So 2024 means year 2024.

The subexpression year{y}/m/d creates a type year_month_day which does no computation but merely stores y, m, and d. For each of the fields the offset is 0. 11 for month means November. 22 for day means the day 22 in the indicated month.

A more verbose but equivalent statement would be: year{m}/month{m}/day{d}. The 2 latter types are implied by specifying the first type year. Note that all of these type names are singular, not plural. These singular types are "calendrical specifiers". Plural types are durations.

sys_days is just a type alias for a time_point based on system_clock with a precision of days. And there is an implicit conversion from year_month_day to sys_days. Here I'm using explicit conversion syntax just because it is convenient to do so.


std::wstring s = std::format(L"{:%Y-%m-%d %H:%M:%S}\n", zoned_time{current_zone(), tp});

time_points based on system_clock are UTC. If what you want is UTC, there is no need to involve any time_zone, including your computer's currently set time_zone returned by current_zone(). You can just format tp directly:

std::wstring s = std::format(L"{:%Y-%m-%d %H:%M:%S}\n", tp);

Furthermore, there exists "shortcuts" for both %Y-%m-%d and %H:%M:%S if you prefer. They are %F and %T respectively. They offer no advantages or disadvantages besides what you may perceive as readability (either less or more, your choice). But the above line could be rewritten as:

std::wstring s = std::format(L"{:%F %T}\n", tp);

In either case the subsequent wprintf will output:

2024-11-22 00:00:00.0000000

Involving zoned_time and current_zone in the format statement causes the format to convert the sys_time (UTC) to local_time according to your computer's locally set time_zone obtained via current_zone().


From the comments:

What would be the format if I wanted millisecond precision, like for example 00:00:00.123.

The easiest way to control the precision for formatting is to control the precision for your entire use case. That is, traffic in millisecond precision from the point of creation of your time_points, not just at format-time.

In this example that looks like:

sys_time<milliseconds> tp = sys_days{year{y}/m/d};

And the output is now:

2024-11-22 00:00:00.000

sys_time is just a template type alias for time_point<system_clock, D> where D is whatever precision you want. %T will print out the full precision of whatever it is given.

Another way to achieve the same thing is:

auto tp = sys_days{year{y}/m/d} + 0ms;

Now the type of tp is deduced as sys_time<milliseconds> from the precision of the initialization expression.

The same technique is used to specify a time of day (UTC):

#include <chrono>
#include <format>
#include <iostream>

void
DisplayDateTime(int y, int m, int d, int h, int M, int s, int ms)
{
    using namespace std::chrono;
    auto tp = sys_days{year{y}/m/d} + hours{h} + minutes{M} + seconds{s}
                                    + milliseconds{ms};
    std::wstring str = std::format(L"{:%F %T}\n", tp);
    wprintf(str.c_str());
}

int
main()
{
    DisplayDateTime(2024, 11, 22, 16, 19, 13, 127);
}

Output:

2024-11-22 16:19:13.127

本文标签: cHow to set a systemclock timepoint with yearmonthdayStack Overflow