Updated Examples and Recipes (markdown)

Howard Hinnant
2017-04-20 16:38:53 -04:00
parent 988ec745b2
commit 7dd89c5173

@@ -302,14 +302,16 @@ This library can query the installed IANA timezone database for its version numb
Here is how you get the version of the installed IANA timezone database:
#include "tz.h"
#include <iostream>
```c++
#include "tz.h"
#include <iostream>
int
main()
{
std::cout << date::get_tzdb().version << '\n';
}
int
main()
{
std::cout << date::get_tzdb().version << '\n';
}
```
As I write this, the output is:
@@ -317,7 +319,9 @@ As I write this, the output is:
If the remote API is enabled ([`HAS_REMOTE_API == 1`](https://howardhinnant.github.io/date/tz.html#Installation)) then you can also query the latest version number at the IANA website with:
std::cout << date::remote_version() << '\n';
```c++
std::cout << date::remote_version() << '\n';
```
which currently outputs `2016f`.
@@ -423,14 +427,16 @@ std::cout << t.tm_year << "-" << t.tm_mon << "-" << t.tm_day << " ";
The following function will "normalize" a `year_month_day`, much like `mktime` normalizes a `tm`. This function is not part of the library, but is offered as an example if you find yourself needing things like this:
date::year_month_day
normalize(date::year_month_day ymd)
{
using namespace date;
ymd += months{0};
ymd = sys_days{ymd};
return ymd;
}
```c++
date::year_month_day
normalize(date::year_month_day ymd)
{
using namespace date;
ymd += months{0};
ymd = sys_days{ymd};
return ymd;
}
```
The first line simply adds 0 months to the `ymd`. If `ymd.month()` is 0, this will subtract one from the `year` and set the month to `Dec`. If `ymd.month()` is greater than 12, the year will be incremented as many times as appropriate, and the month will be brought within the proper range. This operation will do nothing if `ymd.month().ok()` is already true.
@@ -446,17 +452,19 @@ http://howardhinnant.github.io/date_algorithms.html#civil_from_days
Example:
int
main()
{
using namespace date;
auto ymd = 2015_y/55/250;
std::cout << ymd << (ymd.ok() ? "\n" : " invalid date\n");
ymd += months{0};
std::cout << ymd << (ymd.ok() ? "\n" : " invalid date\n");
ymd = sys_days{ymd};
std::cout << ymd << (ymd.ok() ? "\n" : " invalid date\n");
}
```c++
int
main()
{
using namespace date;
auto ymd = 2015_y/55/250;
std::cout << ymd << (ymd.ok() ? "\n" : " invalid date\n");
ymd += months{0};
std::cout << ymd << (ymd.ok() ? "\n" : " invalid date\n");
ymd = sys_days{ymd};
std::cout << ymd << (ymd.ok() ? "\n" : " invalid date\n");
}
```
Outputs:
@@ -476,26 +484,28 @@ Consider this problem:
I want to find all dates for some year `y` which are the 5th Friday of the month (because that is party day or whatever). Here is a **very** efficient function which collects all of the 5th Fridays of a year:
std::pair<std::array<date::year_month_day, 5>, unsigned>
fifth_friday(date::year y)
```c++
std::pair<std::array<date::year_month_day, 5>, unsigned>
fifth_friday(date::year y)
{
using namespace date;
constexpr auto nan = 0_y/0/0;
std::array<year_month_day, 5> dates{nan, nan, nan, nan, nan};
unsigned n = 0;
for (auto m = jan; true; ++m)
{
using namespace date;
constexpr auto nan = 0_y/0/0;
std::array<year_month_day, 5> dates{nan, nan, nan, nan, nan};
unsigned n = 0;
for (auto m = jan; true; ++m)
auto d = fri[5]/m/y;
if (d.ok())
{
auto d = fri[5]/m/y;
if (d.ok())
{
dates[n] = year_month_day{d};
++n;
}
if (m == dec)
break;
dates[n] = year_month_day{d};
++n;
}
return {dates, n};
if (m == dec)
break;
}
return {dates, n};
}
```
It turns out that it is an invariant that *every* year will have either 4 or 5 months which will have 5 Fridays. So we can efficiently return the results as a `pair<array<year_month_day, 5>, unsigned>`, where the second member of the `pair` will always be either 4 or 5.
@@ -503,23 +513,27 @@ The first job is just to initialize the `array` with a bunch of `year_month_day`
Next I iterate over each month for the year `y`. The first thing to do is construct the 5th Friday for this month/year pair:
auto d = fri[5]/m/y;
```c++
auto d = fri[5]/m/y;
```
Now since not every month has a 5th Friday, this may not result in a valid date. But in this function the proper response to constructing an invalid date _is not_ an assert nor an exception. The _proper_ response is to _ignore_ the date and iterate on to the next month. If it is a valid date, then it pushed on to the result.
This function can be exercised like this:
int
main()
{
using namespace std::chrono;
using namespace date;
auto current_year = year_month_day{floor<days>(system_clock::now())}.year();
auto dates = fifth_friday(current_year);
std::cout << "Fifth Friday dates for " << current_year << " are:\n";
for (auto i = 0u; i < dates.second; ++i)
std::cout << dates.first[i] << '\n';
}
```c++
int
main()
{
using namespace std::chrono;
using namespace date;
auto current_year = year_month_day{floor<days>(system_clock::now())}.year();
auto dates = fifth_friday(current_year);
std::cout << "Fifth Friday dates for " << current_year << " are:\n";
for (auto i = 0u; i < dates.second; ++i)
std::cout << dates.first[i] << '\n';
}
```
The variable `current_year` is initialized with the current year in the UTC time zone (close enough for government work -- use "tz.h" if you need to make it more exact to your locale). Then it is a simple matter to feed `current_year` into `fifth_friday` and iterate over the results. This just output for me:
@@ -548,13 +562,15 @@ Given a `year_month_day`, and a day of the week, how to I efficiently increment
This is very easy. But first you have to decide: If the `year_month_day` is already the desired `weekday`, do you want to return the original `year_month_day` or add a week? There is no one right answer. We'll do it both ways here. First I'll show how to keep the original. It is then an easy modification to show how to jump to the next week.
date::year_month_day
next_weekday(date::year_month_day ymd, date::weekday target)
{
using namespace date;
sys_days sd = ymd;
return sd + (target - weekday{sd});
}
```c++
date::year_month_day
next_weekday(date::year_month_day ymd, date::weekday target)
{
using namespace date;
sys_days sd = ymd;
return sd + (target - weekday{sd});
}
```
The first thing to do is to convert the `year_month_day` to a `sys_days`. This is done because it is very efficient to find the day of the week of a `sys_days` (count of days), and to do day-oriented arithmetic on that data structure.
@@ -564,25 +580,29 @@ Above if `target == weekday{sd}`, then we don't add any days at all because `ymd
If we want to add a week when the input is already at the target weekday, then you just add a day to `sd` prior to the algorithm:
date::year_month_day
next_weekday(date::year_month_day ymd, date::weekday target)
{
using namespace date;
sys_days sd = ymd;
sd += days{1};
return sd + (target - weekday{sd});
}
```c++
date::year_month_day
next_weekday(date::year_month_day ymd, date::weekday target)
{
using namespace date;
sys_days sd = ymd;
sd += days{1};
return sd + (target - weekday{sd});
}
```
The reverse is similar:
date::year_month_day
prev_weekday(date::year_month_day ymd, date::weekday target)
{
using namespace date;
sys_days sd = ymd;
sd -= days{1};
return sd - (weekday{sd} - target);
}
```c++
date::year_month_day
prev_weekday(date::year_month_day ymd, date::weekday target)
{
using namespace date;
sys_days sd = ymd;
sd -= days{1};
return sd - (weekday{sd} - target);
}
```
Except now we're subtracting weekdays to find out how many days to subtract from `sd`. Remove the pre-decrement by a day if you want this function to return the input when the input is already at the target weekday.
@@ -592,12 +612,14 @@ Except now we're subtracting weekdays to find out how many days to subtract from
CCSDS (http://public.ccsds.org/default.aspx) has a data structure that looks like this:
struct CCSDS
{
std::uint16_t days;
std::uint32_t ms;
std::uint16_t us;
};
```c++
struct CCSDS
{
std::uint16_t days;
std::uint32_t ms;
std::uint16_t us;
};
```
where `days` is the number of days since Jan 1, 1958, `ms` is the count of milliseconds of the current day, and `us` is the count of microseconds of the current millisecond.
@@ -605,22 +627,24 @@ A need arose to convert a {year, microsecond} data structure to the above CCSDS
Here is a function to perform that conversion:
CCSDS
to_CCSDS(date::year y, std::chrono::microseconds us)
{
using namespace date;
using namespace std::chrono;
auto utc = to_utc_time(sys_days{y/jan/1}) + us;
auto sys = to_sys_time(utc);
auto dp = floor<days>(sys);
auto d = dp - sys_days{1958_y/jan/1};
us = utc - to_utc_time(dp);
auto ms = duration_cast<milliseconds>(us);
us -= ms;
return {static_cast<std::uint16_t>(d.count()),
static_cast<std::uint32_t>(ms.count()),
static_cast<std::uint16_t>(us.count())};
}
```c++
CCSDS
to_CCSDS(date::year y, std::chrono::microseconds us)
{
using namespace date;
using namespace std::chrono;
auto utc = to_utc_time(sys_days{y/jan/1}) + us;
auto sys = to_sys_time(utc);
auto dp = floor<days>(sys);
auto d = dp - sys_days{1958_y/jan/1};
us = utc - to_utc_time(dp);
auto ms = duration_cast<milliseconds>(us);
us -= ms;
return {static_cast<std::uint16_t>(d.count()),
static_cast<std::uint32_t>(ms.count()),
static_cast<std::uint16_t>(us.count())};
}
```
The variable `utc` holds the “year + us” as a time point with microseconds precision. This time point counts microseconds, including leap seconds, since 1970-01-01 00:00:00 UTC.
@@ -638,8 +662,10 @@ http://stackoverflow.com/q/19290421/576911
And I decided to see if I could answer it (here) using this library. The question asks: How can I get the number of months between two dates? And it gives two example dates:
auto d1 = 1_d/oct/2013;
auto d2 = 30_d/oct/2016;
```c++
auto d1 = 1_d/oct/2013;
auto d2 = 30_d/oct/2016;
```
(I've converted the syntax to that of this library).