]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/iso_8601.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / common / iso_8601.cc
CommitLineData
31f18b77
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
11fdf7f2
TL
4#include <iomanip>
5#include <sstream>
6
31f18b77
FG
7#include "iso_8601.h"
8#include "include/timegm.h"
f67539c2 9#include "include/ceph_assert.h"
31f18b77 10
31f18b77
FG
11namespace ceph {
12using std::chrono::duration_cast;
13using std::chrono::nanoseconds;
14using std::chrono::seconds;
15using std::setw;
16using std::size_t;
17using std::stringstream;
18using std::string;
19using std::uint16_t;
20
21using boost::none;
22using boost::optional;
f67539c2 23using std::string_view;
31f18b77
FG
24
25using ceph::real_clock;
26using ceph::real_time;
27
f67539c2 28using sriter = string_view::const_iterator;
31f18b77
FG
29
30namespace {
31// This assumes a contiguous block of numbers in the correct order.
32uint16_t digit(char c) {
33 if (!(c >= '0' && c <= '9')) {
34 throw std::invalid_argument("Not a digit.");
35 }
36 return static_cast<uint16_t>(c - '0');
37}
38
39optional<real_time> calculate(const tm& t, uint32_t n = 0) {
40 ceph_assert(n < 1000000000);
41 time_t tt = internal_timegm(&t);
42 if (tt == static_cast<time_t>(-1)) {
43 return none;
44 }
45
46 return boost::make_optional<real_time>(real_clock::from_time_t(tt)
47 + nanoseconds(n));
48}
49}
50
f67539c2 51optional<real_time> from_iso_8601(const string_view s,
31f18b77
FG
52 const bool ws_terminates) noexcept {
53 auto end = s.cend();
54 auto read_digit = [end](sriter& c) mutable {
55 if (c == end) {
56 throw std::invalid_argument("End of input.");
57 }
58 auto f = digit(*c);
59 ++c;
60 return f;
61 };
62
11fdf7f2 63 auto read_digits = [&read_digit](sriter& c, std::size_t n) {
31f18b77
FG
64 auto v = 0ULL;
65 for (auto i = 0U; i < n; ++i) {
66 auto d = read_digit(c);
67 v = (10ULL * v) + d;
68 }
69 return v;
70 };
71 auto partial_date = [end, ws_terminates](sriter& c) {
72 return (c == end || (ws_terminates && std::isspace(*c)));
73 };
74 auto time_end = [end, ws_terminates](sriter& c) {
75 return (c != end && *c == 'Z' &&
76 ((c + 1) == end ||
77 (ws_terminates && std::isspace(*(c + 1)))));
78 };
79 auto consume_delimiter = [end](sriter& c, char q) {
80 if (c == end || *c != q) {
81 throw std::invalid_argument("Expected delimiter not found.");
82 } else {
83 ++c;
84 }
85 };
86
87 tm t = { 0, // tm_sec
88 0, // tm_min
89 0, // tm_hour
90 1, // tm_mday
91 0, // tm_mon
92 70, // tm_year
93 0, // tm_wday
94 0, // tm_yday
95 0, // tm_isdst
96 };
97 try {
98 auto c = s.cbegin();
99 {
100 auto y = read_digits(c, 4);
101 if (y < 1970) {
102 return none;
103 }
104 t.tm_year = y - 1900;
105 }
106 if (partial_date(c)) {
107 return calculate(t, 0);
108 }
109
110 consume_delimiter(c, '-');
111 t.tm_mon = (read_digits(c, 2) - 1);
112 if (partial_date(c)) {
113 return calculate(t);
114 }
115 consume_delimiter(c, '-');
116 t.tm_mday = read_digits(c, 2);
117 if (partial_date(c)) {
118 return calculate(t);
119 }
120 consume_delimiter(c, 'T');
121 t.tm_hour = read_digits(c, 2);
122 if (time_end(c)) {
123 return calculate(t);
124 }
125 consume_delimiter(c, ':');
126 t.tm_min = read_digits(c, 2);
127 if (time_end(c)) {
128 return calculate(t);
129 }
130 consume_delimiter(c, ':');
131 t.tm_sec = read_digits(c, 2);
132 if (time_end(c)) {
133 return calculate(t);
134 }
135 consume_delimiter(c, '.');
136
137 auto n = 0UL;
138 auto multiplier = 100000000UL;
139 for (auto i = 0U; i < 9U; ++i) {
140 auto d = read_digit(c);
141 n += d * multiplier;
142 multiplier /= 10;
143 if (time_end(c)) {
144 return calculate(t, n);
145 }
146 }
147 } catch (std::invalid_argument& e) {
148 // fallthrough
149 }
150 return none;
151}
152
153string to_iso_8601(const real_time t,
20effc67
TL
154 const iso_8601_format f,
155 std::string_view date_separator,
156 std::string_view time_separator) noexcept {
31f18b77
FG
157 ceph_assert(f >= iso_8601_format::Y &&
158 f <= iso_8601_format::YMDhmsn);
159 stringstream out(std::ios_base::out);
160
161 auto sec = real_clock::to_time_t(t);
162 auto nsec = duration_cast<nanoseconds>(t.time_since_epoch() %
163 seconds(1)).count();
164
165 struct tm bt;
166 gmtime_r(&sec, &bt);
167 out.fill('0');
168
169 out << 1900 + bt.tm_year;
170 if (f == iso_8601_format::Y) {
171 return out.str();
172 }
173
20effc67 174 out << date_separator << setw(2) << bt.tm_mon + 1;
31f18b77
FG
175 if (f == iso_8601_format::YM) {
176 return out.str();
177 }
178
20effc67 179 out << date_separator << setw(2) << bt.tm_mday;
31f18b77
FG
180 if (f == iso_8601_format::YMD) {
181 return out.str();
182 }
183
184 out << 'T' << setw(2) << bt.tm_hour;
185 if (f == iso_8601_format::YMDh) {
186 out << 'Z';
187 return out.str();
188 }
189
20effc67 190 out << time_separator << setw(2) << bt.tm_min;
31f18b77
FG
191 if (f == iso_8601_format::YMDhm) {
192 out << 'Z';
193 return out.str();
194 }
195
20effc67 196 out << time_separator << setw(2) << bt.tm_sec;
31f18b77
FG
197 if (f == iso_8601_format::YMDhms) {
198 out << 'Z';
199 return out.str();
200 }
201 out << '.' << setw(9) << nsec << 'Z';
202 return out.str();
203}
20effc67 204
31f18b77 205}