mirror of
https://github.com/HowardHinnant/date.git
synced 2025-08-04 05:04:27 +02:00
Updated Examples and Recipes (markdown)
@@ -5,6 +5,7 @@ This page contains examples and recipes contributed by community members. Feel f
|
|||||||
- [The current time somewhere else](#elsetime)
|
- [The current time somewhere else](#elsetime)
|
||||||
- [Get the current difference between any two arbitrary time zones](#deltatz)
|
- [Get the current difference between any two arbitrary time zones](#deltatz)
|
||||||
- [Set simultaneous meeting in two different time zones](#meeting)
|
- [Set simultaneous meeting in two different time zones](#meeting)
|
||||||
|
- [Interfacing with the C API](#Ccompatiblity)
|
||||||
- [Get milliseconds since the local midnight](#since_midnight)
|
- [Get milliseconds since the local midnight](#since_midnight)
|
||||||
- [What is my timezone database version?](#version)
|
- [What is my timezone database version?](#version)
|
||||||
- [Obtaining a `time_point` from `y/m/d h:m:s` components](#time_point_to_components)
|
- [Obtaining a `time_point` from `y/m/d h:m:s` components](#time_point_to_components)
|
||||||
@@ -179,6 +180,202 @@ In any event, the output is still:
|
|||||||
2016-07-08 09:00:00 EDT
|
2016-07-08 09:00:00 EDT
|
||||||
2016-07-08 16:00:00 MSK
|
2016-07-08 16:00:00 MSK
|
||||||
|
|
||||||
|
<a name="Ccompatiblity"></a>
|
||||||
|
### Interfacing with the C API
|
||||||
|
(by [Howard Hinnant](https://github.com/HowardHinnant))
|
||||||
|
|
||||||
|
The goal of this library is to ensure that you never have to bother with the C timing API again. That being said, the real world interrupts my goals sometimes, and one just has to convert to/from a `tm`, or `timespec` now and then for interfacing with somebody else's code that still uses the C timing API. So this note is about how to write those conversion functions:
|
||||||
|
|
||||||
|
#### Converting from a `tm`
|
||||||
|
|
||||||
|
C allows a `tm` to contain more members than are documented by the C specification. This example assumes only the portable members of `tm`. Hopefully once you understand how to work with the portable members, you will be able to extend your knowledge to the non-portable members for your platform if necessary. The portable `tm` is:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
struct tm
|
||||||
|
{
|
||||||
|
int tm_sec; // seconds after the minute — [0, 60]
|
||||||
|
int tm_min; // minutes after the hour — [0, 59]
|
||||||
|
int tm_hour; // hours since midnight — [0, 23]
|
||||||
|
int tm_mday; // day of the month — [1, 31]
|
||||||
|
int tm_mon; // months since January — [0, 11]
|
||||||
|
int tm_year; // years since 1900
|
||||||
|
int tm_wday; // days since Sunday — [0, 6]
|
||||||
|
int tm_yday; // days since January 1 — [0, 365]
|
||||||
|
int tm_isdst; // Daylight Saving Time flag
|
||||||
|
};
|
||||||
|
```
|
||||||
|
The `tm` struct can be used to hold a UTC time (`sys_time` or `utc_time`), or a local time (`local_time`). However the `tm` does not contain enough information to identify the time zone of a local time, or even the UTC offset (some platforms include a utc offset member as a conforming extension). Additionally the `tm` is limited to seconds precision. This limits what we can convert to. For example it is not possible to convert to a `zoned_time` because there is not enough information to do so. And it only makes sense to convert to a destination with seconds precision. If you want to convert to something with higher precision than that, it is an implicit conversion from the seconds-precision result of these functions.
|
||||||
|
|
||||||
|
So we can convert from a `tm` to either a `sys_seconds`, or a `local_seconds`. Here is the first:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
date::sys_seconds
|
||||||
|
to_sys_time(std::tm const& t)
|
||||||
|
{
|
||||||
|
using namespace date;
|
||||||
|
using namespace std::chrono;
|
||||||
|
return sys_days{year{t.tm_year + 1900}/(t.tm_mon+1)/t.tm_mday} +
|
||||||
|
hours{t.tm_hour} + minutes{t.tm_min} + seconds{t.tm_sec};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As can be seen, not all of the fields of the `tm` are needed for this conversion. And this conversion assumes that the `tm` represents a UTC time point. The conversion itself is quite straightforward: Convert the year/month/day information into a `sys_days`, and then add the H:M:S information, converting into chrono durations along the way. One has to be careful with the year and month data from `tm` to bias it correctly.
|
||||||
|
|
||||||
|
Converting to a `local_seconds` is identical except for the use of `local_days` in place of `sys_days`:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
date::local_seconds
|
||||||
|
to_local_time(std::tm const& t)
|
||||||
|
{
|
||||||
|
using namespace date;
|
||||||
|
using namespace std::chrono;
|
||||||
|
return local_days{year{t.tm_year + 1900}/(t.tm_mon+1)/t.tm_mday} +
|
||||||
|
hours{t.tm_hour} + minutes{t.tm_min} + seconds{t.tm_sec};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This can give you a local time in an _as-yet-unspecified_ time zone. One can subsequently pair that `local_time` with a time zone such as `current_zone()` to complete the conversion (assuming `current_zone()` is the correct time zone).
|
||||||
|
|
||||||
|
#### Converting to a `tm`
|
||||||
|
|
||||||
|
Conversion _to_ a `tm` is a bit more flexible since we generally have more than enough information to fill out the `tm`. Let's start with converting from `zoned_seconds` to a `tm`:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
std::tm
|
||||||
|
to_tm(date::zoned_seconds tp)
|
||||||
|
{
|
||||||
|
using namespace date;
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto lt = tp.get_local_time();
|
||||||
|
auto ld = floor<days>(lt);
|
||||||
|
time_of_day<seconds> tod{lt - ld}; // <seconds> can be omitted in C++17
|
||||||
|
year_month_day ymd{ld};
|
||||||
|
tm t{};
|
||||||
|
t.tm_sec = tod.seconds().count();
|
||||||
|
t.tm_min = tod.minutes().count();
|
||||||
|
t.tm_hour = tod.hours().count();
|
||||||
|
t.tm_mday = unsigned{ymd.day()};
|
||||||
|
t.tm_mon = unsigned{ymd.month()} - 1;
|
||||||
|
t.tm_year = int{ymd.year()} - 1900;
|
||||||
|
t.tm_wday = unsigned{weekday{ld}};
|
||||||
|
t.tm_yday = (ld - local_days{ymd.year()/jan/1}).count();
|
||||||
|
t.tm_isdst = tp.get_info().save != minutes{0};
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A presumption here is that one desires to convert the _local time_ in the `zoned_time` to the `tm`. If that assumption is not the case, we cover putting a UTC time into a `tm` below. So the first thing to do is to get the local time out of the `zoned_seconds` with `tp.get_local_time()`.
|
||||||
|
|
||||||
|
Next we truncate the `local_seconds` into `local_days` using the `floor<days>(lt)` function. Now we have two `local_time` `time_point`s, one with a precision of seconds, and the other with a precision of days. The days-precision `time_point` can be explicitly converted to a `year_month_day` object so that we can retrieve the year, month and day fields.
|
||||||
|
|
||||||
|
The time of day is just the difference between the seconds-precision `time_point` and the days-precision `time_point`, which gives us the seconds since midnight. This duration can be broken down into a `{hours, minutes, seconds}` struct by converting it to a `time_of_day<seconds>`. In C++17 the `<seconds>` template parameter will be deduced by the seconds-presion duration used in the constructor.
|
||||||
|
|
||||||
|
Now we just start filling out the `tm`, being careful to bias the month and year correctly. It is also good practice to first zero the entire `tm` so as to zero-out any platform-specific fields of the `tm`.
|
||||||
|
|
||||||
|
The `weekday` encoding of this library is the same encoding used in C, and so that conversion is straight forward, going from `local_days`, to `weekday`, to `unsigned`, and finally to `int`.
|
||||||
|
|
||||||
|
The computation for days-since-Jan 1 is found by simply subtracting the expression for New Years day for the current year from the already stored `local_days` value.
|
||||||
|
|
||||||
|
Finally we can set `tm_isdst` to 1 if the `save` member of `sys_info` is not `0min`, and to 0 otherwise. The `sys_info` can be obtained from the `zoned_seconds`, and contains all kinds of useful information (including the UTC offset should you want to install that into your platform-specific `tm`).
|
||||||
|
|
||||||
|
If we want to convert from a `sys_seconds` to a `tm`, that is quite easy to do using the conversion function above:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
std::tm
|
||||||
|
to_tm(date::sys_seconds tp)
|
||||||
|
{
|
||||||
|
return to_tm(date::zoned_seconds{tp});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a `zoned_time`, and defaults the `time_zone` to "UTC", then passes that `zoned_time` to `to_tm`. If desired, one could repeat the code from `zoned_seconds` instead of reuse it. This would save a small amount of processing time involved in looking up "UTC" in the database. And in this event you would always set `t.tm_isdst` to 0. One would also use `sys_days` in place of `local_days` in this alternative.
|
||||||
|
|
||||||
|
One can also create a `tm` from `local_seconds`:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
std::tm
|
||||||
|
to_tm(date::local_seconds tp)
|
||||||
|
{
|
||||||
|
auto tm = to_tm(date::sys_seconds{tp.time_since_epoch()});
|
||||||
|
tm.tm_isdst = -1;
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this variant, the `time_zone` is unknown, and thus `-1` is the proper value for `tm.tm_isdst`.
|
||||||
|
|
||||||
|
#### Converting from a `timespec`
|
||||||
|
|
||||||
|
`timespec` is new in the latest C specification. It contains at least the following members in any order:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
struct timespec
|
||||||
|
{
|
||||||
|
time_t tv_sec; // whole seconds -- >= 0
|
||||||
|
long tv_nsec; // nanoseconds -- [0, 999999999]
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
C uses `timespec` as both a time point, and as a time duration. So we should be able to convert to both `nanoseconds` (a `duration`), and `sys_time<nanoseconds>` (a `time_point`). Both are easy. First to convert to a duration:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
std::chrono::nanoseconds
|
||||||
|
to_nanoseconds(timespec const& ts)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
return seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
One just converts the integrals to their proper `duration` types and adds them. The result has type `nanoseconds`.
|
||||||
|
|
||||||
|
We can resume the above the above function to convert to a `time_point`:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
date::sys_time<std::chrono::nanoseconds>
|
||||||
|
to_time_point(timespec const& ts)
|
||||||
|
{
|
||||||
|
return date::sys_time<std::chrono::nanoseconds>{to_nanoseconds(ts)};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Just get the duration and explicitly convert it to the proper `time_point`.
|
||||||
|
|
||||||
|
#### Converting to a `timespec`
|
||||||
|
|
||||||
|
The reverse conversions are only slightly more complex:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
timespec
|
||||||
|
to_timespec(std::chrono::nanoseconds const& d)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
timespec ts;
|
||||||
|
seconds s = duration_cast<seconds>(d);
|
||||||
|
ts.tv_sec = s.count();
|
||||||
|
ts.tv_nsec = (d - s).count();
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
First truncate the nanoseconds-precision duration to seconds-precision. That truncated value can be placed into `ts.tv_sec`. Now the difference between the original nanoseconds-precision duration and the seconds-precsion duration is the amount of a nanoseconds left over, and is assigned to `ts.tv_nsec`.
|
||||||
|
|
||||||
|
The conversion to a `time_point` follow exactly the same logic, with the syntax being slightly modified to account for the fact that we're working with `time_point`s instead of `duration`s:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
timespec
|
||||||
|
to_timespec(date::sys_time<std::chrono::nanoseconds> const& tp)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
timespec ts;
|
||||||
|
auto tps = time_point_cast<seconds>(tp);
|
||||||
|
ts.tv_sec = tps.time_since_epoch().count();
|
||||||
|
ts.tv_nsec = (tp - tps).count();
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
<a name="since_midnight"></a>
|
<a name="since_midnight"></a>
|
||||||
### Get milliseconds since the local midnight
|
### Get milliseconds since the local midnight
|
||||||
(by [Howard Hinnant](https://github.com/HowardHinnant))
|
(by [Howard Hinnant](https://github.com/HowardHinnant))
|
||||||
@@ -1955,4 +2152,4 @@ Where some_local_time and some_sys_time are template instantiations of local_tim
|
|||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
 _This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/)._
|
 _This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/)._
|
Reference in New Issue
Block a user