Pratik
Pratik

Reputation: 378

Converting ISO8601 string to milliseconds since epoch

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:

  1. strptime() and std::get_time() lose the millisecond precision
  2. More importantly, neither of those can deal with timezone offsets
  3. Some other solutions depend on 3P libraries

Is there some easy way to do this?

Upvotes: 2

Views: 1741

Answers (1)

Howard Hinnant
Howard Hinnant

Reputation: 218860

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:

  1. 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.

  2. 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<<;.

Upvotes: 4

Related Questions