6

How do I parse a RFC3339 timestamp ("1985-04-12T23:20:50.52Z") (i.e. a subset of ISO8601) in C++03? I'm using Boost, but none of the Boost datetime libraries seem to include a function to do this.

The type of the actual time object doesn't matter, as long as I can easily compare it to 'now'. I only care about timestamps in the UTC timezone.

Community
  • 1
  • 1
Phil
  • 2,239
  • 3
  • 25
  • 26
  • I think I am doing just that in the (R-based) package [anytime](https://github.com/eddelbuettel/anytime) -- see its `src/anytime.cpp`. – Dirk Eddelbuettel Jun 30 '17 at 09:46
  • Do you have examples? Are we going to have to assemble a question ourselves from hyperlinks? – sehe Jun 30 '17 at 09:49
  • Good point, I've added an example – Phil Jun 30 '17 at 10:12
  • Does it need to be C++03? In C++11 you could try to use `std::get_time`. – sh- Jun 30 '17 at 10:26
  • If there is C++11>> or higher use [data.h](https://github.com/HowardHinnant/date) is built at top of chrono. And works fine with glibc and musl - `std::get_time` unfortunately not. – S.R Mar 15 '18 at 11:25

2 Answers2

0

Without using Boost, just strptime you can.

Assuming the same _adaptive_parser_ helper posted here: Using boost parse datetime string: With single digit hour format

Note: the samples taken from the RFC link

#include "adaptive_parser.h"
#include <string>
#include <iostream>

int main() {    
    using namespace mylib::datetime;

    adaptive_parser parser;

    for (std::string const input : {
            "1985-04-12T23:20:50.52Z",
            "1996-12-19T16:39:57-08:00",
            "1990-12-31T23:59:60Z",
            "1990-12-31T15:59:60-08:00",
            "1937-01-01T12:00:27.87+00:20",
        })
    try {
        std::cout << "Parsing '" << input << "'\n";
        std::cout << " -> epoch " << parser(input).count() << "\n";
    } catch(std::exception const& e) {
        std::cout << "Exception: " << e.what() << "\n";
    }
}

Prints

Parsing '1985-04-12T23:20:50.52Z'
 -> epoch 482196050
Parsing '1996-12-19T16:39:57-08:00'
 -> epoch 851042397
Parsing '1990-12-31T23:59:60Z'
 -> epoch 662688000
Parsing '1990-12-31T15:59:60-08:00'
 -> epoch 662688000
Parsing '1937-01-01T12:00:27.87+00:20'
 -> epoch -1041335973

Of course, you can limit the number of accepted patterns for the subset you require.

sehe
  • 374,641
  • 47
  • 450
  • 633
0

Have limitation of parsing timezone.

#include <sstream>
#include <iostream>
#include <string>
#include <iomanip>

int main() {
  std::tm t = {};
  std::string s = "2016-01-02T15:04:05+09:00";
  int tz_offset = 0;
  auto pos = s.find_last_of("+-Z");
  if (pos != s.npos) {
    std::string zone = s.substr(pos);
    tz_offset = std::atoi(zone.c_str());
    s.resize(pos);
  }
  std::stringstream ss(s);
  ss >> std::get_time(&t, "%Y-%m-%dT%H:%M:%S");
  if (ss.fail()) {
    std::cout << "Parse failed\n";
  } else {
    std::time_t l = std::mktime(&t);
    std::tm tm_utc(*std::gmtime(&l));
    std::time_t utc = std::mktime(&tm_utc);
    tz_offset += (utc - l);
    l = std::mktime(&t) - tz_offset;
    t = *std::gmtime(&l);
    std::cout << std::put_time(&t, "%c") << std::endl;
  }
}
mattn
  • 7,571
  • 30
  • 54