std::chrono::system_clock::now() considering the OS configured time zone

15,021

Though unspecified by the standard, every implementation of std::chrono::system_clock::now() is tracking Unix Time, which is a very close approximation to UTC.

If you want to translate std::chrono::system_clock::now() to local time, you can either translate system_clock::time_point to time_t via system_clock::to_time_t, and then work your way through the C API (e.g. localtime), or you might try this modern timezone library which is built on top of <chrono>:

https://howardhinnant.github.io/date/tz.html

You would use this to get the current local time like this:

#include "tz.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono;
    auto t = make_zoned(current_zone(), system_clock::now());
    std::cout << t << '\n';
}

make_zoned is a factory function which returns the type zoned_time with whatever precision your system_clock supports (e.g. nanoseconds). It is a pairing of a time_zone and a system_clock::time_point.

You can get a local_time<Duration> which is a std::chrono::time_point like this:

    auto t = make_zoned(current_zone(), system_clock::now());
    auto lt = t.get_local_time();
    std::cout << lt.time_since_epoch().count() << '\n';

Though the library has a remote API to download the IANA timezone database automatically, that API can be disabled by compiling with -DHAS_REMOTE_API=0. This is all detailed in the installation instructions. With the remote API disabled, you would have to manually download the database from IANA timezone database (it's just a tar.gz).

If you need the current UTC offset, that can be obtained like this:

    auto t = make_zoned(current_zone(), system_clock::now());
    auto offset = t.get_info().offset;
    std::cout << offset << '\n';

In the snippet above, I'm taking advantage of "chrono_io.h" found at the same github repository to print offset. offset has type std::chrono::seconds. This just output for me:

-14400s

(-0400)

Finally, if you want to discover the IANA name of your current timezone, that is simply:

std::cout << current_zone()->name() << '\n';

which for me just output:

America/New_York

The type returned from name() is std::string. If desired, one could record that string, and then later use the time_zone with that name, even if that is not the computer's current time zone:

auto tz_name = current_zone()->name();
// ...
auto t = make_zoned(tz_name, system_clock::now()); // current local time in tz_name

Update 4 years down the road

This library is now part of C++20 with the following modifications:

  • The contents of "tz.h" are in <chrono>.
  • The contents of names in namespace date are in namespace std::chrono.
  • make_zoned is not part of C++20 because CTAD makes it unnecessary. You can use zoned_time in its place and the template arguments are deduced.

Additionally, a custom (user-written) time_zone can now be used either with https://howardhinnant.github.io/date/tz.html or with the new C++20 <chrono>. And a good example of a custom time_zone has been written to model POSIX time zones here. This can be used to answer the original question much more precisely:

#include "date/ptz.h"
#include <cstdlib>
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std;
    using namespace std::chrono;

    const char* tz = getenv("TZ");
    if (tz == nullptr)
        tz = "UTC0";
    zoned_time now{Posix::time_zone{tz}, system_clock::now()};
    cout << format("%F %T%z", now) << '\n';
}

With my TZ environment variable set to UTC+3 this just output for me:

2020-09-17 10:28:06.343050-0300

Notes:

  • Despite the dependence on tz.h, ptz.h is a header-only library. There is no need to install the IANA tz database, nor compile tz.cpp.

  • POSIX specifies that the sign of the UTC offset is opposite what everything else uses (including other parts of POSIX). A positive offset is west of the prime meridian instead of east. This is reflected in the output which formats the UTC offset as negative in this example, which is consistent with the POSIX strftime spec.

  • If compiling this with C++11 or C++14, you don't have CTAD in your toobox. In this case:

`

zoned_time now{Posix::time_zone{tz}, system_clock::now()};

becomes:

zoned_time<system_clock::duration, Posix::time_zone> now{Posix::time_zone{tz}, system_clock::now()};
Share:
15,021
Mendes
Author by

Mendes

BY DAY: Working hard to turn ideas into borderline software BY NIGHT: Family, fun, barbecue and rockn´roll - go to sleep and brand new ideas again... C++, Javascript, MEAN, ReactJs, Relay and naturally C++ (never missing it) Forerunner into future.... http://stackrating.com/badge/Mendes

Updated on June 25, 2022

Comments

  • Mendes
    Mendes almost 2 years

    I´m programming a C++ code that is running on a BusyBox embedded linux. My code and its libraries has several calls to std::chrono::system_clock::now() to get the current time.

    Since now my box was configured as dafault time zone (UTC) and everything works fine, processes running and results ok.

    Now I had to set my linux to stay in a different timezone. Then I did it by configuring in the box /etc/profile:

    export TZ=UTC+3
    

    When I issue date command and the console I get the correct time, but my calls to std::chrono::system_clock::now() I´m still getting the UTC time, not the time that is shown in the date command (the correct time).

    I don´t want to change all my now() calls - there are hundreds on them... And that is causing my processes to work with different time than the correct time, set on the console.

    IS there any way to solve that without changing my code ? Anything I´m missing here ?

    Thanks for helping.

  • Mendes
    Mendes over 7 years
    Well, I need not a string, but the same format as the original now() but with the clock representing the correct timezone. No internet connection on the embedded system in case the library needs some external access...
  • Howard Hinnant
    Howard Hinnant over 7 years
    @Mendes: Updated answer to address your concerns.
  • Boppity Bop
    Boppity Bop about 2 years
    Its end of April 2022 and I dont have current_zone in C++20 G++ v 11.2 CentOS 9.. What a wonderful modern language it is.. You just need to parse OS environment variable to get time.. Better than this is only JavaScript
  • Howard Hinnant
    Howard Hinnant about 2 years
    A bug report might have more impact than a comment here.