Updated Examples and Recipes (markdown)

Howard Hinnant
2017-08-10 17:31:57 -04:00
parent 0747dcef77
commit ef42876e73

@@ -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
*** ***
![CC BY Logo](http://mirrors.creativecommons.org/presskit/buttons/80x15/svg/by.svg) _This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/)._ ![CC BY Logo](http://mirrors.creativecommons.org/presskit/buttons/80x15/svg/by.svg) _This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/)._