Std::Chrono::System_Clock::Now() Considering The Os Configured Time Zone

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 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()};

How do i convert gmt time to other timezones in c++

clang/libc++/libstdc++ isn't yet fully supporting the C++20 <chrono> specification. However there exists a free, open-source preview of this part of C++20 that works with C++11/14/17. It exists in namespace date rather than namespace std::chrono, but otherwise is a pretty good approximation of the C++20 spec.

It consists of one source file, tz.cpp, that must be compiled with your source. On macOS and linux you can define this configuration macro: USE_OS_TZDB to use the time zone database provided by your OS. An easy way to do this is to put -DUSE_OS_TZDB=1 on the compile command line.

Here are complete setup instructions.

Here is example code that prints out the local time in several timezones (along with UTC):

#include "date/tz.h"
#include <chrono>
#include <iostream>

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

auto timenow = system_clock::now();
auto fmt = "%F %T %Z\n";
cout << "UTC : " << format(fmt, timenow);
cout << "America/New_York : " << format(fmt, zoned_time{"America/New_York", timenow});
cout << "Europe/London : " << format(fmt, zoned_time{"Europe/London", timenow});
cout << "Asia/Tokyo : " << format(fmt, zoned_time{"Asia/Tokyo", timenow});
}

I compiled this on macOS using:

clang++ test.cpp -std=c++17 -I../date/include ../date/src/tz.cpp -DUSE_OS_TZDB=1

This just output for me:

UTC              : 2022-04-30 12:54:43.963126 UTC
America/New_York : 2022-04-30 08:54:43.963126 EDT
Europe/London : 2022-04-30 13:54:43.963126 BST
Asia/Tokyo : 2022-04-30 21:54:43.963126 JST

The latest Visual Studio with the option /std:c++latest is shipping the C++20 version of chrono. The above example slightly changes:

#include <chrono>
#include <format>
#include <iostream>

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

auto timenow = system_clock::now();
cout << "UTC : " << format("{:%F %T %Z}\n", timenow);
cout << "America/New_York : " << format("{:%F %T %Z}\n", zoned_time{"America/New_York", timenow});
cout << "Europe/London : " << format("{:%F %T %Z}\n", zoned_time{"Europe/London", timenow});
cout << "Asia/Tokyo : " << format("{:%F %T %Z}\n", zoned_time{"Asia/Tokyo", timenow});
}
  1. Drop #include "date/tz.h"
  2. Add #include <format>
  3. Drop using namespace date;
  4. Prefix the fmt string with {: and postfix it with }.
  5. Inline the fmt string with each call to format.

I don't know why step 5 is needed. That looks like a bug in <format> to me, but I'm not positive.


Here is a way to programmatically list all of the time zone names your system provides:

for (auto& tz : get_tzdb().zones)
cout << tz.name() << '\n';
for (auto& tz : get_tzdb().links)
cout << tz.name() << '\n';

It is also possible to provide user-written time zones. Here is an example of a user-written POSIX time zone.

How to correctly store timestamp and offsets between clients and cloud server using C++ and PHP

The "best practice" is to always store everywhere UTC times.
These timestamps need to be converted to local time only in user interface. That is, on your web pages, app window dispaying data, etc. so the time zone is used only when displaying, not when storing time.

does a C++ locale have an associated timezone? And if yes, how do you access it?

Direct Answer to your question

Does a C++ locale have an associated time zone?

No.

Nor will it in the future. As correctly noted in the question, for many locales it wouldn't make sense as the geographical area represented by the locale can have more than one time zone.

The C standard does say in the spec for strftime:

%Z is replaced by the locale’s time zone name or abbreviation, or by no characters if no
time zone is determinable. [tm_isdst]

But the C spec for struct lconv provides no such member to store that information. The spec does allow implementations to add such members, but in practice, implementations do not store that information with the C locale.

The C++ locale facets time_put and time_get define themselves in terms of the C spec for strftime, the POSIX spec for strptime, and some additions, which do not include a time zone name or abbreviation.

The POSIX spec for strftime is much more detailed than the C spec, and removes the association with "locale":

Z Replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists. [ tm_isdst]

The POSIX spec for struct lconv is also much more detailed than the C spec, but still does not provide storage for a time zone name or abbreviation.

But the future does bring hope of more easily and effectively accessing information about time zones, at least in C++.

