Convert time_t from localtime zone to UTC - c++

I have a time_t that represents the time in seconds since epoch. Those seconds refer to the local time.
I want to convert them to UTC.
Is there a way to do this in C++?

I'm going to show two ways of doing this:
Using the C API.
Using a modern C++11/14 library based on top of <chrono>.
For the purposes of this demo, I'm assuming that the current number of seconds in the local time zone is 1,470,003,841. My local time zone is America/New_York, and so the results I get reflect that we are currently at -0400 UTC.
First the C API:
This API is not type-safe and is very error prone. I made several mistakes just while coding up this answer, but I was able to quickly detect these mistakes because I was checking the answers against the 2nd technique.
#include <ctime>
#include <iostream>
int
main()
{
std::time_t lt = 1470003841;
auto local_field = *std::gmtime(&lt);
local_field.tm_isdst = -1;
auto utc = std::mktime(&local_field);
std::cout << utc << '\n'; // 1470018241
char buf[30];
std::strftime(buf, sizeof(buf), "%F %T %Z\n", &local_field);
std::cout << buf;
auto utc_field = *std::gmtime(&utc);
std::strftime(buf, sizeof(buf), "%F %T UTC\n", &utc_field);
std::cout << buf;
}
First I initialize the time_t. Now there is no C API to go from a local time_t to a UTC time_t. However you can use gmtime to go from a UTC time_t to a UTC tm (from serial to field type, all in UTC). So the first step is to lie to gmtime, telling it you've got a UTC time_t. And then when you get the result back you just pretend you've got a local tm instead of a UTC tm. Clear so far? This is:
auto local_field = *std::gmtime(&lt);
Now before you go (and I personally messed this part up the first time through) you have to augment this field type to say that you don't know if it is currently daylight saving or not. This causes subsequent steps to figure that out for you:
local_field.tm_isdst = -1;
Next you can use make_time to convert a local tm to a UTC time_t:
auto utc = std::mktime(&local_field);
You can print that out, and for me it is:
1470018241
which is 4h greater. The rest of the function is to print out these times in human readable format so that you can debug this stuff. For me it output:
2016-07-31 22:24:01 EDT
2016-08-01 02:24:01 UTC
A modern C++ API:
There exist no facilities in the std::lib to do this. However you can use this free, open source (MIT license) library for this.
#include "date/tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono_literals;
auto zt = make_zoned(current_zone(), local_seconds{1470003841s});
std::cout << zt.get_sys_time().time_since_epoch() << '\n'; // 1470018241s
std::cout << zt << '\n';
std::cout << zt.get_sys_time() << " UTC\n";
}
The first step is to create the local time in terms of seconds since the epoch:
local_seconds{1470003841s}
The next thing to do is to create a zoned_time which is a pairing of this local time and the current time zone:
auto zt = make_zoned(current_zone(), local_seconds(1470003841s));
Then you can simply print out the UTC number of seconds of this pairing:
std::cout << zt.get_sys_time().time_since_epoch() << '\n';
This output for me:
1470018241s
(4h later than the input). To print out this result as I did in the C API:
std::cout << zt << '\n';
std::cout << zt.get_sys_time() << " UTC\n";
which outputs:
2016-07-31 22:24:01 EDT
2016-08-01 02:24:01 UTC
In this modern C++ approach, the local time and the UTC time are different types, making it much more likely that I catch accidental mixing of these two concepts at compile time (as opposed to creating run time errors).
Update for C++20
The second technique will be available in C++20 with the following syntax:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
zoned_time zt{current_zone(), local_seconds{1470003841s}};
std::cout << zt.get_sys_time().time_since_epoch() << '\n'; // 1470018241s
std::cout << zt << '\n';
std::cout << zt.get_sys_time() << " UTC\n";
}

You can use gmtime:
Convert time_t to tm as UTC time Uses the value pointed by timer to
fill a tm structure with the values that represent the corresponding
time, expressed as a UTC time (i.e., the time at the GMT timezone).
(c) http://www.cplusplus.com/reference/ctime/gmtime/

