Converting ISO8601 string to milliseconds since epoch - c++

Sample string:
2018-10-31T14:45:21.778952-07:00
I would like to convert it to int64_t representing milliseconds (or even microseconds) since epoch. The timezone can vary. The code will be executing on a linux box, but I only have access to std and folly (cannot use any arbitrary 3P libraries).
I searched for this and found a few different ways that do not work for me:
strptime() and std::get_time() lose the millisecond precision
More importantly, neither of those can deal with timezone offsets
Some other solutions depend on 3P libraries
Is there some easy way to do this?

From the comments above:
I am looking into using Howard's library. However, that it makes a web call gives me pause. I assume that if the call fails it will just use the locally stored timezone name data? We won't be dealing with timezone names, so I don't care about those. However, making a network call might be an issue.
Howard's library is layered:
A foundational layer that does not need the IANA time zone database and thus never makes networking calls. This is a single header, header-only library.
A time zone layer that is aware of the IANA time zone database. This layer can be configured to make network calls or not (depending on build flags), or even use your OS's time zone database (except on Windows).
Your application does not require the time zone layer as it only deals with UTC offsets, and not time zone names or rules.
Here is a function that converts a std::string with your sample format into a std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds>. That type is a big verbose mouthful for a system_clock::time_point except guaranteed to have microseconds precision. The foundational layer has a type alias for this type called date::sys_time<std::chrono::microseconds>.
#include "date/date.h"
#include <chrono>
#include <iostream>
#include <sstream>
auto
to_sys_time(std::string s)
{
using namespace date;
using namespace std;
using namespace std::chrono;
istringstream in{move(s)};
in.exceptions(ios::failbit);
sys_time<microseconds> tp;
in >> parse("%FT%T%z", tp);
return tp;
}
Put the string into a istringstream.
Optionally set the istringstream to throw an exception if it fails to parse something (you may choose to handle errors differently).
Declare your time_point with the desired precision.
Parse it with your desired format.
Return it.
You can exercise this function like so:
int
main()
{
auto tp = to_sys_time("2018-10-31T14:45:21.778952-07:00");
using date::operator<<;
std::cout << tp << " UTC\n";
std::cout << tp.time_since_epoch() << '\n';
}
Call to_sys_time with the desired input string.
Make the streaming operators in namespace date available.
Print out the time_point (this is a UTC time_point).
Extract and print out the duration of the time_point.
The output of this program is:
2018-10-31 21:45:21.778952 UTC
1541022321778952µs
This program will port to C++20 by removing #include "date/date.h", using namespace date; and using date::operator<<;.

Related

C++ Convert Unix time (nanoseconds) to readable datetime in local timezone

