0% found this document useful (0 votes)
9 views55 pages

9-Dates and Time Zones

The document discusses the design and implementation of the C++ <chrono> library, focusing on date and time functionalities developed by Howard Hinnant. It emphasizes the importance of library design as an iterative process that balances trade-offs between performance, readability, and extensibility. The document also highlights the capabilities of the library, including time zones, calendars, and efficient date algorithms, while encouraging developers to learn from past successes and failures in software engineering.

Uploaded by

knipar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views55 pages

9-Dates and Time Zones

The document discusses the design and implementation of the C++ <chrono> library, focusing on date and time functionalities developed by Howard Hinnant. It emphasizes the importance of library design as an iterative process that balances trade-offs between performance, readability, and extensibility. The document also highlights the capabilities of the library, including time zones, calendars, and efficient date algorithms, while encouraging developers to learn from past successes and failures in software engineering.

Uploaded by

knipar
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 55

Time and Date

Bjarne Stroustrup
Columbia University
www.stroustrup.com

Partly based on talks by Howard Hinnant


<chrono>: Time and date
• A library design and implemented by Howard Hinnant
• https://en.cppreference.com/w/cpp/chrono
• He’s now at Ripple, formerly at Apple
• Designer and implementer of std::chrono
• https://github.com/HowardHinnant/date
• https://www.youtube.com/watch?v=tzyGjOm8AKo
• https://www.youtube.com/watch?v=adSAN282YIw&t=3613s
• A design case study
• Date and time are fundamental, pervasive, and surprisingly difficult to get right
• Overview
• Date and time
• Performance
• Time-zones
• Calendars
Stroustrup - Date - Aarhus 2024 2
C++ time: Std::chrono
• Types with properly scaled arithmetic
• time_point
• duration
• Typical use
using namespace std::chrono;
auto t0 = system_clock::now();
// … do some work …
auto t1 = system_clock::now();
cout << duration_cast<milliseconds>(t1-t0).count() << "msec\n"; // specify unit
cout << t1-t0 << "\n"; // some appropriate unit
• Initially designed for short time intervals
• Picoseconds to hours

Stroustrup - Date - Aarhus 2024 3


Dates and time zones: std::chrono
• Calendars
auto today = April/3/2020;
• Time zones
zoned_time tp{"Europe/Berlin", local_days{2019y/11/14} + 1h};
tp.get_sys_time(); // 2019-11-14 00:00:00
tp.get_local_time(); // 2019-11-14 01:00:00
• Printf-style output formatting
cout << zoned_time{tz, tp} << '\n’; // 2019-11-14 11:13:40.785346 CET
cout << format("{:%d.%m.%Y %T%z}\n", zoned_time{tz, tp}); // 14.11.2019 11:13:40.785346+0100

Stroustrup - Date - Aarhus 2024 4


Library Design (Howard Hinnant, 2019)

• Library Design is an engineering process. Both an art and a science.


• There are always tradeoffs to be made among conflicting goals.
• It is an iterative process, as is all engineering.

Stroustrup - Date - Aarhus 2024 5


Library Design
• It is an iterative process, as is all engineering.
• The first car wasn't Ferrari Enzo.
• It was a tricycle with a motor attached.
• It took many years and iterations for engineering technology
to evolve from one to another.
• So it goes with software.

And for the supporting infrastructure to be designed and built


• Roads
• Gas stations
• Repair shops
• Sales and distribution systems
• …
Stroustrup - Date - Aarhus 2024 6
Library Design
• And we're still early in the maturing of this industry.
• Study other's code.
• Learn from past successes.
• Learn even more from failures.

Stroustrup - Date - Aarhus 2024 7


Library Design
• Detect as many errors as you can at compile-time.
• Make client code as readable as possible.
• Eliminate ambiguities in client code.
• Encourage your client to write efficient code.
• Offer both low-level and high-level access.
• Low-level access emphasizes uncompromising performance and flexibility.
• High-level access emphasizes convenience for common cases.
• High-level for optimization

