Convert date-time from/to UTC timestamp using time location - c++

I would like to convert between date-times and timestamps on arbitrary time locations (eg. America/New_York)
(Time location, (year, ..., sec)) ==> UTC timestamp
(UTC timestamp, time location) ==> (year, ..., sec, dayOfWeek)
I'm doing to do these conversions on multiple threads and different time zones. The time locations for a given thread are not changing so I can store some time-zone structures for repeated usage.
I know that the first is ambiguous when a DST change happens (2:30 two or zero times in a day). It would be an extra if a situation like that would be reported, but it is absolutely not a priority.
Update: I would like to have a cross platform solution. Linux + Windows is fine. By arbitrary I mean that it comes from the user and has no relation to the processing machine's location.

Try this free, open source, modern parser of the complete IANA timezone database:
http://howardhinnant.github.io/date/tz.html
It requires C++11 or better. It currently requires you to download and maintain your own copy of the IANA database. This could be a blessing if you don't want to have to wait for OS platforms to update (you can be as responsive as the database maintainers).
Here is sample code:
#include <chrono>
#include <iostream>
#include "date.h"
#include "tz.h"
int
main()
{
using namespace std::chrono_literals;
using namespace date;
// Dave was born April 24, 1954. 10:03 AM pst
// Want to know when he is 2 Gigaseconds old
auto birthday = make_zoned("America/Los_Angeles",
local_days{apr/24/1954} + 10h + 3min);
std::cout << "born : " << birthday << '\n';
birthday = birthday.get_sys_time() + 2'000'000'000s;
std::cout << "2Gs birthday: " << birthday << '\n';
}
This finds Dave's 2Gs (gigasecond) birthday, and outputs:
born : 1954-04-24 10:03:00 PST
2Gs birthday: 2017-09-08 14:36:20 PDT
Note that all the arithmetic is done in the (implied) UTC timezone, so that changes in the local timezone ("America/Los_Angeles") are correctly accounted for. I.e. the birthday is during PST, and 2Gs later it is daylight saving time: PDT.
Tested on gcc-5.2, clang, VS-2015, and most recently, gcc-4.8.

You may like to have a look at Google C++ Time Zone Library:
CCTZ (C++ Time Zone) is a library for translating between absolute times and civil times (see the Fundamental Concepts section below for an explanation of these terms) using the rules defined by a time zone.
This library currently works on Linux and Mac OS X, using the standard IANA time zone data installed on the system in /usr/share/zoneinfo.

Related

Number of seconds between two dates including leap seconds

I'm fiddling around with time representation in C++.
I would like to have a strictly monotonic representation of time, that handles leap seconds well. The utc_clock in C++20 should be able to do that, and since my compiler doesn't support this version yet, I'm using HowardHinnant/date.
To understand the library better I have started making small test cases, but got stuck on one.
I take two dates, before and after insertion of a leap second and check that duration between those two dates actually has the extra second.
This is the test case:
TEST(DateTime, TimeLeap)
{
using namespace std::chrono;
using namespace date;
// Two dates with a leap second in between
// https://en.wikipedia.org/wiki/Leap_second
auto t1 = clock_cast<utc_clock>(static_cast<sys_days>(2016_y/December/31));
auto t2 = clock_cast<utc_clock>(static_cast<sys_days>(2017_y/January/1));
EXPECT_EQ(duration_cast<seconds>(t2 - t1).count(), 24 * 3600 + 1);
}
but it fails for me:
common/tests/datetime.cpp:39: Failure
Expected: duration_cast<seconds>(t2 - t1).count()
Which is: 86400
To be equal to: 24 * 3600 + 1
Which is: 86401
It seems that the conversion between sys_clock and utc_clock doesn't add the leap second.
Suspecting that the problem is the resolution of sys_days, I've also tried doing a time_point_cast<seconds>(...) before the clock_cast<utc_clock>, but the result didn't change.
I've also tried using 2017-01-02 as the second date, in case there was an issue with distinction between 2016-12-31 23:59:60 and 2017-01-01 00:00 -- the leap second also didn't appear there.
It looks like you're using the OS supplied timezone database (USE_OS_TZDB=1), and that the leapseconds aren't being read. This can be confirmed with:
cout << get_tzdb().leap_seconds.size() << '\n';
This should output 27 (currently), but for you I imagine it is outputting 0. This means leapsecond data is missing.
With a recent (2020-09-11) commit: https://github.com/HowardHinnant/date/commit/ba99134b8a7c4a6e7d28d738a0234a85dc6bd827, the leapsecond data is read from either one of these files:
zoneinfo/leapseconds
zoneinfo/leap-seconds.list
Both of these files are IANA-supplied, but have slightly different formats. Either file will do as they have duplicate information in them. tz.cpp will search for both. If your platform doesn't ship either one of these files, you can download it from the IANA data download and copy it into place manually.