Prior to C++20, C++ has knowledge of:

  1. A single time standard: UTC, which is closely modeled by Unix Time.

  2. A single time zone: the "local time zone" set by the user or administrator of the computer. UTC can also be used as a local time zone.

As detailed above, the local time zone is not part of the C++ (or C) locale data. The locale data does include some calendrical data such as:

  • Full and abbreviated weekday names.
  • Full and abbreviated month names.
  • Local conventional formats for displaying date and time (e.g. year, month, day ordering).

The UTC offset (%z) and time zone abbreviation (%Z) may be available, but would be stored as part of the local time zone data, instead of with the current locale data, largely because there is not a good one-to-one mapping between time zones and locales.

Explanation of what happened with the code presented in the OP's question

In your example: tm when{}; zeroes all members of the tm, including tm_isdst. When tm_isdst is zero, this means Daylight Saving Time is known to not be in effect, for this particular tm.

tm is also allowed to have members not specified by the standard. A popular extension is to have a member tm_gmtoff which holds the UTC offset in seconds. If your Linux implementation has such a member, tm when{}; would have set it to 0 seconds. If your Windows implementation does not have such a member, the UTC offset of the local time zone would be stored elsewhere. This explains the differences you're seeing, and both implementations are conforming.


Helpful information about how to access time zones since C++ locales do not provide access

In the C++20 spec, there exists a new type called std::chrono::time_zone. One of the member functions of time_zone is:

template<class Duration> sys_info get_info(const sys_time<Duration>& st) const;

sys_time<Duration> is just a system_clock::time_point, but of any precision. So you give a time_zone a time_point, and you get back a sys_info which contains all kinds of useful information about that time_zone at that time_point:

struct sys_info
{
sys_seconds begin;
sys_seconds end;
seconds offset;
minutes save;
string abbrev;
};
  • The range [begin, end) tells you for what times this information is valid (these are UTC time points).
  • offset is the time_zone's current UTC offset in seconds.
  • If save != 0min, the time_zone is currently considered to be in daylight savings.
  • The time_zone's current abbreviation is stored in abbrev.

Additionally, there is a non-member function:

const time_zone* current_zone();

which returns a pointer to your current local time zone. Putting this all together, here is a C++20 program that prints out interesting information about your current local time zone:

#include <chrono>
#include <iostream>

int
main()
{
using namespace std::chrono;
std::cout << current_zone()->get_info(system_clock::now()) << '\n';
}

This just output for me:

2018-03-11 07:00:00
2018-11-04 06:00:00
-04:00:00
01:00
EDT

If you like, you can experiment with this part of C++20 using C++11, 14 or 17 by using Howard Hinnant's timezone library. This library puts everything in namespace date instead of std::chrono.

You can also get information on any IANA time zone, for example:

#include "date/tz.h"
#include <chrono>
#include <iostream>

int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << locate_zone("Australia/Sydney")->get_info(system_clock::now()) << '\n';
}

which just output for me:

2018-10-06 16:00:00
2019-04-06 16:00:00
11:00:00
01:00
AEDT

Note though that even in C++20, time zones and locales are not coupled. It just doesn't make sense to do so.

Convert boost ptime from local time to UTC

Fwiw, from this free, open source library:

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

int
main()
{
using namespace date;
using namespace std::chrono;
auto az = make_zoned("Europe/Madrid",
local_days{2016_y/oct/5} + 17h + 36min + 27s + 701162us);
std::cout << "Zone " << az.get_time_zone()->name() << '\n';
std::cout << "local_date_time: " << az << '\n';
std::cout << "local_time: " << az.get_local_time() << '\n';
std::cout << "utc_time: " << az.get_sys_time() << '\n';
std::cout << floor<seconds>(az.get_sys_time()).time_since_epoch() << '\n';
}

And the output is:

Zone Europe/Madrid
local_date_time: 2016-10-05 17:36:27.701162 CEST
local_time: 2016-10-05 17:36:27.701162
utc_time: 2016-10-05 15:36:27.701162
1475681787s

One could also construct the zoned_time using current_zone() like this:

    auto az = make_zoned(current_zone(),
local_days{2016_y/oct/5} + 17h + 36min + 27s + 701162us);

Or:

    auto az = make_zoned(current_zone(), system_clock::now());

How can I get the Windows system time with millisecond resolution?

GetTickCount will not get it done for you.

Look into QueryPerformanceFrequency / QueryPerformanceCounter. The only gotcha here is CPU scaling though, so do your research.



Related Topics



Leave a reply



Submit