Stroustrup - Date - Aarhus 2024 8


Library Design

If you only take away one thing from this talk...

• The readability of the code your client writes is far more important than
the readability of your library's synopsis or header.

Really, library writers/designers


have a very hard time understanding that

Stroustrup - Date - Aarhus 2024 9


Library design
• Appropriate for the intended domain
• Match the concepts and notation of the users
• Users have ideas and conventions beyond your imagination (so study and listen)
• Match the needed performance (time and space)
• Be extensible when necessary
• Julian calendar
• Japanese calendar
• Arabic calendar
• ISO week
• Support appropriate error handling

Stroustrup - Date - Aarhus 2024 10


What day of the week is July 4, 2001?
• From the C standard
• A bit simpler (C++ standard)
#include <stdio.h> • and far more efficient
#include <time.h>
#include <chrono>
static const char *const wday[] = { Using namespace std;
"Sunday", "Monday", "Tuesday", "Wednesday",
using namespace chrono;
"Thursday", "Friday", "Saturday", "-unknown-"};

int main() { int main()


struct tm time_str; {
time_str.tm_year = 2001 - 1900; cout << weekday{ July/4/2001 } << '\n';
time_str.tm_mon = 7 - 1; }
time_str.tm_mday = 4;
time_str.tm_hour = 0;
time_str.tm_min = 0; • It can be (is) done at compile time
time_str.tm_sec = 0;
time_str.tm_isdst = -1; static_assert( weekday{July/4/2001} == Wednesday );
if (mktime(&time_str) == (time_t)(-1))
time_str.tm_wday = 7;
printf("%s\n", wday[time_str.tm_wday]); Stroustrup - Date - Aarhus 2024 11
}
What day of the week is July 4, 2001?
• From the C standard
• A bit simpler (C++ standard)
#include <stdio.h> • and far more efficient
#include <time.h>
Import std;
static const char *const wday[] = { Using namespace std;
"Sunday", "Monday", "Tuesday", "Wednesday",
using namespace chrono;
"Thursday", "Friday", "Saturday", "-unknown-"};

int main() { int main()


struct tm time_str; {
time_str.tm_year = 2001 - 1900; cout << weekday{ July/4/2001 } << '\n';
time_str.tm_mon = 7 - 1; }
time_str.tm_mday = 4;
time_str.tm_hour = 0;
time_str.tm_min = 0; • It can be (is) done at compile time
time_str.tm_sec = 0;
time_str.tm_isdst = -1; static_assert( weekday{July/4/2001} == Wednesday );
if (mktime(&time_str) == (time_t)(-1))
time_str.tm_wday = 7;
printf("%s\n", wday[time_str.tm_wday]); Stroustrup - Date - Aarhus 2024 12
}
Dates are important
• “Many millions of dates”
• Some processed on critical paths

Stroustrup - Date - Aarhus 2024 13


The Diary of a Datum (IBM)
• It’s not as easy as it sounds
• https://www.researchgate.net/publication/228754397_The_diary_of_a_datum_An_approach_to_analyzing_runtime
_complexity_in_framework-based_applications

Stroustrup - Date - Aarhus 2024 14


Inter-library comparison
Code size Execution speed
140 8
130
120 7 1 run
110 6
100

microseconds
90 5
80
70 4
Kb

Hot cache
60
50 3
40 2
30
20 1
10
0 0
Bloomberg

I want to plan an event for the 5th Friday of every month which has one
Stroustrup - Date - Aarhus 2024 15
boost2
Big Picture
• C++20 <chrono>: A seamless extension of C++14 <chrono> into the realm of calendars.
• It is minimalistic.
• Date: the essential foundation
• Time zone: essential for many
• ISO week: iso_week.h (very useful for some)
• It won’t do everything you want.
• It provides efficient building blocks so you can easily do for yourself everything you want.

<chrono> handles: min h


as fs ps ns μs ms s ks Ms Gs Ts Ps Es

