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)) {
(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]
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)) {
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