I have a Unix time (nanoseconds since epoch) and I would like to go back and forth between this and a readable string in a specifiable timezone (using TZ strings), with nanoseconds preserved. I am using C++17 but willing to migrate to C++20 if it would make things much easier.
Example:
uint64_t unix_time = 1669058870115719258;
std::string datetime = convert_unix_to_datetime(unix_time, "America/Detroit");
std::cout << datetime << "\n";
std::cout << convert_datetime_to_unix_time(datetime) << "\n";
Desired output:
2021-11-21 14:27:50.115719258
1669058870115719258
Apologies if this is a basic question--I have searched around the site and tried to implement solutions but keep getting confused by different C++ versions, whether the datetime is in UTC or local timezone, different types like chrono::time_point vs. time_t etc. Hoping to find a simple solution here.
Using C++20, this is very easy. Using C++11/14/17 it is harder but doable with a free, open-source time zone library.
Here is what it looks like with C++20:
#include <chrono>
#include <cstdint>
#include <format>
#include <iostream>
#include <sstream>
#include <string>
std::string
convert_unix_to_datetime(std::uint64_t unix_time, std::string const& tz)
{
using namespace std;
using namespace chrono;
sys_time<nanoseconds> tp{nanoseconds{unix_time}};
return format("{:%F %T} ", zoned_time{tz, tp}) + tz;
}
The first line converts the uint64_t, first into a nanoseconds chrono::duration, and then into a nanoseconds-precision chrono::time_point based on system_clock. chrono::system_clock uses Unix Time as its measure: http://eel.is/c++draft/time.clock.system#overview-1
The second line creates a chrono::zoned_time from the time zone name, and the sys_time timepoint. A zoned_time is a simple pairing of these two objects from which you can extract a local time. When formatted, it is the local time that is printed. Here I've used "%F %T" as the format which will output with the syntax: YYYY-MM-DD HH:MM:SS.fffffffff. Many formatting flags are available..
Finally I've added the time zone name to the timestamp. This is done so that the parsing function can recover the time zone name as it is not passed in as one of the parameters to convert_datetime_to_unix_time.
std::uint64_t
convert_datetime_to_unix_time(std::string s)
{
using namespace std;
using namespace chrono;
istringstream in{std::move(s)};
in.exceptions(ios::failbit);
std::string tz_name;
local_time<nanoseconds> tp;
in >> parse("%F %T %Z", tp, tz_name);
zoned_time zt{tz_name, tp};
return zt.get_sys_time().time_since_epoch().count();
}
The first line moves the string into a istringstream as the parsing must use a stream. I've set the stream to throw an exception if there is a syntax error in the input s. If you would rather check for failbit manually, remove the line that sets failbit to throw an exception.
Next arguments are default constructed which will be parsed into:
std::string tz_name;
local_time<nanoseconds> tp;
Here it is important to use the type local_time as opposed to sys_time because it is the local time that was formatted out. Use of the local_time type informs the library how to apply the UTC offset under the zoned_time constructor after parse.
The parse can pick up the time zone name with the %Z flag, and will place it in the last argument to parse.
Construct the zoned_time with the time zone and time point.
Finally, the sys_time (Unix Time) can be extracted from the zoned_time with the .get_sys_time() member function. This will have precision nanoseconds since the input local_time has precision nanoseconds. The underlying integral count of nanoseconds is extracted with .time_since_epoch().count().
Using your example driver, this will output:
2022-11-21 14:27:50.115719258 America/Detroit
1669058870115719258
As I write this, only the latest MSVC tools fully implement this part of C++20.
If you need to use the free, open-source time zone library, a few minor changes are needed:
Add using namespace date; to each function.
Change the format string from "{:%F %T} " to "%F %T ".
Add #include "date/tz.h".
Compile and link to tz.cpp using the instructions at https://howardhinnant.github.io/date/tz.html#Installation
This will by default download a copy of the IANA time zone database, though there are ways to avoid that on non-Windows platforms (described in the installation instructions).

Converting timezone offset from timezone name string in C/C++ in a thread-safe way

