Table of Contents

In this blog post, we will explore handling dates using std::chrono, including time zones. We’ll utilize the latest features of the library to retrieve the current time across various time zones, taking into account daylight saving time changes as well. Additionally, we will incorporate new capabilities introduced in C++23, such as enhanced printing functions and more.

Let’s start with the following code that prints the current date and time:

#include <chrono>
#include <print>

int main() {
    auto now = std::chrono::system_clock::now();
    std::print("now is {}", now);
}

You can run it through Compiler Explorer

In my session, I’m getting the following results:

Now is 2024-11-01 11:44:06.374573703

It’s simple, but what time zone are we in? Can we print it in a shorter form? How can we check the time around the world? What about daylight saving time?

First things first: time output.

Improving the print  

As you can see, I used std::print from C++23, which is available in GCC 14! By default, the time point obtained from ::now() is printed with full information, but we can change it and adjust it. For example:

Basic Date and Time Formatting  

  • Show just the date: {0:%F} will format the date as YYYY-MM-DD.
  • Show time in hh:mm:ss format: {0:%T} will format the time as HH:MM:SS.
  • Show the name of the month and/or day of the week: {0:%A, %B} will format it as Day, Month.

Advanced Formatting Options  

  • Year and Month:

    • {0:%Y}: Full year (e.g., 2024).
    • {0:%y}: Last two digits of the year (e.g., 24).
    • {0:%B}: Full month name (e.g., November).
    • {0:%b}: Abbreviated month name (e.g., Nov).
  • Day and Week:

    • {0:%d}: Day of the month, zero-padded (e.g., 01).
    • {0:%A}: Full weekday name (e.g., Friday).
    • {0:%a}: Abbreviated weekday name (e.g., Fri).
  • Time of Day:

    • {0:%H}: Hour (24-hour clock), zero-padded (e.g., 14).
    • {0:%I}: Hour (12-hour clock), zero-padded (e.g., 02).
    • {0:%M}: Minute, zero-padded (e.g., 05).
    • {0:%S}: Second, zero-padded (e.g., 09).
    • {0:%p}: AM/PM designation.
  • Time Zone:

    • {0:%Z}: Time zone abbreviation (e.g., UTC).
    • {0:%z}: Offset from UTC (e.g., +0000).

Here is an example demonstrating some of these formatting options:

#include <chrono>
#include <print>

int main() {
    auto now = std::chrono::system_clock::now();

    std::print("Full date and time: {0:%Y-%m-%d %H:%M:%S}\n", now);
    std::print("Date only: {0:%F}\n", now);
    std::print("Time only: {0:%T}\n", now);
    std::print("Day of the week: {0:%A}\n", now);
    std::print("Month name: {0:%B}\n", now);
    std::print("12-hour clock with AM/PM: {0:%I:%M:%S %p}\n", now);
    std::print("ISO 8601 format: {0:%FT%T%z}\n", now);
}

Run @Compiler Explorer

Example output:

Full date and time: 2024-11-02 19:35:16.296643881
Date only: 2024-11-02
Time only: 19:35:16.296643881
Day of the week: Saturday
Month name: November
12-hour clock with AM/PM: 07:35:16.296643881 PM
ISO 8601 format: 2024-11-02T19:35:16.296643881+0000

We can print the time, and it’s now clear that it is always in UTC. What about other time zones?

Getting time in my current time zone  