If you are okay with using Abseil's time library, one other way to do this is:
auto civil_second =
absl::LocalTimeZone().At(absl::FromTimeT(<your time_t>)).cs;
time_t time_in_utc = absl::ToTimeT(absl::FromCivil(civil_second, absl::UTCTimeZone()));
(Maybe there is a simpler set of calls in the library to do this, but I have not explored further. :))

Normaly, you would convert from time_t to struct tm and there aren't many examples of converting from time_t to time_t in a different time zone (UTC in case of the OP's question). I wrote these 2 functions for that exact purpose. They may be useful when you are only in a need ot using time_t but in a specific time zone.
time_t TimeAsGMT(time_t t)
{
std::chrono::zoned_time zt{"UTC", std::chrono::system_clock::from_time_t(t)};
return std::chrono::system_clock::to_time_t(zt.get_sys_time());
}
or if you want the current time as UTC in the form of time_t
time_t CurTimeAsGMT()
{
std::chrono::zoned_time zt{"UTC", std::chrono::system_clock::now()}; // Get the time in UTC time zone
return std::chrono::system_clock::to_time_t(zt.get_sys_time()); // return this time as time_t
}
If you run both functions and compare the initial value and the result value, you will see that the difference matches the difference between your current time (at your current time zone) and UTC / GMT time zone.

Related

How to tell if it's before a configured time in C++?