10-18 10-15 10-12 10-9 10-6 10-3 100 103 106 109 1012 1015 1018
year
month
Chrono (C++11, C++14) day
Predefined units: Date (C++20)

Stroustrup - Date - Aarhus 2024 16


Durations
• <chrono>
// C++11
• using nanoseconds = duration<int64_t, nano>;
• using microseconds = duration<int64_t, micro>;
• using milliseconds = duration<int64_t, milli>;
• using seconds = duration<int64_t>; Chrono was added in stages
• using minutes = duration<int64_t, ratio<60>>;
• using hours = duration<int64_t, ratio<3600>>;
// C++20
• using days = duration<int, ratio_multiply<hours::period, ratio<24>>;
• using weeks = duration<int, ratio_multiply<days::period, ratio<7>>;
• using months = duration<int, ratio_divide<years::period, ratio<12>>;
• using years = duration<int, ratio_multiply<days::period, ratio<146097, 400>>; // !!!

Stroustrup - Date - Aarhus 2024 17


Time points
• UTC time
template <class Duration>
using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
using sys_days = sys_time<days>;
using sys_seconds = sys_time<std::chrono::seconds>;

• When we are using time zones


template <class Duration>
using local_time = std::chrono::time_point<local_t, Duration>;
using local_seconds = local_time<std::chrono::seconds>;
using local_days = local_time<days>;

Stroustrup - Date - Aarhus 2024 18


Where this library fits

IANA tz database

“tz.h”

“date.h”

<chrono>
Now part of <chrono>
NTP Server

OS

hardware Chrono was added in stages

Stroustrup - Date - Aarhus 2024 19


Where date.h fits
Client code

conventional API

<chrono> C++20 Type-Safe Objects

Date Algorithms

• Clients can write to either the “conventional API"


• April/19/2015 // “conventional”
• or to the lower-level (also) type-safe constructor API.
• year_month_day{2015y, April, 19d}; // “ordinary”
• year_month_day{year{2015}, month{4}, day{19}}; // “even more ordinary”
• Either way
• Time-and-day algorithms are completely encapsulated and type-safe
Stroustrup - Date - Aarhus 2024 20
“Conventional API” (as used by humans)
• Constants and literals aid when field components are known at code-writing time
• A set of overloaded / operators that compose field specifiers into field-based types.
• September/25/2015
• 2015y/ September/25 // year suffix
• 25d/ September/2015 // day suffix
• September/Friday[2]/2015 // second Friday in September 2015
• September/Friday[last]/2015 // last Friday in September 2015
• last/February/2015 // last day of February 2015

• 2015/September/25 // compile-time error: 2015 is an int, year or month required


• 25/2015y/September // compile-time error: 25 is an int , year or month required
• The traditional constructor syntax is always available.
• Year_month_day{year{2015}, month{3}, day{23}}; // year{2015} is the 2015 of this epoch
Stroustrup - Date - Aarhus 2024 21
A UTC time point: sys_time weekday

year_month_day sys_time year_month_weekday

year_month_weekday_last
year_month_day_last

• sys_time is
• a serial-based time_point with a system determined resolution.
• Easily converted to sys_seconds and sys_days (and other) well-defined resolutions
• a simple count of clock ticks since the std::chrono::system_clock epoch (e.g., 1970y/1/1).
• the central theme of this library
• is nothing more than a alias for a time_point.
• Type safe
• auto t = system_clock::now(); // 2022-03-23 21:44:13.6544519 (clock’s precision)
auto today = floor<days>(t); // 2022-03-23 (precision: days)
Stroustrup - Date - Aarhus 2024 22
sys_time
• Time-line arithmetic is very efficient on sys_time
• Just ordinary integer arithmetic
• No day, month, year conversions until we want such

sys_time t0 = system_clock::now();
work();
auto t1 = system_clock::now(); // t0 is a sys_time; we use auto a lot in chrono
cout << duration_cast<milliseconds>(t1-t0).count() << "msec\n"; // specify unit
cout << t1-t0 << "\n";

