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