I'm trying to find the best way to see if the current time is before a specified time. Say I want to see if it's before 14:32. What's the best way to do this in C++? Ideally I'd be able to build some time object that represents 14:32, then compare it with the current time as some object.
This is what I'm doing right now. Pretty messy and uses 3 different representations of time.
int hour_ = 14;
int min_ = 32;
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t tt = std::chrono::system_clock::to_time_t(now);
std::tm utc_tm = *gmtime(&tt);
if ((utc_tm.tm_hour < hour_) || (utc_tm.tm_hour == hour_ && utc_tm.tm_min < min_) ) {
std::cout << "It's before " << hour_ << ":" << min_ << std::endl;
}
Here is how you can do it in C++20. Later I will show how to convert this to use a free, open-source C++20 chrono preview library which works with C++11/14/17.
#include <chrono>
bool
is_now_before(std::chrono::minutes local_config_tod)
{
using namespace std::chrono;
auto tz = current_zone();
auto now = system_clock::now();
auto local_day = floor<days>(zoned_time{tz, now}.get_local_time());
auto utc_config = zoned_time{tz, local_day + local_config_tod}.get_sys_time();
return now < utc_config;
}
The parameter has type minutes which will be interpreted to be the local time of day in minutes. For example 14:32 is represented by minutes{872}. This representation is compact (one integer), and it is trivial to convert {hours, minutes} to just minutes (shown below).
current_zone() gets the computer's current local time zone. This information is needed twice in this function, so it is best to just get it once. Not only does this save the result, but it also sidesteps the problem of the local time zone changing out from under you (between multiple calls) in a mobile device.
Next the current time is obtained (just once) via system_clock. This gives the current time in UTC.
Now we have a choice:
We could do the comparison in UTC, or
We could do the comparison in local time.
Doing the comparison in UTC is less error prone in the corner case that the UTC offset is changing in the current local day (such as going on or off of daylight saving).
To convert the local config time-of-day (local_config_tod) to a UTC time_point one first has to find out what the current local day is. In general this can be different than the current UTC day. So the current UTC now has to be converted to local time, and then truncated to days-precision:
auto local_day = floor<days>(zoned_time{tz, now}.get_local_time());
Now a local time_point can be created simply by summing local_day and local_config_tod. This local time_point can then be converted back into UTC (a time_point based on system_clock but with seconds precision):
auto utc_config = zoned_time{tz, local_day + local_config_tod}.get_sys_time();
The line of code above handles the corner cases for you. If there is not a unique (one-to-one) mapping from local time to UTC, then an exception is thrown. The .what() of the exception type will have a detailed description about how this mapping is either ambiguous, or non-existent.
Assuming the above mapping does not throw an exception, you can simply compare these two UTC time_points:
return now < utc_config;
The precision of this comparison is with whatever precision your system_clock has (typically microseconds to nanoseconds).
This can be exercised like so:
int hour_ = 14;
int min_ = 32;
using namespace std::chrono;
auto b = is_now_before(hours{hour_} + minutes{min_});
If 14 and 32 are literals (and you're in C++14 or later), it can be shortened to:
auto b = is_now_before(14h + 32min);
If you are using a standard prior to C++17, the zoned_time constructions will require an explicit template parameter:
auto local_day = floor<days>(zoned_time<system_clock::duration>{tz, now}.get_local_time());
auto utc_config = zoned_time<minutes>{tz, local_day + local_config_tod}.get_sys_time();
If you would like to use the free, open-source C++20 chrono preview library, add #include "date/tz.h" and using namespace date;. Some installation is required.
If you would like to avoid an exception in the case that local_day + local_config_tod does not have a unique mapping to UTC, that is also possible with minor changes to is_now_before. But you will have to decide things such as: Do I want to compare against the first or second local_config_tod of the local_day (in case the UTC offset has been decreased).
Oops! Is the config time already UTC?
On re-reading your question it occurred to me that I may have misread your question. If 14:32 is UTC, then things get much, much simpler! And rather than removing my answer showing the local 14:32 interpretation, I thought it would be better to add this, so future readers could pick either solution.
Assuming the config is a UTC time, then time zones play no role at all:
#include <chrono>
bool
is_now_before(std::chrono::minutes utc_config_tod)
{
using namespace std::chrono;
auto now = system_clock::now();
auto utc_day = floor<days>(now);
return now < utc_day + utc_config_tod;
}
The current day in UTC is simply:
auto utc_day = floor<days>(now);
And now the config date-time is simply utc_day + utc_config_tod. This is just drop-dead simple.
If you can't use C++20, the free, open-source C++20 chrono preview library is also much simpler now as it is header-only, requiring no installation at all. Just #include "date/date.h" and add using namespace date;.
In C++ we can use the mt_structure from the date/time functions (documentation here: https://en.cppreference.com/w/cpp/chrono/c/tm) Here is how I would print the date, and check to see if it's past a certain time
#include <iostream>
#include <ctime>
#include <chrono>
using namespace std;
int main()
{
time_t t = time(0); // get time now
tm* now = localtime(&t);
cout << (now->tm_year + 1900) << '-'
<< (now->tm_mon + 1) << '-'
<< now->tm_mday << ", "
<< now->tm_hour << ":" << now->tm_min
<< "\n";
int hour = 7, minute = 30;
if((now->tm_hour > hour) || (now->tm_hour == hour && now->tm_min >= minute))
cout << "it's past 7:30\n";
else
cout << "it's not past 7:30";
}
prints:
2021-10-27, 20:40
it's past 7:30

Detecting DST changes in C++

I am working on c++ real time application which is doing lot of date manipulations. For performance reasons, I made the UTC offset as configurable value which is read only once at the time of application initialization. But it is causing issues in DST zones.
When DST Changes happens, My UTC offset variable contain wrong value.
Calculating the Offset every time is not a optimal solution for me.
So is there anyway to notify my application about DST changes? So I can calculate the offset only when it need to modified.
Using the draft C++20 <chrono> facilities you can discover the std::chrono::system_clock::time_point for the next UTC offset change for any time zone, and then std::this_thread::sleep_until that time_point. When you wake, do whatever it is you want to do, such as compute a new UTC offset.
The new draft C++20 <chrono> library has been prototyped and is available as a free, open-source library under namespace date. This library is portable across VS, gcc and clang, and operates with C++11 and later.
Here is a sketch of how this might be coded:
#include "date/tz.h"
#include <thread>
template <class F>
void
on_utc_offset_change(date::time_zone const* tz, F f)
{
using namespace date;
using namespace std;
using namespace std::chrono;
while (true)
{
auto info = tz->get_info(system_clock::now());
f(info);
this_thread::sleep_until(info.end);
}
}
A time_zone has a get_info(system_clock::time_point) member function that returns a sys_info. The sys_info contains all information about a time_zone for a particular point in time:
struct sys_info
{
sys_seconds begin;
sys_seconds end;
std::chrono::seconds offset;
std::chrono::minutes save;
std::string abbrev;
};
The begin and end members are seconds-precision system_clock time_points that delineate the range [begin, end) for which this time_zone has this UTC offset and abbreviation (abbrev).
This function might be called like this:
auto lambda = [](date::sys_info const& info)
{
using namespace date;
std::cerr << "Current UTC offset: " << info.offset << '\n';
std::cerr << "Current tz abbreviation: " << info.abbrev << '\n';
std::cerr << "Sleeping until " << info.end << " UTC\n";
};
std::thread{on_utc_offset_change<decltype(lambda)>,
date::current_zone(),
lambda}.detach();
This detaches a thread that runs forever and every time the UTC offset changes for the computer's current time_zone (at the time the thread was launched), it prints out current information, for example.
Current UTC offset: -14400s
Current tz abbreviation: EDT
Sleeping until 2018-11-04 06:00:00 UTC
Alternatively you could run this for a specific time_zone which is not the computer's currently set time_zone with:
std::thread{on_utc_offset_change<decltype(lambda)>,
date::locate_zone("America/New_York"),
lambda}.detach();

how to find the full minute before and after a date in miliseconds

I've a a problem with time numbers,
Lets supose that i've this time: 05-03-2016 09:45:55.064371, I've a function that converts this to miliseconds (Using Epoch (reference_date), using ctime and chrono libraries)->1457167555064, now what I want to find is the full minute with miliseconds before and after this time, so in this case what I want is to find 05-03-2016 09:45:00.000000 and 05-03-2016 09:46:00.000000
I'm open to lisent another way to find if a date is inside a minute.
Thank you!
The most convenient tool for this is the new std::chrono::floor and std::chrono::ceil in C++17. If you don't have C++17, you can get a preview of these in Howard Hinnant's free, open-source datetime library:
#include "date/date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
sys_time<milliseconds> tp{1457167555064ms};
sys_time<milliseconds> t0 = floor<minutes>(tp);
sys_time<milliseconds> t1 = ceil<minutes>(tp);
std::cout << t0 << '\n';
std::cout << tp << '\n';
std::cout << t1 << '\n';
}
Output:
2016-03-05 08:45:00.000
2016-03-05 08:45:55.064
2016-03-05 08:46:00.000
These times are all UTC. You appear to have a local time at UTC +01:00. There is also a timezone library at this same GitHub site that you can use to convert between UTC and local time, or between any two IANA timezones.
Above, sys_time<milliseconds> is simply a type alias for time_point<system_clock, milliseconds>.

Time offset calculation is off by one minute

I am trying to replace a number of different time classes with a single consistent API. However I have recently run into a problem whereby I cannot serialise the timezone offset correctly. Note that I am attempting to replicate an existing format that is already in wide use in the system.
The format should be YYYY-mm-DD HH:MM:SS.xxxxxxx -HHMM, where the x represents the sub-second precision and the last -HHMM is the TZ offset from UTC.
Code:
using namespace My::Time;
namespace chrn = std::chrono;
time_point now = clock::now();
time_point lclNow = getDefaultCalendarProvider()->toLocal(now);
duration diff{ lclNow - now };
std::wstring sign = diff > duration::zero() ? L" +" : L" -";
duration ms{ now.time_since_epoch().count() % duration::period::den };
int diffHrs = popDurationPart<chrn::hours>(diff).count();
int diffMins{ abs(chrn::duration_cast<chrn::minutes>(diff).count()) };
std::cout << Format{ lclNow, TimeZone::UTC, L" %Y-%m-%d %H:%M:%S." } << ms.count()
<< sign << std::setfill(L'0') << std::setw(2) << diffHrs
<< std::setfill(L'0') << std::setw(2) << diffMins << std::endl;
Problem:
Expected:<2016-05-25 09:45:18.1970000 +0100> Actual:< 2016-05-25
09:45:18.1964787 +0059>
The expected value is what you get when I use the old class to do the same operation. The problem appears to be at the point where I attempt to get the difference between lclNow and now.
Currently I am in UTC +1 (due to DST being in effect). However the diff value is always 35999995635. Being on Visual C++ in Windows the tick is 100 ns, so there are 10000000 ticks per second, meaning the diff value is 3599.9995 seconds, which is just short of the 3600 seconds I would need to make an hour.
When I print the two time values using the same format then I can see that they are exactly one hour apart. So it appears that the time-zone translation is not the issue.
The issue appears to have come from the time-zone conversions as I was attempting (as SamVarshavchik pointed out). Unfortunately I am unable to use Howard Hinnant's very complete date and tz libraries because they require a mechanism to update the IANA time-zone DB that is required for them to work, so I resorted to wrapping the Windows native calls for the time-zone conversions; namely the TzSpecificLocalTimeToSystemTime and SystemTimeToTzSpecificLocalTime functions.
However these only work with SYSTEMTIME and not time_point. This meant I took the quick and easy option of converting the time_point to a FILETIME (just modify the "epoch") and the FILETIME to a SYSTEMTIME before passing it to one of the two above functions. This resulted in truncation of the time value when it was pushed into the SYSTEMTIME struct (which only holds millisecond resolution). The outcome is that while I was accurate for dates, I was not entirely accurate when converting the date back into the original value.
The new solution does no calendar mapping for the basic time_point to time_point translations. It uses the following code to work out the offset in std::chrono::minutes (where zoneInfo is a TIME_ZONE_INFORMATION):
time_point WindowsTzDateProvider::doToUtc(const time_point& inLocal) const {
return inLocal + getBias(inLocal);
}
time_point WindowsTzDateProvider::doToLocal(const time_point& inUtc) const {
return inUtc - getBias(inUtc);
}
std::chrono::minutes WindowsTzDateProvider::doGetBias(const time_point& input) const {
bool isDst = CalendarDateProvider::isDstInEffect(input);
minutes baseBias{ zoneInfo.Bias };
minutes extraBias{ isDst ? zoneInfo.DaylightBias : zoneInfo.StandardBias };
return baseBias + extraBias;
}
bool CalendarDateProvider::isDstInEffect(const time_point& t) {
time_t epochTime = clock::to_time_t(t);
tm out;
#ifdef WIN32
localtime_s(&out, &epochTime);
#else
localtime_r(&out, &epochTime);
#endif
return out.tm_isdst > 0;
}
Note: I'm using the non-virtual interface idiom for the classes, hence the "do..." versions of the methods.
Consider using this free, open source time zone library which does exactly what you want with very simple syntax, and works on VS-2013 and later:
#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 << format("%F %T %z", t) << '\n';
}
This should output for you:
2016-05-25 09:45:18.1970000 +0100