• A sys_time is a time_point
• And so is a year_month_day

sys_days tp = January / 3 / 1970 ; // precision: days


sys_time st = tp; // system’s precision
assert(floor<days>(st) == January / 3 / 1970);
Stroustrup - Date - Aarhus 2024 23
Date algorithms
• For example
• Convert {year, month, day} into a serial count of days
• Convert a serial count of days into a {year, month, day}
• Convert a serial count of days into a weekday (i.e., day of the week)

• Every date algorithm has been unit tested for every single day over
a range of +/- a million years.
• http://howardhinnant.github.io/date_algorithms.html
• “reasonably thorough” ☺

Stroustrup - Date - Aarhus 2024 24


How expensive is this?
• Compare factory functions for year_month_day with that for a simplistic struct

// C style

struct YMD_4 {
int16_t year;
uint8_t month;
// <chrono> uint8_t day;
};
date::year_month_day
make_year_month_day(int y, int m, int d) YMD_4
{ make_YMD_4(int y, int m, int d)
using namespace chrono; {
return year{y}/m/d; // “conventional API” return {(int16_t)y, (uint8_t)m, (uint8_t)d};
} }
Stroustrup - Date - Aarhus 2024 25
How expensive is this?
.globl __Z19make_year_month_dayiii .globl __Z10make_YMD_4iii
.align 4, 0x90 .align 4, 0x90
__Z19make_year_month_dayiii: __Z10make_YMD_4iii:
.cfi_startproc .cfi_startproc
## BB#0: ## BB#0:
pushq %rbp pushq %rbp
Ltmp2: Ltmp2:
.cfi_def_cfa_offset 16 .cfi_def_cfa_offset 16
Ltmp3: Ltmp3:
.cfi_offset %rbp, -16 .cfi_offset %rbp, -16
movq %rsp, %rbp movq %rsp, %rbp
Ltmp4: Ltmp4:
.cfi_def_cfa_register %rbp .cfi_def_cfa_register %rbp
shll $24, %edx shll $24, %edx
shll $16, %esi shll $16, %esi
andl $16711680, %esi andl $16711680, %esi
movzwl %di, %eax movzwl %di, %eax
orl %edx, %eax orl %esi, %eax
orl %esi, %eax orl %edx, %eax
popq %rbp popq %rbp
retq retq
.cfi_endproc .cfi_endproc

• The “conventional API” has zero space/time overhead!


• Zero-overhead abstraction Stroustrup - Date - Aarhus 2024 26
How expensive is this?
• Shift time point (measured in seconds) epoch from 2000-01-01 to 1970-01-01

// C style

long shift_epoch(long t)
{
return t + 946684800;
}
// date.h
// did I get 946684800 right?
time_point shift_epoch(time_point t)
{
return t + (sys_time{January/1/2000} - sys_time{January/1/1970});
}

Stroustrup - Date - Aarhus 2024 27


How expensive is this?
// date.h

time_point shift_epoch(time_point t)
{
using namespace date;
return t + ( sys_time{January/1/2000} - sys_time{January/1/1970} );
}
convert to serial convert to serial

Subtract to get 10,957 days

Convert to 946,684,800s
All at compile time!
Stroustrup - Date - Aarhus 2024 28
Type-safe objects
Not picking the right data structure is the most common
reason for performance problems.— Alexander Stepanov

• This library offers you a selection of data structures for dates and encourages
you to build more of your own.

• sys_time // { days count; } days since 1970/01/01


• year_month_day // { year y; month m; day d; }
• year_month_day_last // { year y; month m; day_last d; }
• year_month_weekday // { year y; month m; weekday d; }
• year_month_weekday_last // { year y; month m; weekday_last d; }
• And more, such as month_weekday_last, for convenience and existing use cases
Stroustrup - Date - Aarhus 2024 29
Invalid Dates: Alternatives A problem:
I haven’t found a good way to say
“in this part of my code, always do X in !ok()”
auto ymd = 2015y/January/31;
ymd += month{1}; // 2015y/February/31

