mirror of
https://github.com/ceph/ceph
synced 2025-01-19 17:41:39 +00:00
50190741f1
Signed-off-by: Willem Jan Withagen <wjw@digiware.nl>
201 lines
4.4 KiB
C++
201 lines
4.4 KiB
C++
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
|
// vim: ts=8 sw=2 smarttab
|
|
|
|
#include "iso_8601.h"
|
|
#include "include/timegm.h"
|
|
|
|
#include <sstream>
|
|
|
|
namespace ceph {
|
|
using std::chrono::duration_cast;
|
|
using std::chrono::nanoseconds;
|
|
using std::chrono::seconds;
|
|
using std::setw;
|
|
using std::size_t;
|
|
using std::stringstream;
|
|
using std::string;
|
|
using std::uint16_t;
|
|
|
|
using boost::none;
|
|
using boost::optional;
|
|
using boost::string_ref;
|
|
|
|
using ceph::real_clock;
|
|
using ceph::real_time;
|
|
|
|
using sriter = string_ref::const_iterator;
|
|
|
|
namespace {
|
|
// This assumes a contiguous block of numbers in the correct order.
|
|
uint16_t digit(char c) {
|
|
if (!(c >= '0' && c <= '9')) {
|
|
throw std::invalid_argument("Not a digit.");
|
|
}
|
|
return static_cast<uint16_t>(c - '0');
|
|
}
|
|
|
|
optional<real_time> calculate(const tm& t, uint32_t n = 0) {
|
|
ceph_assert(n < 1000000000);
|
|
time_t tt = internal_timegm(&t);
|
|
if (tt == static_cast<time_t>(-1)) {
|
|
return none;
|
|
}
|
|
|
|
return boost::make_optional<real_time>(real_clock::from_time_t(tt)
|
|
+ nanoseconds(n));
|
|
}
|
|
}
|
|
|
|
optional<real_time> from_iso_8601(const string_ref s,
|
|
const bool ws_terminates) noexcept {
|
|
auto end = s.cend();
|
|
auto read_digit = [end](sriter& c) mutable {
|
|
if (c == end) {
|
|
throw std::invalid_argument("End of input.");
|
|
}
|
|
auto f = digit(*c);
|
|
++c;
|
|
return f;
|
|
};
|
|
|
|
auto read_digits = [end, &read_digit](sriter& c, std::size_t n) {
|
|
auto v = 0ULL;
|
|
for (auto i = 0U; i < n; ++i) {
|
|
auto d = read_digit(c);
|
|
v = (10ULL * v) + d;
|
|
}
|
|
return v;
|
|
};
|
|
auto partial_date = [end, ws_terminates](sriter& c) {
|
|
return (c == end || (ws_terminates && std::isspace(*c)));
|
|
};
|
|
auto time_end = [end, ws_terminates](sriter& c) {
|
|
return (c != end && *c == 'Z' &&
|
|
((c + 1) == end ||
|
|
(ws_terminates && std::isspace(*(c + 1)))));
|
|
};
|
|
auto consume_delimiter = [end](sriter& c, char q) {
|
|
if (c == end || *c != q) {
|
|
throw std::invalid_argument("Expected delimiter not found.");
|
|
} else {
|
|
++c;
|
|
}
|
|
};
|
|
|
|
tm t = { 0, // tm_sec
|
|
0, // tm_min
|
|
0, // tm_hour
|
|
1, // tm_mday
|
|
0, // tm_mon
|
|
70, // tm_year
|
|
0, // tm_wday
|
|
0, // tm_yday
|
|
0, // tm_isdst
|
|
};
|
|
try {
|
|
auto c = s.cbegin();
|
|
{
|
|
auto y = read_digits(c, 4);
|
|
if (y < 1970) {
|
|
return none;
|
|
}
|
|
t.tm_year = y - 1900;
|
|
}
|
|
if (partial_date(c)) {
|
|
return calculate(t, 0);
|
|
}
|
|
|
|
consume_delimiter(c, '-');
|
|
t.tm_mon = (read_digits(c, 2) - 1);
|
|
if (partial_date(c)) {
|
|
return calculate(t);
|
|
}
|
|
consume_delimiter(c, '-');
|
|
t.tm_mday = read_digits(c, 2);
|
|
if (partial_date(c)) {
|
|
return calculate(t);
|
|
}
|
|
consume_delimiter(c, 'T');
|
|
t.tm_hour = read_digits(c, 2);
|
|
if (time_end(c)) {
|
|
return calculate(t);
|
|
}
|
|
consume_delimiter(c, ':');
|
|
t.tm_min = read_digits(c, 2);
|
|
if (time_end(c)) {
|
|
return calculate(t);
|
|
}
|
|
consume_delimiter(c, ':');
|
|
t.tm_sec = read_digits(c, 2);
|
|
if (time_end(c)) {
|
|
return calculate(t);
|
|
}
|
|
consume_delimiter(c, '.');
|
|
|
|
auto n = 0UL;
|
|
auto multiplier = 100000000UL;
|
|
for (auto i = 0U; i < 9U; ++i) {
|
|
auto d = read_digit(c);
|
|
n += d * multiplier;
|
|
multiplier /= 10;
|
|
if (time_end(c)) {
|
|
return calculate(t, n);
|
|
}
|
|
}
|
|
} catch (std::invalid_argument& e) {
|
|
// fallthrough
|
|
}
|
|
return none;
|
|
}
|
|
|
|
string to_iso_8601(const real_time t,
|
|
const iso_8601_format f) noexcept {
|
|
ceph_assert(f >= iso_8601_format::Y &&
|
|
f <= iso_8601_format::YMDhmsn);
|
|
stringstream out(std::ios_base::out);
|
|
|
|
auto sec = real_clock::to_time_t(t);
|
|
auto nsec = duration_cast<nanoseconds>(t.time_since_epoch() %
|
|
seconds(1)).count();
|
|
|
|
struct tm bt;
|
|
gmtime_r(&sec, &bt);
|
|
out.fill('0');
|
|
|
|
out << 1900 + bt.tm_year;
|
|
if (f == iso_8601_format::Y) {
|
|
return out.str();
|
|
}
|
|
|
|
out << '-' << setw(2) << bt.tm_mon + 1;
|
|
if (f == iso_8601_format::YM) {
|
|
return out.str();
|
|
}
|
|
|
|
out << '-' << setw(2) << bt.tm_mday;
|
|
if (f == iso_8601_format::YMD) {
|
|
return out.str();
|
|
}
|
|
|
|
out << 'T' << setw(2) << bt.tm_hour;
|
|
if (f == iso_8601_format::YMDh) {
|
|
out << 'Z';
|
|
return out.str();
|
|
}
|
|
|
|
out << ':' << setw(2) << bt.tm_min;
|
|
if (f == iso_8601_format::YMDhm) {
|
|
out << 'Z';
|
|
return out.str();
|
|
}
|
|
|
|
out << ':' << setw(2) << bt.tm_sec;
|
|
if (f == iso_8601_format::YMDhms) {
|
|
out << 'Z';
|
|
return out.str();
|
|
}
|
|
out << '.' << setw(9) << nsec << 'Z';
|
|
return out.str();
|
|
}
|
|
}
|