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