]>
Commit | Line | Data |
---|---|---|
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 |
11 | namespace ceph { |
12 | using std::chrono::duration_cast; | |
13 | using std::chrono::nanoseconds; | |
14 | using std::chrono::seconds; | |
15 | using std::setw; | |
16 | using std::size_t; | |
17 | using std::stringstream; | |
18 | using std::string; | |
19 | using std::uint16_t; | |
20 | ||
21 | using boost::none; | |
22 | using boost::optional; | |
f67539c2 | 23 | using std::string_view; |
31f18b77 FG |
24 | |
25 | using ceph::real_clock; | |
26 | using ceph::real_time; | |
27 | ||
f67539c2 | 28 | using sriter = string_view::const_iterator; |
31f18b77 FG |
29 | |
30 | namespace { | |
31 | // This assumes a contiguous block of numbers in the correct order. | |
32 | uint16_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 | ||
39 | optional<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 | 51 | optional<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 | ||
153 | string 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 | } |