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