Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 895
Mendes Avatar asked Sep 01 '16 14:09

Mendes


People also ask

What does std :: Chrono :: System_clock :: now return?

std::chrono::system_clock::now Returns a time point representing with the current point in time.

Is System_clock UTC?

system_clock measures Unix Time (i.e., time since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, not counting leap seconds).

What is std :: Chrono :: Time_point?

Class template std::chrono::time_point represents a point in time. It is implemented as if it stores a value of type Duration indicating the time interval from the start of the Clock 's epoch. Clock must meet the requirements for Clock or be std::chrono::local_t (since C++20).

What is steady clock?

A steady_clock is a monotonic clock, which means that the time it reports only moves forward.


1 Answers

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()};
like image 63
Howard Hinnant Avatar answered Nov 15 '22 07:11

Howard Hinnant