• assert
assert(ymd.ok()); // Ensure the result is valid!
• throw
if (!ymd.ok())
throw std::domain_error{compose_message(ymd,m)};
• fix date
if (!ymd.ok())
ymd = ymd.year()/ymd.month()/last; // ymd == 2015_y/February/28
• fix month
if (!ymd.ok())
ymd = sys_time{ymd}; // ymd == 2015_y/March/3
Stroustrup - Date - Aarhus 2024 30
class year_month_day {
date::year y_; // exposition only
date::month m_; // exposition only
date::day d_; // exposition only
public:
constexpr year_month_day(const date::year& y, const date::month& m, const date::day& d) noexcept;
constexpr year_month_day(const year_month_day_last& ymdl) noexcept;
constexpr year_month_day(const sys_days& dp) noexcept;
constexpr year_month_day(const local_days& dp) noexcept;

// operators +=, and -= for year and month

constexpr date::year year() const noexcept;


constexpr date::month month() const noexcept;
constexpr date::day day() const noexcept;

constexpr operator sys_days() const noexcept;


constexpr explicit operator local_days() const noexcept;
constexpr bool ok() const noexcept;
};

// operators ==, !=, <, <=, >, >=, +, -, << for year and month

Stroustrup - Date - Aarhus 2024 31


Year_month_day
• Adding days, months, and years
• auto w0 = Tuesday[5]/m/y + months{1}; // OK, but might give bad date
• auto w1 = Tuesday[5]/m/y + years{1}; // OK , but might give bad date
• auto w2 = Tuesday[5]/m/y + days{1}; // compile-time error
• Adding days can be done correctly odd
• But is expensive: requires a conversion to sys_time and back to year_month_day
• Adding months and adding years
• Don’t have a single correct solution
• You are supposed to know that
• Use ok() where needed
• Are cheap
• Try this for February/29/2016
• ymd = ymd.year() / ymd.month() / (ymd.day() + days{ 1 }); // OK but gives bad date: !ymd.ok()
Stroustrup - Date - Aarhus 2024 32
Year_month_day
• See what Howard Hinnant has to say:
• https://stackoverflow.com/questions/43010362/c-add-months-to-chronosystem-
clocktime-point/43018120#43018120

Stroustrup - Date - Aarhus 2024 33


Weekdays
• Weekday
• constexpr weekday Sunday {0};
• constexpr weekday Monday {1};
• constexpr weekday Tuesday {2};
• constexpr weekday Wednesday {3};
• constexpr weekday Thursday {4};
• constexpr weekday Friday {5};
• constexpr weekday Saturday {6};
• Year_month_weekday
• auto today = year_month_weekday{floor<days>(system_clock::now())};
• cout << today << '\n'; // 2015/March/Sunday

Stroustrup - Date - Aarhus 2024 34


Date_and_time
• You’ve got it!
auto tp = chrono::system_clock::now();
cout << tp << '\n'; // 2016-04-19 19:16:27.2090495

auto dp = floor<days>(tp); // get the day


auto ymd = year_month_day(dp);
auto time = make_time(tp - dp); // get the time
cout << ymd << ' ' << time << '\n'; // 2016-04-19 19:16:27.2090495
• Note: UTC
• I ran this at 3:16pm US EDT (==UTC-4)

Stroustrup - Date - Aarhus 2024 35


Inter-library comparison

• I want to plan an event for the 5th Friday of every month which has one.
• This happens 4, sometimes 5 times a year.
• How easy is this to code?
• How expensive is it?
• Fair for API test? (probably)

Stroustrup - Date - Aarhus 2024 36


Inter-library comparison
• Output something like this:

2015-1-30
2015-5-29
2015-7-31
2015-10-30
<time in nanoseconds>

• I’m reporting the average of ten runs on an idle 4 core MacBook Pro, using this command line

$ a.out < tempfile

Stroustrup - Date - Aarhus 2024 37


