Search code examples
c++timezonec++20c++-chrono

How do I get a list of all available time zones in C++?


I would like to somehow enumerate what time zones are available, and if possible, where my current time zone fits in this enumeration, with portable C++.


Solution

  • This is not possible (portably) in C++ prior to C++20. But it becomes trivial to do in C++20.

    To start, in C++20 there is a type std::chrono::time_zone which encapsulates a geographic area, and its rules for UTC offsets over the years. In general the rule is a function of the year. These time zones model the IANA time zone database.

    Additionally there are std::chrono::time_zone_link. A time_zone_link is like a type alias, but for time_zone. It is nothing more than an alternative name for another time_zone. You can use the name of either a time_zone, or a time_zone_link in a call to std::chrono::locate_zone("continent/city") which when successful, will return a time_zone const*.

    Below is code which iterates over all available time_zones, all available time_zone_links, and identifies your current time_zone:

    #include <chrono>
    #include <iostream>
    
    int
    main()
    {
        using namespace std;
        using namespace chrono;
    
        cout << "All available time zones:\n";
        for (auto& tz : get_tzdb().zones)
            cout << tz.name() << '\n';
    
        cout << "\nAll time zone links:\n";
        for (auto& tz : get_tzdb().links)
            cout << tz.name() << '\n';
    
        cout << "\nMy time zone:\n";
        cout << current_zone()->name() << '\n';
    }
    

    Demo.

    Head of output:

    All available time zones:
    Africa/Abidjan
    Africa/Algiers
    Africa/Bissau
    Africa/Cairo
    Africa/Casablanca
    Africa/Ceuta
    ...
    

    Any of these names can be used with locate_zone and that can be used to translate between UTC and the local time, in either direction. For example:

    #include <chrono>
    #include <format>
    #include <iostream>
    
    int
    main()
    {
        auto tz = std::chrono::locate_zone("Australia/Sydney");
        auto now_utc = std::chrono::system_clock::now();
        auto now_syd = std::chrono::zoned_time{tz, now_utc};
        std::cout << std::format("{:%F %T %Z}", now_utc) << '\n';
        std::cout << std::format("{:%F %T %Z}", now_syd) << '\n';
        std::cout << std::format("{:%F %T %Z}", now_syd.get_sys_time()) << '\n';
    }
    

    Example output:

    2023-09-27 04:00:11.408252895 UTC
    2023-09-27 14:00:11.408252895 AEST
    2023-09-27 04:00:11.408252895 UTC
    

    Demo.

    Finally note that if this isn't the precision you're looking for, you can change to your desired precision in one place, and all of the subsequent arithmetic, conversions, and formatting automatically adjust:

    #include <chrono>
    #include <format>
    #include <iostream>
    
    int
    main()
    {
        auto tz = std::chrono::locate_zone("Australia/Sydney");
        auto now_utc = std::chrono::floor<std::chrono::seconds>
                           (std::chrono::system_clock::now());
        auto now_syd = std::chrono::zoned_time{tz, now_utc};
        std::cout << std::format("{:%F %T %Z}", now_utc) << '\n';
        std::cout << std::format("{:%F %T %Z}", now_syd) << '\n';
        std::cout << std::format("{:%F %T %Z}", now_syd.get_sys_time()) << '\n';
    }
    

    Example output:

    2023-09-27 04:00:11 UTC
    2023-09-27 14:00:11 AEST
    2023-09-27 04:00:11 UTC