Calendar dates and formatting
C++20 added the calendar to <chrono>: year, month and day are types, and year_month_day is a date that knows how to convert to and from a count of days. The formatting library prints it, and the arithmetic keeps the calendar rules.
The pieces of lesson 072 come back here: sys_days is a time point with a day as its unit, and the difference between two dates is a duration. This lesson builds dates, prints them, finds a weekday and adds a few months.
The calendar types know the rules that a hand-written date library gets wrong: leap years, the length of each month and the number of days between two dates. The conversion through sys_days is the operation that applies them all.
A date parsed from a file should go through the same check: build the three fields, call ok(), and only then store the value. The parsing code of lesson 070 is where that check belongs.
A date as a type
year_month_day{year{2026}, month{11}, day{11}} is a date with three typed fields, and the stream operator prints it as 2026-11-11. Converting it to sys_days gives a time point whose unit is a day.
The program prints the date and the day count of its sys_days. The count is the number of days since the epoch of system_clock, which is why it is a large number.
The conversion works both ways: year_month_day{sys_days{...}} turns a day count back into a date, which is how a date computed by arithmetic is printed.
#include <chrono>
#include <iostream>
int main()
{
using namespace std::chrono;
year_month_day date{year{2026}, month{11}, day{11}};
sys_days days = date;
std::cout << date << ' ' << days.time_since_epoch().count() << '\n';
}
g++ -std=c++23 -Wall -Wextra date.cpp -o date
./date
2026-11-11 20768
Formatting a date
std::format has specifiers for the calendar types: %Y is the year, %m the month and %d the day, all zero-padded. The program prints the same date in the ISO form and then with a written month.
The second line uses %B for the month name and %A for the weekday name, which the formatter computes from the date. The output depends on the locale, and the default locale is the one the program runs under.
The format string is the same one lesson 063 introduced; the calendar types plug their own specifiers into it.
#include <chrono>
#include <format>
#include <iostream>
int main()
{
using namespace std::chrono;
year_month_day date{year{2026}, month{11}, day{11}};
std::cout << std::format("{:%Y-%m-%d}", date) << '\n';
std::cout << std::format("{:%A, %d %B %Y}", date) << '\n';
}
g++ -std=c++23 -Wall -Wextra format.cpp -o format_date
./format_date
2026-11-11
Wednesday, 11 November 2026
The weekday of a date
weekday is a type with seven values. Constructed from a sys_days it computes the day of the week, and c_encoding() returns 0 for Sunday to 6 for Saturday.
The program prints the date, the three-letter name of its weekday and the number. The name is produced by the stream operator, which uses the standard abbreviations.
The weekday is also the tool for questions such as the next Friday: add days until weekday{date} == Friday. That loop is a calendar operation the type makes readable.
#include <chrono>
#include <iostream>
int main()
{
using namespace std::chrono;
year_month_day date{year{2026}, month{11}, day{11}};
weekday day{sys_days{date}};
std::cout << date << ' ' << day << ' ' << day.c_encoding() << '\n';
}
g++ -std=c++23 -Wall -Wextra weekday.cpp -o weekday_demo
./weekday_demo
2026-11-11 Wed 3
Adding months
year_month_day supports adding months and years directly, and the result keeps the day when the target month has it. The program adds three months to November and prints February.
Adding days goes through sys_days: convert, add a duration of days, convert back. That form is exact and it is what a program uses when it moves a date by a number of days.
Adding a month to the 31st can produce a date that does not exist in the target month. The standard leaves the day unchanged, so the result is invalid until the code decides what to do; ok() reports it.
#include <chrono>
#include <iostream>
int main()
{
using namespace std::chrono;
year_month_day date{year{2026}, month{11}, day{11}};
year_month_day later = date + months{3};
sys_days moved = sys_days{date} + days{20};
std::cout << date << ' ' << later << ' ' << year_month_day{moved} << '\n';
}
g++ -std=c++23 -Wall -Wextra add.cpp -o add_dates
./add_dates
2026-11-11 2027-02-11 2026-12-01
The date that does not exist
year_month_day accepts any combination of fields, including the 30th of February. ok() answers whether the combination is a real date, and it is the check a parser runs after reading three numbers.
The program builds the 30th of February and prints ok(), which is 0. The date can still be printed, and it can still be converted to sys_days, where it becomes the 2nd of March.
A function that returns a date should return an optional or throw when ok() is false, which is the subject of lesson 061 and part 7. The check itself costs one call.
#include <chrono>
#include <iostream>
int main()
{
using namespace std::chrono;
year_month_day date{year{2026}, month{2}, day{30}};
std::cout << date.ok() << ' ' << sys_days{date} << '\n';
}
g++ -std=c++23 -Wall -Wextra invalid.cpp -o invalid_date
./invalid_date
0 2026-03-02
Exercises
- Print the weekday of your birthday for this year and the number of days until it.
- Add one month to the 31st of January and check ok() on the result.
- Format a date as 11/11/2026 with the specifiers of std::format.
Questions
A new set of questions in a new order every time. Choose one answer for each, then check.
-
What does
year_month_daystore?Answer
A year, a month and a day
The type is a calendar date, and it converts to sys_days for arithmetic.
-
What does
.ok()report?Answer
Whether the date exists in the calendar
31 February is representable but not ok().
-
What does a
weekdayhold?Answer
The day of the week, with Sunday as 0
The type ranges from Sunday to Saturday, and its index starts at zero.
-
What does
year_month_day + months{3}do?Answer
It moves the date by three months
The arithmetic is on the calendar, and the result may be an invalid day such as 31 June.
-
What does this program print?
#include <chrono> #include <iostream> using namespace std::chrono; int main() { year_month_day date{year{2026}, month{11}, day{11}}; std::cout << date << ' ' << sys_days{date}.time_since_epoch().count(); }Answer
2026-11-11 20768
The date prints in the ISO order, and the day count from the epoch is 20768.
-
What does this program print?
#include <chrono> #include <format> #include <iostream> using namespace std::chrono; int main() { year_month_day date{year{2026}, month{11}, day{11}}; std::cout << std::format("{:%Y-%m-%d}\n", date); std::cout << std::format("{:%A, %d %B %Y}", date); }Answer
2026-11-11 / Wednesday, 11 November 2026
The format specifiers print the numeric date and then the full weekday and month names.
-
What does this program print?
#include <chrono> #include <iostream> using namespace std::chrono; int main() { year_month_day date{year{2026}, month{11}, day{11}}; weekday day{date}; std::cout << date << ' ' << day << ' ' << day.iso_encoding(); }Answer
2026-11-11 Wed 3
11 November 2026 is a Wednesday, and its ISO encoding is 3.
-
What does this program print?
#include <chrono> #include <iostream> using namespace std::chrono; int main() { year_month_day date{year{2026}, month{11}, day{11}}; year_month_day next = date + months{3}; year_month_day first = year{2026} / 12 / 1; std::cout << next << ' ' << first; }Answer
2027-02-11 2026-12-01
Three months after November is February of the next year, and the first of December is built from the year-month-day expression.
-
What does this program print?
#include <chrono> #include <iostream> using namespace std::chrono; int main() { year_month_day date{year{2026}, month{2}, day{30}}; std::cout << date.ok() << ' ' << year_month_day{year{2026} / 3 / 2}; }Answer
0 2026-03-02
30 February is not ok, while the second date exists and prints as 2 March.
-
Why does the calendar need a conversion to sys_days?
Answer
The day count is what supports arithmetic and comparison
The calendar type is a field list, and sys_days is the linear count.
The code on this page was built with g++ 15.2.0 and run on Alpine Linux 3.24.3 (x86_64) in a Docker container.
Article author: Arthur Isaev