I'm trying to implement a function such that can convert a given timezone name string, which it does:
Accepts an input timezone string, for example, "Australia/Melbourne";
Checks host OS (assume it's a POSIX environment) timezone database;
Returns an integer of timezone offset in seconds (for Melbourne in DST, it's 36000).
It can be done by calling putenv("TZ=Australia/Melbourne") but this is may not be thread-safe.
Is there a way to do it without putenv() or somehow let it be thread-safe? Thanks in advance!
Since compilers don't yet support all the features of the C++20 std::chrono library, I will write a solution that's using Howard Hinnant's datehttps://github.com/HowardHinnant/date library. C++20 is introducing std::chrono::zoned_time which you can use to achieve what you want.
#include "date/tz.h"
#include <iostream>
int main()
{
auto current_time = std::chrono::system_clock::now();
auto la = date::zoned_time{"America/Los_Angeles", current_time};
auto sy = date::zoned_time{"Australia/Sydney", current_time};
std::cout << date::format("%T\n", sy.get_local_time() - la.get_local_time());
}
The above example will give you the difference between two time zones. Furthermore, if you want to obtain the current OS's time zone you could use something that, I hope, will be supported by compilers in future - std::chrono::time_zone::name.
In C++20, you will be able to use std::chrono::tzdb::locate_zone(), that will convert a given timezone name to a std::chrono::time_zone object that you can query to get the offset.
There is no standard C function that deals with time zones. There are POSIX.1-compliant functions, but they are indeed not thread-safe. However, depending on your operating system, you might be able to open the timezone database files and parse them manually. For example, on Linux you could open /usr/share/zoneinfo/Australia/Melbourne and parse it according to its specification in the tzfile(5) manpage.
In addition to OP's thread concerns, another consideration:
"timezone offset from timezone name string" is not sufficient in general as the offset differs depending on data/time throughout the year.
Even if no annual daylight adjustments occur in the select zone, the offset can vary due to the history of the zone.
long offset(tz_name) is not enough, long offset(tz_name, time_t t) is needed.

How to get UTC time offset in c++ with windows os

My question is pretty simple. How can I get the UTC offset for Philippines which is +8 and for Japan which is +9, and convert that to integer.
Any suggestions is a great help. Thank you.
UPDATE
For further explanation, I have a c++ desktop application, and I need to automatically detect its timezone wherever the application is used. Like if the application will be used in Japan, the timezone offset for japan will be used. Is that possible?
If you would rather not dig into the Windows-specific API, here is portable, and C++20-standard C++ code1 to do this using Howard Hinnant's timezone library.
#include "date/tz.h"
#include <iostream>
int
main()
{
using date::operator<<;
using std::chrono::system_clock;
std::cout << date::current_zone()->get_info(system_clock::now()).offset << '\n';
}
date::current_zone() detects the computer's current local time zone. This returns a pointer to a date::time_zone which has a member function called get_info().
You can call time_zone::get_info with either a UTC time_point or a local_time time_point to get all kinds of information about this time zone at the indicated point in time (as the information changes with times according to current politics). Use of system_clock::now() as the time_point will get the current information for the time_zone.
A date::sys_info is returned which is an aggregate, one of which members is called offset. This is the current UTC offset with units of std::chrono::seconds.
This just output for me:
-14400s
Indicating that my current local time zone is 14400 seconds west of UTC.
For typical code, one should not program down at the level of adding UTC offsets directly. There is a higher-level API to do this for you. Use of the higher-level API prevents careless errors such as adding the UTC offset when you should have subtracted (or vice-versa). However this low-level API exists because sometimes low-level access to information like this is what makes your program efficient.
For example, here is a higher-level code that simply prints out the current local time:
std::cout << date::make_zoned(date::current_zone(), system_clock::now()) << '\n';
system_clock::now() is still explicitly called, but now the UTC offset addition, as well as the call to current_zone()->get_info() is encapsulated under this higher-level API.
1 This library is in namespace std::chrono in C++20: http://eel.is/c++draft/time.zone.db.access#8
If in your os, std::localtime can access time zone info (in most cases it can, by definition), this is my one-line code:
auto offset_epoch = std::localtime(new time_t(0));
where offset_epoch->tm_hour is the offset hour and offset_epoch->tm_min is the offset minute. You can safely ignore offset_epoch->tm_year as it is of value 70.
It works fine for me so far.

Using std::chrono / date::gps_clock for converting a double gps timestamp to utc/tai

I get a timestamp from a GPS device in a gps_data struct as a double.
I'd like to convert this GPS timestamp to UTC and TAI times, something simple as:
void handle_gps_timestamp(double timestamp)
{
double utc = utc_from_gps(timestamp);
double tai = tai_from_gps(timestamp);
do_stuff(gps, utc, tai);
}
Luckily I found Howard Hinnant's date and timezone library (proposed for C++20) that seems to provide this exact functionality. Unfortunately, at least from what I can see, the date/tz/chrono library has no convenient methods that allow this simple usage.
I must first somehow "transfer" my double into a known chrono/date type. But OK, since I understand the overall concept, namely that the timepoint is defined as a duration after (or before) the epoch of a clock, and I think that this is a beautiful model.
Assumption
I should be able to very easily translate that model to fit my problem, right?
In my case, I have a timestamp that is a point in time, specified as the duration since the gps epoch. Now, there should be a class type of a clock that abstracts and handles all of this for me, I'd like to think. And yes! There is a date::gps_clock and a date::gps_time, which surely should do the work.
Problem
I cannot make it work for me. I'm sure the solution is trivial.
Question
Can someone give me a helping hand, showing how I should use Howard's date library applied to my problem?
It is difficult to answer this question precisely because the input to the problem is underspecified:
I get a timestamp from a GPS device in a gps_data struct as a double ... specified as the duration since the gps epoch.
Therefore I'm going to make some assumptions. I'll state all of my assumptions, and hopefully it will be clear how to alter my answer for other guesses/facts about what that double represents.
Let's say that the double is a non-integral count of milliseconds since the gps epoch. Let's furthermore assume that I want to capture the precision of this input down to microseconds.
#include "date/tz.h"
#include <cstdint>
#include <iostream>
int
main()
{
double gps_input = 1e+12 + 1e-3;
using namespace date;
using namespace std::chrono;
using dms = duration<double, std::milli>;
gps_time<microseconds> gt{round<microseconds>(dms{gps_input})};
auto utc = clock_cast<utc_clock>(gt);
auto tai = clock_cast<tai_clock>(gt);
std::cout << gt << " GPS\n";
std::cout << utc << " UTC\n";
std::cout << tai << " TAI\n";
}
I've arbitrarily created an example input and stored it in gps_input.
Some using directives make the code a lot less verbose.
A custom chrono::duration type that exactly matches the documented specification for what the double represents makes things much simpler, and lessens the chance for errors. In this case I've made a chrono::duration that stores milliseconds in a double and named that type dms.
Now you simply convert the double to dms, and then using round, convert the dms to microseconds, and store those microseconds in a gps time point with precision microseconds or finer. One could use duration_cast in place of round, but when converting from floating point to integral, I usually prefer round, which means round-to-nearest-and-to-even-on-tie.
Now that you have a gps_time, one can use the clock_cast function to convert to other times such as utc_time and tai_time.
This program outputs:
2011-09-14 01:46:40.000001 GPS
2011-09-14 01:46:25.000001 UTC
2011-09-14 01:46:59.000001 TAI
Adjust the milliseconds and microseconds units above as needed. For example if the input represents seconds, the easiest thing to do is to default the second template argument on dms:
using dms = duration<double>;
This library works with C++11/14/17. And with minor modifications it is now part of the official C++20 specification.
The other answer isn't bad but it does require you to have c++17, curl, run cmake, and acquire some custom libraries.
Something that is much easier to drop in as a .h and .cpp would be http://www.leapsecond.com/tools/gpsdate.c.
That doesn't handle the TAI conversion but that might also be on that list.

C++ date and time

I am developing an Appointment application in C++ and want to use some Date and Time features.
Is it easier to just use strings when talking about Date and Time, or should I write or get a Date/Time class?
I am wanting to code an appointment class that holds both the time and date of an appointment. Once I have coded the class file, I am wanting to integrate it into a forms application in C++ builder.
I see that there is a TMonthCalendar control. I would like to use this control when making the forms application. As such, what format for the date does this control use? I would like to use the same type as the control when making the class so that I can easily integrate it together.
UPDATE
I have found that it uses the TDateTime type. My question is this: What include statement do I need to use to use this in a console application?
C++11 includes convenience data types and functions for date/time representations, as well as their conversion to strings.
With that, you can do things like this (pretty self-explanatory, I think):
#include <iostream>
#include <iomanip>
#include <ctime>
int main()
{
std::time_t t = std::time(NULL);
std::tm tm = *std::localtime(&t);
std::cout << "Time right now is " << std::put_time(&tm, "%c %Z") << '\n';
}
In particular, there are data types std::time_t and std::tm, and a very nice IO manipulator std::put_time for pretty printing. The format strings used by it are well-documented at cppreference.
This is also supposed to work together well with locales, e.g. for a Japanese time/date format:
std::cout.imbue(std::locale("ja_JP.utf8"));
std::cout << "ja_JP: " << std::put_time(&tm, "%c %Z") << '\n';
The chrono library included in the C++11 standard library also allows you to do simple time/date arithmetic conveniently:
std::chrono::time_point<std::chrono::system_clock> now;
now = std::chrono::system_clock::now();
/* The day before today: */
std::time_t now_c = std::chrono::system_clock::to_time_t(
now - std::chrono::hours(24));
Unfortunately, not all of this is available in all compilers yet. In particular, the std::put_time function does not seem to be available in GCC 4.7.1 yet. To get the code I gave initially to work, I had to use the slightly less elegant std::strftime function:
#include <iostream>
#include <iomanip>
#include <ctime>
int main()
{
std::time_t t = std::time(NULL);
std::tm tm = *std::localtime(&t);
constexpr int bufsize = 100;
char buf[bufsize];
if (std::strftime(buf,bufsize,"%c %Z",&tm) != 0)
std::cout << "Time right now is " << buf << std::endl;
}
Is it easier to just use strings when talking about Date and Time?
No. Even simple things like calculating a duration if you have a meeting start and end date need to account for complex things such as leap years.
should I write or get a Date/Time class?
No. Writing a Date/Time Library seems simple, but it's rather difficult to get right and extremely easy to get wrong. Also, others have done it before - boost is a collection of free libraries with stellar reputation. So many others in fact, that it's become a cliche that newbie programmers want to write a Date/Time library, failing horribly at it.
I see that there is a TMonthCalendar control. [...] I have found that it uses the TDateTime type.
Relying on the same Date/Time class as your GUI framework is ok, but if you later change the GUI framework it can become an issue. Since it's not terribly hard to swap a sensible Date/Time library for another sensible Date/Time library later on, just use one that you find easy to use.