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

  1. Print the weekday of your birthday for this year and the number of days until it.
  2. Add one month to the 31st of January and check ok() on the result.
  3. 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.

  1. What does year_month_day store?

    Answer

    A year, a month and a day

    The type is a calendar date, and it converts to sys_days for arithmetic.

  2. What does .ok() report?

    Answer

    Whether the date exists in the calendar

    31 February is representable but not ok().

  3. What does a weekday hold?

    Answer

    The day of the week, with Sunday as 0

    The type ranges from Sunday to Saturday, and its index starts at zero.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

  9. 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.

  10. 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