How to get nanoseconds from boost::chrono::hight_resolution_clock::time_point?

I am new to boost and chrono. I am writing a logger that logs the timestamps of API calls, entry and exit. I tried using boost::xtime first, but it wasn't giving the high resolution value I needed. Hence was thinking about using Chrono. I declared a boost::chrono::hight_resolution_clock::time_stamp x; variable for getting the timestamp and assigned it to boost::chrono::hight_resolution_clock::now ();. Now, I need to get the nanoseconds from this variable and put it in my log file (thats the requirement). So I cast it boost::chrono::duration_cast (x). But it just wouldn't let me do that. It needs 2 parameters apparently, and I only have one. Is there a way to get around this?. Is it possible to create another time_stamp variable and assign zero to it and use that variable?. I tried assigning zero, but its not working. Kindly help me out.
Thanks,
Sam
If tagged c++11, any reason why not to use std::chrono?
// Using std::chrono
auto start = std::chrono::high_resolution_clock::now(); // start timer
/* do some work */
auto diff = std::chrono::high_resolution_clock::now() - start; // get difference
auto nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(diff);
std::cout << "it took: " << nsec.count() << " nanoseconds" << std::endl;
boost::chrono::duration_cast converts a duration into the specified units, but you've given it a boost::chrono::time_point, not a duration.
There's really no such thing as "the current time in nanoseconds". To get a duration, you need to specify the time since which you want to know how many nanoseconds have elapsed (an "epoch"). Different clocks will measure their time based on different epochs.
boost::chrono::system_clock (currently) uses the Unix epoch (midnight Jan 1, 1970) as its epoch, but it's not steady and it may not have the resolution you need (it's in nanoseconds on my Ubuntu box, but in 1/10,000,000ths of a second on my Windows box).
boost::chrono::high_resolution_clock uses boot up as its epoch, is steady, and measures time in nanoseconds on both boxes I tested on.
Boost also provides other clocks like process_cpu_clock that use other epochs and count in other units.
Thus you can get nanos since Jan 1, 1970 using system_clock, but it may not actually be nanosecond-accurate, and it may go backwards if the user changes the system time or the computer syncs with network time, or you can get nanos since some other point in time using high_resolution_clock.

C++ boost::chrono vs std:chrono time since epoch (get current system time) in windows

I tried to use boost/chrono....
using boost::chrono::high_resolution_clock::now().time_since_epoch()
I am not able to get the current system time instead I get Thu Jan 01 09:53:53 1970... but with std::chrono I am able to get the correct system time.
How to get current system time using boost/chrono same as std:: chrono?
To get the system time, use system_clock not high_resolution_clock.
There's no guarantee, in either Boost or the standard library, that high_resolution_clock will use the same epoch as the system clock.
Use simple math
boost::posix_time::ptime const time_epoch(boost::gregorian::date(1970, 1, 1));
auto ms = (boost::posix_time::microsec_clock::local_time() - time_epoch).total_microseconds();
std::cout << "microseconds: " << ms << "\n";
boost::posix_time::ptime now = time_epoch + boost::posix_time::microseconds(ms);
std::cout << boost::posix_time::to_iso_string(now);
More samples:
time since epoch to string using boost
Actual millis in a C++
Creating a boost::posix_time::ptime object from a 64 bit integer second count (custom epoch)

C++ timegm conversion DST to a certain timezone at a given time in the future?