Inter-library comparison
boost
std::pair<std::array<ymd, 5>, std::uint32_t>
fifth_friday(int y)
{
using namespace boost::gregorian;
std::array<ymd, 5> dates;
std::uint32_t n = 0;
for (auto m = 1u; m <= 12; ++m)
{
auto d = nth_day_of_the_week_in_month(nth_kday_of_month::fifth,Friday, m).get_date(y);
auto day = d.day();
if (day >= 29) { // if the last Friday of the month is >= 29
dates[n].y = y;
dates[n].m = m;
dates[n].d = day;
++n;
}
}
return {dates, n};
}
Stroustrup - Date - Aarhus 2024 38
Inter-library comparison
boost v2
std::pair<std::array<ymd, 5>, std::uint32_t>
fifth_friday(int y)
{
using namespace boost::date_time2;
std::array<ymd, 5> dates;
std::uint32_t n = 0;
for (auto m = 1u; m <= 12; ++m)
{
day_of_week dow(Fifth, Fri, m, y);
auto day = year_month_day(dow).day_of_month;
if (day >= 29) { // if the 5th Friday of the month >=29
dates[n].y = y;
dates[n].m = m;
dates[n].d = day;
++n;
}
}
return {dates, n};
}
Stroustrup - Date - Aarhus 2024 39
Inter-library comparison
date
std::pair<std::array<ymd, 5>, std::uint32_t>
fifth_friday(int y)
{
using namespace chrono;
std::array<ymd, 5> dates;
std::uint32_t n = 0;
for (auto m = 1u; m <= 12; ++m)
{
auto d = year_month_weekday{Friday[last]/m/y};
if (d.index() == 5) { // if the last Friday is the 5th Friday
auto x = year_month_day{d};
dates[n].y = y;
dates[n].m = m;
dates[n].d = unsigned{x.day()};
++n;
}
}
return {dates, n};
}
Stroustrup - Date - Aarhus 2024 40
Inter-library comparison
chrono

auto fifth_friday(int y) -> pair<array<year_month_day, 5>, int>


{
array<year_month_day, 5> dates;
int n = 0;
for (auto m = 1; m<=12; ++m) {
auto d = year_month_weekday{Friday[last]/m/y};
if (d.index() == 5) // if the last Friday is the 5th Friday
dates[n++] = year_month_day{d};
}
return {dates, n};
}

Stroustrup - Date - Aarhus 2024 41


Inter-library comparison
Bloomberg – “most creative abuse of the rules” ☺

typedef std::pair<std::array<ymd, 5>, std::uint32_t> fifth_friday_profile;

static const fifth_friday_profile profiles[14] = {


{ {{ {0, 3, 31}, {0, 6, 30}, {0, 9, 29}, {0, 12, 29}, {0, 0, 0} }}, 4 },
{ {{ {0, 3, 30}, {0, 6, 29}, {0, 8, 31}, {0, 11, 30}, {0, 0, 0} }}, 4 },
{ {{ {0, 3, 29}, {0, 5, 31}, {0, 8, 30}, {0, 11, 29}, {0, 0, 0} }}, 4 },
{ {{ {0, 1, 31}, {0, 5, 30}, {0, 8, 29}, {0, 10, 31}, {0, 0, 0} }}, 4 },
{ {{ {0, 1, 30}, {0, 5, 29}, {0, 7, 31}, {0, 10, 30}, {0, 0, 0} }}, 4 },
{ {{ {0, 1, 29}, {0, 4, 30}, {0, 7, 30}, {0, 10, 29}, {0, 12, 31} }}, 5 },
{ {{ {0, 4, 29}, {0, 7, 29}, {0, 9, 30}, {0, 12, 30}, {0, 0, 0} }}, 4 },
{ {{ {0, 3, 30}, {0, 6, 29}, {0, 8, 31}, {0, 11, 30}, {0, 0, 0} }}, 4 },
{ {{ {0, 3, 29}, {0, 5, 31}, {0, 8, 30}, {0, 11, 29}, {0, 0, 0} }}, 4 },
{ {{ {0, 2, 29}, {0, 5, 30}, {0, 8, 29}, {0, 10, 31}, {0, 0, 0} }}, 4 },
{ {{ {0, 1, 31}, {0, 5, 29}, {0, 7, 31}, {0, 10, 30}, {0, 0, 0} }}, 4 },
{ {{ {0, 1, 30}, {0, 4, 30}, {0, 7, 30}, {0, 10, 29}, {0, 12, 31} }}, 5 },
{ {{ {0, 1, 29}, {0, 4, 29}, {0, 7, 29}, {0, 9, 30}, {0, 12, 30} }}, 5 },
{ {{ {0, 3, 31}, {0, 6, 30}, {0, 9, 29}, {0, 12, 29}, {0, 0, 0} }}, 4 }
};
Stroustrup - Date - Aarhus 2024 42
Inter-library comparison

