5

In a program of mine I have to read in times in several different formats. In all formats however the time is given in UTC (i.e. not in my local timezone). What would be the best way for a function that takes the date string and a format string and outputs a std::time_t timestamp?

Currently I am using boost

#include "boost/date_time/gregorian/gregorian.hpp"
#include "boost/date_time/posix_time/posix_time.hpp"

std::time_t to_timestamp_utc(const std::string& _dateString) {
    using namespace boost::gregorian;
    using namespace boost::posix_time;
    return to_time_t(ptime(from_undelimited_string(_dateString)));
}

but this works only for the "YYYYMMDD" format. The standard library function std::get_time on the other hands assumes the input date to be formatted in my local time not UTC (or at least I haven't found a way to change it yet). Any suggestions are most welcome.

Current Solution based on Maxim Egorushkin suggestion.

std::time_t utc_to_timestamp(const std::string& _dateString, const std::string& _format) {
    // Set sec, min, hour to zero in case the format does not provide those
    std::tm timeStruct;
    timeStruct.tm_sec = 0;
    timeStruct.tm_min = 0;
    timeStruct.tm_hour = 0;
    char* const result = strptime(_dateString.c_str(), _format.c_str(), &timeStruct);
    // Throw exception if format did not work
    REQUIRE(result == _dateString.c_str()+_dateString.size(), "Failed to parse dateTime.");
    return timegm(&timeStruct);
}
Haatschii
  • 9,021
  • 10
  • 58
  • 95
  • Which formats do you need? (UK, US, ?) – BiagioF Aug 11 '16 at 13:54
  • You need to know the formats you expect. E.g. `07-06-2016` is ambiguous because in US they do mm-dd-yyyy, whereas in EU they do dd-mm-yyyy. – Maxim Egorushkin Aug 11 '16 at 14:33
  • @BiagioFesta I mainly need `yyyy-mm-dd hh:mm:ss`, `yyyymmdd` and `dd.mm.yyyy hh:mm`, but it would be nice to have the function take an arbitrary format as an argument as `std::get_time` does for example. – Haatschii Aug 11 '16 at 15:10
  • @MaximEgorushkin I do know them in advance, I just want to be able to give the format as an additional paramenter, because I need several formats (see previous comment). – Haatschii Aug 11 '16 at 15:11
  • If the dates are being read initially from a stream, you could use `std::ge_time` to parse it to a `tm` instead. – apokaliptis Dec 18 '19 at 21:28

2 Answers2

4

Here is a free, open-source C++11/14 library with a parse function that takes a std::basic_istream<CharT, Traits>, a format string (std::basic_string<CharT, Traits>), and a std::chrono::time_point<system_clock, seconds>.

template <class CharT, class Traits, class Duration>
void
parse(std::basic_istream<CharT, Traits>& is,
      const std::basic_string<CharT, Traits>& format, sys_time<Duration>& tp);

You use it like this:

std::istringstream in{"2014-11-12 19:12:14"};
date::sys_seconds tp;
in >> date::parse("%F %T", tp);

If failbit is not set on exit, tp will be Unix Time with a precision of seconds. You can convert that to a time_t like so:

time_t t = system_clock::to_time_t(tp);

Or you could just print it out:

cout << tp.time_since_epoch().count() << '\n';

If you do get failbit set, you could try again with another format string.

This will also handle sub-second precision should your timestamps have that. All output will be UTC, as this is a type-safe library where time_point<system_clock, whatever-duration> means UTC (technically it means Unix Time). The library also has a way to parse local times.

If your formats are ambiguous, then one might succeed and return the wrong value, making the order with which you try your formats significant.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Hey thanks for that. I'll definitely have a look at it. For now however I am after a more lightweight solution. – Haatschii Aug 14 '16 at 22:51
4

If you know the possible formats in advance you can use strptime function to find the format that parses the string successfully. It returns struct tm which you treat as broken-down UTC and pass it into timegm to get time_t.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271