C++ boost get unix timestamp in UTC

Hello I'm trying to get time elapsed since epoch using boost in UTC but it seems that microsec_clock::universal_time(); doesn't return UTC time, instead it returns time in timezone of PC.
How can I get current time in miliseconds in UTC using boost?
Here is my code that I'm using
const long long unix_timestmap_now()
{
ptime time_t_epoch(date(1970, 1, 1));
ptime now = microsec_clock::universal_time();
time_duration diff = now - time_t_epoch;
return diff.total_milliseconds();;
}
Why you use a boost? All needed (which refers to the time) moved to the STL in C ++ .
It is important - not everyone knows that "unix timestamp" at a time is the same for the whole world, ie if the check time on the server in Russia, and for example on a server in the USA, the value will be the same (of course under the condition that both servers correct time right), it differs only in its transformation into understandable for people of form, depending on the server settings. And of course the reverse priobrazovanie will also vary if you do not set the time zone.
Tested on cpp.sh
#include <iostream>
#include <chrono>
int main ()
{
using namespace std::chrono;
system_clock::time_point tp = system_clock::now();
system_clock::duration dtn = tp.time_since_epoch();
std::cout << "current time since epoch, expressed in:" << std::endl;
std::cout << "milliseconds: " << duration_cast<milliseconds>(dtn).count();
std::cout << std::endl;
return 0;
}