1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
8 #include "include/timegm.h"
9 #include "include/ceph_assert.h"
12 using std::chrono::duration_cast
;
13 using std::chrono::nanoseconds
;
14 using std::chrono::seconds
;
17 using std::stringstream
;
22 using boost::optional
;
23 using std::string_view
;
25 using ceph::real_clock
;
26 using ceph::real_time
;
28 using sriter
= string_view::const_iterator
;
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.");
36 return static_cast<uint16_t>(c
- '0');
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)) {
46 return boost::make_optional
<real_time
>(real_clock::from_time_t(tt
)
51 optional
<real_time
> from_iso_8601(const string_view s
,
52 const bool ws_terminates
) noexcept
{
54 auto read_digit
= [end
](sriter
& c
) mutable {
56 throw std::invalid_argument("End of input.");
63 auto read_digits
= [&read_digit
](sriter
& c
, std::size_t n
) {
65 for (auto i
= 0U; i
< n
; ++i
) {
66 auto d
= read_digit(c
);
71 auto partial_date
= [end
, ws_terminates
](sriter
& c
) {
72 return (c
== end
|| (ws_terminates
&& std::isspace(*c
)));
74 auto time_end
= [end
, ws_terminates
](sriter
& c
) {
75 return (c
!= end
&& *c
== 'Z' &&
77 (ws_terminates
&& std::isspace(*(c
+ 1)))));
79 auto consume_delimiter
= [end
](sriter
& c
, char q
) {
80 if (c
== end
|| *c
!= q
) {
81 throw std::invalid_argument("Expected delimiter not found.");
100 auto y
= read_digits(c
, 4);
104 t
.tm_year
= y
- 1900;
106 if (partial_date(c
)) {
107 return calculate(t
, 0);
110 consume_delimiter(c
, '-');
111 t
.tm_mon
= (read_digits(c
, 2) - 1);
112 if (partial_date(c
)) {
115 consume_delimiter(c
, '-');
116 t
.tm_mday
= read_digits(c
, 2);
117 if (partial_date(c
)) {
120 consume_delimiter(c
, 'T');
121 t
.tm_hour
= read_digits(c
, 2);
125 consume_delimiter(c
, ':');
126 t
.tm_min
= read_digits(c
, 2);
130 consume_delimiter(c
, ':');
131 t
.tm_sec
= read_digits(c
, 2);
135 consume_delimiter(c
, '.');
138 auto multiplier
= 100000000UL;
139 for (auto i
= 0U; i
< 9U; ++i
) {
140 auto d
= read_digit(c
);
144 return calculate(t
, n
);
147 } catch (std::invalid_argument
& e
) {
153 string
to_iso_8601(const real_time t
,
154 const iso_8601_format f
,
155 std::string_view date_separator
,
156 std::string_view time_separator
) noexcept
{
157 ceph_assert(f
>= iso_8601_format::Y
&&
158 f
<= iso_8601_format::YMDhmsn
);
159 stringstream
out(std::ios_base::out
);
161 auto sec
= real_clock::to_time_t(t
);
162 auto nsec
= duration_cast
<nanoseconds
>(t
.time_since_epoch() %
169 out
<< 1900 + bt
.tm_year
;
170 if (f
== iso_8601_format::Y
) {
174 out
<< date_separator
<< setw(2) << bt
.tm_mon
+ 1;
175 if (f
== iso_8601_format::YM
) {
179 out
<< date_separator
<< setw(2) << bt
.tm_mday
;
180 if (f
== iso_8601_format::YMD
) {
184 out
<< 'T' << setw(2) << bt
.tm_hour
;
185 if (f
== iso_8601_format::YMDh
) {
190 out
<< time_separator
<< setw(2) << bt
.tm_min
;
191 if (f
== iso_8601_format::YMDhm
) {
196 out
<< time_separator
<< setw(2) << bt
.tm_sec
;
197 if (f
== iso_8601_format::YMDhms
) {
201 out
<< '.' << setw(9) << nsec
<< 'Z';