1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
5 #include "include/timegm.h"
10 using std::chrono::duration_cast;
11 using std::chrono::nanoseconds;
12 using std::chrono::seconds;
15 using std::stringstream;
20 using boost::optional;
21 using boost::string_ref;
23 using ceph::real_clock;
24 using ceph::real_time;
26 using sriter = string_ref::const_iterator;
29 // This assumes a contiguous block of numbers in the correct order.
30 uint16_t digit(char c) {
31 if (!(c >= '0' && c <= '9')) {
32 throw std::invalid_argument("Not a digit.");
34 return static_cast<uint16_t>(c - '0');
37 optional<real_time> calculate(const tm& t, uint32_t n = 0) {
38 ceph_assert(n < 1000000000);
39 time_t tt = internal_timegm(&t);
40 if (tt == static_cast<time_t>(-1)) {
44 return boost::make_optional<real_time>(real_clock::from_time_t(tt)
49 optional<real_time> from_iso_8601(const string_ref s,
50 const bool ws_terminates) noexcept {
52 auto read_digit = [end](sriter& c) mutable {
54 throw std::invalid_argument("End of input.");
61 auto read_digits = [end, &read_digit](sriter& c, std::size_t n) {
63 for (auto i = 0U; i < n; ++i) {
64 auto d = read_digit(c);
69 auto partial_date = [end, ws_terminates](sriter& c) {
70 return (c == end || (ws_terminates && std::isspace(*c)));
72 auto time_end = [end, ws_terminates](sriter& c) {
73 return (c != end && *c == 'Z' &&
75 (ws_terminates && std::isspace(*(c + 1)))));
77 auto consume_delimiter = [end](sriter& c, char q) {
78 if (c == end || *c != q) {
79 throw std::invalid_argument("Expected delimiter not found.");
98 auto y = read_digits(c, 4);
102 t.tm_year = y - 1900;
104 if (partial_date(c)) {
105 return calculate(t, 0);
108 consume_delimiter(c, '-');
109 t.tm_mon = (read_digits(c, 2) - 1);
110 if (partial_date(c)) {
113 consume_delimiter(c, '-');
114 t.tm_mday = read_digits(c, 2);
115 if (partial_date(c)) {
118 consume_delimiter(c, 'T');
119 t.tm_hour = read_digits(c, 2);
123 consume_delimiter(c, ':');
124 t.tm_min = read_digits(c, 2);
128 consume_delimiter(c, ':');
129 t.tm_sec = read_digits(c, 2);
133 consume_delimiter(c, '.');
136 auto multiplier = 100000000UL;
137 for (auto i = 0U; i < 9U; ++i) {
138 auto d = read_digit(c);
142 return calculate(t, n);
145 } catch (std::invalid_argument& e) {
151 string to_iso_8601(const real_time t,
152 const iso_8601_format f) noexcept {
153 ceph_assert(f >= iso_8601_format::Y &&
154 f <= iso_8601_format::YMDhmsn);
155 stringstream out(std::ios_base::out);
157 auto sec = real_clock::to_time_t(t);
158 auto nsec = duration_cast<nanoseconds>(t.time_since_epoch() %
165 out << 1900 + bt.tm_year;
166 if (f == iso_8601_format::Y) {
170 out << '-' << setw(2) << bt.tm_mon + 1;
171 if (f == iso_8601_format::YM) {
175 out << '-' << setw(2) << bt.tm_mday;
176 if (f == iso_8601_format::YMD) {
180 out << 'T' << setw(2) << bt.tm_hour;
181 if (f == iso_8601_format::YMDh) {
186 out << ':' << setw(2) << bt.tm_min;
187 if (f == iso_8601_format::YMDhm) {
192 out << ':' << setw(2) << bt.tm_sec;
193 if (f == iso_8601_format::YMDhms) {
197 out << '.' << setw(9) << nsec << 'Z';