I need an accurate conversion in my class from UTC time to local time of a given timezone, with or without DST in effect. My problem is, that when I use struct tm I have to provide the tm_isdst member, or leave it -1 to be determined automatically.
from mktime(3) - linux man page:
"The value specified in the tm_isdst field informs mktime() whether or not daylight saving time (DST) is in effect for the time supplied in the tm structure:
a positive value means DST is in effect;
zero means that DST is not in effect;
and a negative value means that mktime() should (use timezone information and system databases to) attempt to determine whether DST is in effect at the specified time.
Now here's my problem. I'm working with exchanges from all over the globe (from Chicago, New York, Sao Paolo, Melbourne, Buenos Aires, Johannesburg, Shanghai, Seoul ...). I have a table with the name of each timezone for each of the exchanges: for example Africa/Johannesburg, America/Chicago, America/Winnipeg.
The data I'm working on is expiration for some given futures and options financial instruments. The data I'm receiving is always in UTC, and I need to convert to the local time of the exchange.
Long story short, my current implementation is adding 1 extra hour to the expiration time of those assets that should not have the DST in effect (for example an instrument that expires in December and local time is America/Chicago, should add -360 minutes offset and DST 0, while another one which expires in June in the same timezone should have -360 timezone offset +60 DST offset, which would be -300 offset to the UTC timestamp. What I'm currently having the problem with, for example for the month of December I get 9:30 AM instead of 8:30 AM as the UTC timestamp contains the dst offset already.
Here is my function for doing the conversion, which is obviously broken:
#ifdef WIN32
#define timegm _mkgmtime
#endif
SimpleDateTime BusinessDateCalculator::UTC2TZ(const SimpleDateTime& utcDateTime, const int tz_utc_offset, const int tz_dst)
{
struct tm stm;
memset(&stm, 0, sizeof(stm));
stm.tm_year = utcDateTime.getYear() - 1900;
stm.tm_mon = utcDateTime.getMonth() - 1;
stm.tm_mday = utcDateTime.getDay();
stm.tm_hour = utcDateTime.getHour();
stm.tm_min = utcDateTime.getMinute();
stm.tm_sec = utcDateTime.getSecond();
stm.tm_isdst = -1; //see note at the top of this file
time_t tt = timegm( &stm );
tt += (tz_utc_offset + tz_dst) * 60;
struct tm ntm;
memset(&ntm, 0, sizeof(ntm));
gmtime_r( &tt, &ntm );
return SimpleDateTime(ntm.tm_year + 1900, ntm.tm_mon + 1, ntm.tm_mday, ntm.tm_hour, ntm.tm_min, ntm.tm_sec, utcDateTime.getMillisecond());
}
Where SimpleDateTime is a date-time class with additional functionality, like conversion from different date/time formats (SQL, FIX, Timeonly, DateOnly).
I have the timezone information of a certain exchange available at all time. My question can I provide the timezone name somehow, and let the timegm perform a database search and determine whether in the America/Chicago timezone the DST is or is not in effect on 2014-12-19 and accordingly not add the extra 60 minutes to the UTC time, and on the same date for example in the southern hemisphere timezone America/Sao_Paulo it is in effect until 16 Feb 2015, and for this timestamp it should add 60 minutes to get the correct local time for the given date.
When you refer to a time zone by it's IANA identifier (eg "America/Chicago") - that already includes all of the DST information and full history of the time zone. At least, it does in the original source data in the IANA time zone database.
You mentioned Boost (in comments). While Boost does have support for these types of identifiers, it makes the mistake of assuming that they are permanently fixed in time. That is not true, as time zones of the world change their offsets and DST rules all the time. Consider that the USA changed its DST rules in 2007, and that Russia is changing its time zones significantly later this year (Oct. 2014). If you look at the Boost time zone data file, you'll see that its format strips away all of the history and just maps each identifier to a single set of rules. For this reason, I recommend you do not use Boost for local time zone conversions.
Instead, consider using ICU. Among other things, it includes time zone conversion functions, and uses the full copy of the IANA time zone database. You can read more here, and see sample code here. I'm not particularly skilled in C++, but it would appear that you can use ICU's Calendar class to project a UTC time to a local time in a particular time zone.
Another option would be to use the time zone functions built in to the GNU C library. It also uses the full time zone database. There's a simple example of converting UTC tor local time using an IANA/Olson identifier on this site, which I have also posted below:
/*
C source code example: Convert UTC to local time zone, considering daylight
savings. Uses mktime(), gmtime() and localtime(). Works for dates between
years 1902 and 2037. Should compile and run with any recent GNU C if your
tzdata is healthy. Written by Robert Larsson http://rl.se
Code put in public domain; do what you want with it.
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define DESTZONE "TZ=Europe/Stockholm" // Our destination time zone
int main(void)
{
struct tm i;
time_t stamp; // Can be negative, so works before 1970
putenv("TZ=UTC"); // Begin work in Greenwich …
i.tm_year = 2009-1900; // Populate struct members with
i.tm_mon = 8-1; // the UTC time details, we use
i.tm_mday = 29; // 29th August, 2009 12:34:56
i.tm_hour = 12; // in this example
i.tm_min = 34;
i.tm_sec = 56;
stamp = mktime(&i); // Convert the struct to a Unix timestamp
putenv(DESTZONE); // Switch to destination time zone
printf("UTC : %s", asctime(gmtime(&stamp)));
printf("Local: %s", asctime(localtime(&stamp)));
return 0; // That’s it, folks.
}

How do you get the timezone (offset) for a location on a particular date?

I am storing messages in UTC. Therefore, if someone looks back to a previous message, I need to be able to convert the timestamp to what the time was relative to what the timezone was then. How do I get what the timezone was then?
For example, on September 3, 2012 the timezone is PDT. The current time offset is -0700. I send a message at 9:06. The time UTC is 16:06.
I come back to this message December 1, 2012. The current timezone is PST. The current time offset is -0800. I look at the message I sent on September 3, 2012. If I were to convert back from UTC using the current time offset I get 8:06 which is NOT when the message was sent. It was sent at 9:06.
Therefore, I need a way to find out that on September 3, 2012 the timezone was PDT, not PST.
P.S. without libraries would be the best, thanks.
Boost Date_time does that, here is simply example I had hanging around (code below):
edd#max:~/src/progs/C++$ g++ -o boost_posix_time_dst boost_posix_time_dst.cpp
edd#max:~/src/progs/C++$ ./boost_posix_time_dst
DST ran from 2012-Mar-11 02:00:00 to 2012-Nov-04 02:00:00
DST shortcut PDT
DST name Pacific Daylight Time
edd#max:~/src/progs/C++$
There is also functionality to form a date (your Dec 1, 2012) and see if it is inside a give interval (as formed here by the DST start and end).
I think you can also get it by forming a date and checking the isDST() boolean.
My short program is below. You need a local copy of the csv file which is a) in the Boost sources and b) on a number of sites dealing with timezones (eg Google's first or second hit finds it at CERN):
#include <iostream>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/local_time/local_time.hpp>
using namespace boost::posix_time;
using namespace boost::gregorian;
int main(int argc, char **argv) {
boost::local_time::tz_database tz;
tz.load_from_file("/tmp/date_time_zonespec.csv");
boost::local_time::time_zone_ptr tzp =
tz.time_zone_from_region("America/Los_Angeles");
int year = 2012;
boost::posix_time::ptime t1 = tzp->dst_local_start_time(year);
boost::posix_time::ptime t2 = tzp->dst_local_end_time(year);
std::cout << "DST ran from " << t1 << " to " << t2 << std::endl;
std::cout << "DST shortcut " << tzp->dst_zone_abbrev() << std::endl;
std::cout << "DST name " << tzp->dst_zone_name() << std::endl;
}
If it's a simple C++ application that doesn't require the use of separate time-zones during execution then you can simply use localtime to get a shared struct tm * reference that contains timezone information for a particular time_t that it is called with. If you system is configured with a timezone of PST, then when you invoke localtime, then it will use the pacific timezone information for the display - an example using Jan 1, 2012 (GMT) and June 1, 2012:
time_t def_time = 1325376000;
struct tm *a_tim = localtime(&def_time);
printf("%s %ld %d\n", a_tim->tm_zone, a_tim->tm_gmtoff, a_tim->tm_isdst);
def_time = 1338505200;
a_tim = localtime(&def_time);
printf("%s %ld %d\n", a_tim->tm_zone, a_tim->tm_gmtoff, a_tim->tm_isdst);
On my system (TZ=Europe/Dublin) this displays:
GMT 0 0
IST 3600 1
By overriding the TZ environment variable with America/Los_Angeles I get:
PST -28800 0
PDT -25200 1
i.e. the system is well able to determine the timezone name, offset from GMT and if summer time is in effect from the UTC timestamp.
Edit: trying to use multiple timezones within C/C++ programs simultaneously using the posix supplied timezone routines is horrible, and I would definitely recommend using boost if you're in that situation as it's a quick solution.
Just knowing the current offset isn't enough to tell you the offset for the same location at another time of year; there are plenty of pairs of locations that share a timezone for only part of the year (e.g. Central Europe and Algeria, or the eastern US and Colombia).
The simplest fix would be to store the timezone along with the message.