std::chrono::system_clock::now() considering the OS configured time zone
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 innamespace std::chrono
. -
make_zoned
is not part of C++20 because CTAD makes it unnecessary. You can usezoned_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 compiletz.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()};
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, 2022Comments
-
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 tostd::chrono::system_clock::now()
I´m still getting the UTC time, not the time that is shown in thedate
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 over 7 yearsWell, 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 over 7 years@Mendes: Updated answer to address your concerns.
-
Boppity Bop about 2 yearsIts 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 about 2 yearsA bug report might have more impact than a comment here.