Code size Execution speed


140 8
130 1 run
120 7
110 6
100

microseconds
90 5
80
70 4
Kb

60
50 3
40 2
30
20 1
10
0 0

Stroustrup - Date - Aarhus 2024 43


Inter-library comparison

• <chrono>is small.
• <chrono> is fast.
• "The ability to create invalid dates
without being 'scolded' can be both a
readability and performance
advantage.“
-- Howard Hinnant

Stroustrup - Date - Aarhus 2024 44


Time zones

Stroustrup - Date - Aarhus 2024 45


zoned_time
• The time
• std::chrono::system_clock: System clock
• sys_time: UTC time (Greenwich)
• local_time: time in some time zone >;
• The place
• time_zone
• name()
• to_sys()
• to_local()
• A zoned_time
• a {const time_zone*,sys_time<Duration>} pair
• With conversion operations

Stroustrup - Date - Aarhus 2024 46


Time zones

auto& db = get_tzdb(); // Get time zones from IANA data base

std::cout << current_zone()->name() << '\n'; // America/NewYork

try // format: Continent/MajorCity


{
cout << locate_zone("Europe/London")->name() << '\n';
cout << locate_zone("USA/NewYork")->name() << '\n’; // No: USA is not a continent
cout << locate_zone(“Europe/Aarhus")->name() << '\n’; // No: Aarhus is not a major city
}
catch (const exception& e)
{
cout << e.what() << '\n';
}
Stroustrup - Date - Aarhus 2024 47
Local time <-> UTC
• System time (UTC) to local time
auto now = system_clock::now();

auto local1 = locate_zone("Europe/Copenhagen"); // time_zone*


auto lt1 = local1->to_local(now); // local_time: a time_point
cout << lt1 << '\n’; // time in zone

auto local2 = locate_zone("America/Los_Angeles");


auto lc2 = local2->to_local(now);
cout << lc2 << ' ' << local1->get_info(now).abbrev << '\n’; // 2016-04-22 06:45:2586.2586900 PDT
• Local time to UTC
auto utc = local2->to_sys(lc2.first);
cout << utc << " UTC\n"; // 2016-04-22 13:45:2586.2586900 UTC

Stroustrup - Date - Aarhus 2024 48


Time zones
• C++20 TZ is not yet fully supported by all major compilers
• If not, download date: https://github.com/HowardHinnant/date
zoned_seconds zt{ "Europe/Copenhagen", floor<seconds>(system_clock::now()) };

auto local_datetime = zt.get_local_time(); // 2022-03-25 23:06:30


auto local_date = floor<days>(local_datetime); // 2022-03-25
auto local_time = local_datetime - local_date; // 83190s
auto abbrev = zt.get_info().abbrev; // GMT+1

Stroustrup - Date - Aarhus 2024 49


DG schedule (as it arrived in my email)
2019-01-09 18:00:00 GMT
2019-01-09 13:00:00 EST
#include <chrono>
2019-01-09 18:00:00 UTC
#include <iostream>
2019-01-23 18:00:00 GMT
int main()
2019-01-23 13:00:00 EST
{
2019-01-23 18:00:00 UTC
using namespace std::chrono;
for (auto m = local_days{January/9/2019};

year_month_day{m}.year() < 2020y;
m += weeks{2}) {
zoned_time london{"Europe/London", m + 18h};
std::cout << london << '\n’;
std::cout << zoned_time{"America/New_York", london} << '\n';
std::cout << zoned_time{"Etc/UTC", london} << '\n';
std::cout << '\n';
}
}
Stroustrup - Date - Aarhus 2024 50
Date and Time formatting
• There are defaults
auto t0 = system_clock::now();
cout << t0; // time point: 2022-03-24 15:08:40.3007247
cout << March/25/2022; // date: 2022-03-25
cout << Friday; // day of week: Fri
auto d = system_clock::now() – t0;
cout << d; // duration: 4893[1/10000000]
cout << duration_cast<microseconds>(d); // duration: 489us
• Formatting using format strings
cout << zoned_time{ current_zone(), t0 }; // 2022-03-24 11:23:33.0315768 EDT (run in New York)
cout << zoned_time{ "Europe/Copenhagen", t0 }; // 2022-03-24 16:23:33.0315768 GMT+1
auto ymd = 2022y / March / 25;
cout << format("ymd: {3:%A}, {1:%B} {2:}, {0:}\n",
ymd.year(), ymd.month(), ymd.day(), weekday(ymd)); // ymd: Friday, March 25, 2022
cout << format("%x: {:%x}\n", sys_days{ ymd }); // %x: 03/25/22

Stroustrup - Date - Aarhus 2024 51


format() – type-safe printf()-style formatting
• Construct strings
string s = format("'{}' has {} characters\n", s, length(s));
• Control of order
format("'{}' has {} characters\n", s, length(s));
format("{1} characters has {0}\n", s, length(s));
• Formatting with a whole little printf-like sub-language of annotations: { position-opt : format-opt }
format("{:0=#6x}", 0xa); // "0x000a"
format("{}", 1.234); // "1.234"
format("{:n}", 1.234); // "1,234" (locale dependent)
format("{}", 1.6180339887); // 1.6180339887
format("{:E6} ", 1.6180339887); // 1.618034E00
format("{}", 7+2i); // (7,2) – a complex number

format("{}", September/15/2019); // 2019-09-15


format("{:%d/%m/%Y}", September/15/2019); // 15/09/2019
Stroustrup - Date - Aarhus 2024 52
Howard Hinnant
“As long as you can relate your custom field-based structure (be it the Julian calendar, the
Hindu calendar, or the Maya calendar) to the number of days before and after civil 1970-01-01,
you can achieve interoperability with every other field-based structure that does so. And
obviously you can then also convert your custom calendar to UTC
(std::chrono::system_clock::time_point).

This is the Rosetta Stone of time keeping.”

Stroustrup - Date - Aarhus 2024 53


Iso_week – iso_week.h (extension)
int main()
{
using namespace date;
using namespace std;
using namespace std::chrono;

auto dp = floor<days>(system_clock::now());
auto ymd = year_month_day(dp);
cout << ymd << '\n'; // 2015-05-20
auto iso = iso_week(ymd);
cout << iso << '\n'; // 2015-W(21)-Wed
auto ymwd = year_month_weekday(iso);
cout << ymwd << '\n'; // 2015/May/Wed[3]

assert(year_month_day(iso) == ymd);
}
Stroustrup - Date - Aarhus 2024 54
Info
Warning: pre-C++20 versions, but still very good code

• https://howardhinnant.github.io/date/date.html
• Feedback for Howard appreciated

• Also:
• "iso_week.h" implements the ISO week calendar http://howardhinnant.github.io/date/iso_week.html
• "julian.h“ implements a proleptic Julian calendar http://howardhinnant.github.io/date/julian.html
• "islamic.h" implements a proleptic Islamic calendar http://howardhinnant.github.io/date/islamic.html

Stroustrup - Date - Aarhus 2024 55

You might also like