C++20 chrono gives us a powerful module that handles time zones. It’s not that easy, as the library has to reach out to the operating system and get the IANA database (see more @std::chrono::tzdb - cppreference.com.

But as a user, it’s relatively easy for us: we have to use zoned_time and ask for proper time_zone:

#include <chrono>
#include <print>

int main() {
    const auto now = std::chrono::system_clock::now();      
    auto zt_local = std::chrono::zoned_time{ std::chrono::current_zone(), now };
    std::print("now is {} UTC and local is: {}\n", now, zt_local);

    constexpr std::string_view Warsaw{ "Europe/Warsaw" };
    constexpr std::string_view NewYork{ "America/New_York" };
    constexpr std::string_view Tokyo{ "Asia/Tokyo" };

    try
    {
        const std::chrono::zoned_time zt_w{Warsaw, now};
        std::print("Warsaw: {0:%F} {0:%R}\n", zt_w);
        const std::chrono::zoned_time zt_ny{NewYork, now};
        std::print("New York: {0:%F} {0:%R}\n", zt_ny);
        const std::chrono::zoned_time zt_t{Tokyo, now};
        std::print("Tokyo: {0:%F} {0:%R}\n", zt_t);
    }
    catch (std::runtime_error& ex)
    {
        std::print("Error: {}", ex.what());
    }
}

See at Compiler Explorer

Example output:

now is 2024-11-01 19:24:51.319170338 UTC and local is: 2024-11-01 19:24:51.319170338
Warsaw: 2024-11-01 20:24
New York: 2024-11-01 15:24
Tokyo: 2024-11-02 04:24

Compiler Explorer apparently runs on some servers located in the UTC time zone, so there’s no difference in time. But my time zone - Warsaw/Poland - shows different times, Similar to New York and Tokyo.

Let’s now dive into the details:

  1. Getting the Current Time:
    • We start by obtaining the current time using std::chrono::system_clock::now(). This gives us the current time in UTC.
  2. Local Time Zone:
    • We create a std::chrono::zoned_time object using std::chrono::current_zone(), which automatically detects the local time zone of the system where the code is running. This allows us to convert the UTC time to the local time zone.
  3. Time Zone Conversion: - We define three time zones using std::string_view: Warsaw, New York, and Tokyo. These are specified using their respective IANA time zone identifiers.
    • For each time zone, we create a std::chrono::zoned_time object, which converts the current UTC time to the specified time zone.
  4. Printing the Results:
    • We use std::print to display the current time in UTC and the local time zone.
    • For each specified time zone (Warsaw, New York, Tokyo), we print the date and time using the format specifiers {0:%F} for the date (YYYY-MM-DD) and {0:%R} for the time (HH:MM).
  5. Error Handling:
    • The code is wrapped in a try-catch block to handle any potential runtime errors, such as invalid time zone identifiers.

Daylight saving time  

We can even check some extra information related to our current time zone. We can obtain info from our zoned_time and print information like start/end, offset, and more:

#include <chrono>
#include <print>
#include <iostream>

int main() {
    try
    {
        const auto now = std::chrono::floor<std::chrono::minutes>(std::chrono::system_clock::now());        
        auto zt_local = std::chrono::zoned_time{ "Europe/Warsaw", now };
        std::print("now is {} UTC and Warsaw is: {}\n", now, zt_local);

        auto info = zt_local.get_info();
        std::print("local time info: \nabbrev: {},\n begin {}, end {}, \noffset {}, save {}\n", 
                          info.abbrev, info.begin, info.end, info.offset, info.save);
    }
    catch (std::runtime_error& ex)
    {
        std::print("Error: {}", ex.what());
    }
}

See @Compiler Explorer

and daylight info:

#include <chrono>
#include <print>

void printInfo(std::chrono::sys_days sd, std::string_view zone) {
    auto zt_local = std::chrono::zoned_time{ "Europe/Warsaw", std::chrono::sys_days{sd} };
    auto info = zt_local.get_info();
    std::print("time info for {:%F} in {}:\nabbrev: {},\nbegin {}, end {}, \noffset {}, save {}\n", 
                sd, zone, info.abbrev, info.begin, info.end, info.offset, info.save);
}

int main() {
    try
    {
        printInfo(std::chrono::year{ 2024 } / 9 / 14, "Europe/Warsaw");
        printInfo(std::chrono::year{ 2024 } / 11 / 14, "Europe/Warsaw");
    }
    catch (std::runtime_error& ex)
    {
        std::print("Error: {}", ex.what());
    }
}

Run @Compiler Explorer

I’m getting:

time info for 2024-09-14 in Europe/Warsaw:
abbrev: CEST,
begin 2024-03-31 01:00:00, end 2024-10-27 01:00:00, 
offset 7200s, save 60min
time info for 2024-11-14 in Europe/Warsaw:
abbrev: CET,
begin 2024-10-27 01:00:00, end 2025-03-30 01:00:00, 
offset 3600s, save 0min

As you can see, we get the following information:

  • info.abbrev - zone abbreviation name, like CET, PT, etc
  • info.begin, info.end - start and end date (in sys_seconds) for a given time zone, usually changes when there’s a daylight time zone switch
  • info.offset - offset from UTC time
  • info.save - if nonzero, indicates that the time zone is on daylight saving time

Summary  

What a ride! We began with a simple display of the current time and progressed to more complex scenarios, including various formatting options available in C++20 and C++23, as well as time zones. In the end, we explored how to check details of a specific timepoint and examine the information related to its time zone.

Would you like to see more?
Read my other articles on std::chrono. See Exploring std::chrono in C++20 - Time Zones or Exploring std::chrono in C++20 - Calendar Types ~2700 words, which are available for C++ Stories Premium/Patreon members. See all Premium benefits here.

Back to you

  • Have you tried std::chrono?
  • Do you use the latest additions from C++20 for date support?

Share your comments below.