Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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 }