/src/znc/third_party/cctz/include/cctz/time_zone.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright 2016 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | // A library for translating between absolute times (represented by |
16 | | // std::chrono::time_points of the std::chrono::system_clock) and civil |
17 | | // times (represented by cctz::civil_second) using the rules defined by |
18 | | // a time zone (cctz::time_zone). |
19 | | |
20 | | #ifndef CCTZ_TIME_ZONE_H_ |
21 | | #define CCTZ_TIME_ZONE_H_ |
22 | | |
23 | | #include <chrono> |
24 | | #include <cstdint> |
25 | | #include <limits> |
26 | | #include <string> |
27 | | #include <utility> |
28 | | |
29 | | #include "cctz/civil_time.h" |
30 | | |
31 | | namespace cctz { |
32 | | |
33 | | // Convenience aliases. Not intended as public API points. |
34 | | template <typename D> |
35 | | using time_point = std::chrono::time_point<std::chrono::system_clock, D>; |
36 | | using seconds = std::chrono::duration<std::int_fast64_t>; |
37 | | using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead. |
38 | | |
39 | | namespace detail { |
40 | | template <typename D> |
41 | | std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp); |
42 | | std::pair<time_point<seconds>, seconds> split_seconds( |
43 | | const time_point<seconds>& tp); |
44 | | } // namespace detail |
45 | | |
46 | | // cctz::time_zone is an opaque, small, value-type class representing a |
47 | | // geo-political region within which particular rules are used for mapping |
48 | | // between absolute and civil times. Time zones are named using the TZ |
49 | | // identifiers from the IANA Time Zone Database, such as "America/Los_Angeles" |
50 | | // or "Australia/Sydney". Time zones are created from factory functions such |
51 | | // as load_time_zone(). Note: strings like "PST" and "EDT" are not valid TZ |
52 | | // identifiers. |
53 | | // |
54 | | // Example: |
55 | | // cctz::time_zone utc = cctz::utc_time_zone(); |
56 | | // cctz::time_zone pst = cctz::fixed_time_zone(std::chrono::hours(-8)); |
57 | | // cctz::time_zone loc = cctz::local_time_zone(); |
58 | | // cctz::time_zone lax; |
59 | | // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } |
60 | | // |
61 | | // See also: |
62 | | // - http://www.iana.org/time-zones |
63 | | // - https://en.wikipedia.org/wiki/Zoneinfo |
64 | | class time_zone { |
65 | | public: |
66 | 0 | time_zone() : time_zone(nullptr) {} // Equivalent to UTC |
67 | | time_zone(const time_zone&) = default; |
68 | | time_zone& operator=(const time_zone&) = default; |
69 | | |
70 | | std::string name() const; |
71 | | |
72 | | // An absolute_lookup represents the civil time (cctz::civil_second) within |
73 | | // this time_zone at the given absolute time (time_point). There are |
74 | | // additionally a few other fields that may be useful when working with |
75 | | // older APIs, such as std::tm. |
76 | | // |
77 | | // Example: |
78 | | // const cctz::time_zone tz = ... |
79 | | // const auto tp = std::chrono::system_clock::now(); |
80 | | // const cctz::time_zone::absolute_lookup al = tz.lookup(tp); |
81 | | struct absolute_lookup { |
82 | | civil_second cs; |
83 | | // Note: The following fields exist for backward compatibility with older |
84 | | // APIs. Accessing these fields directly is a sign of imprudent logic in |
85 | | // the calling code. Modern time-related code should only access this data |
86 | | // indirectly by way of cctz::format(). |
87 | | int offset; // civil seconds east of UTC |
88 | | bool is_dst; // is offset non-standard? |
89 | | const char* abbr; // time-zone abbreviation (e.g., "PST") |
90 | | }; |
91 | | absolute_lookup lookup(const time_point<seconds>& tp) const; |
92 | | template <typename D> |
93 | | absolute_lookup lookup(const time_point<D>& tp) const { |
94 | | return lookup(detail::split_seconds(tp).first); |
95 | | } |
96 | | |
97 | | // A civil_lookup represents the absolute time(s) (time_point) that |
98 | | // correspond to the given civil time (cctz::civil_second) within this |
99 | | // time_zone. Usually the given civil time represents a unique instant |
100 | | // in time, in which case the conversion is unambiguous. However, |
101 | | // within this time zone, the given civil time may be skipped (e.g., |
102 | | // during a positive UTC offset shift), or repeated (e.g., during a |
103 | | // negative UTC offset shift). To account for these possibilities, |
104 | | // civil_lookup is richer than just a single time_point. |
105 | | // |
106 | | // In all cases the civil_lookup::kind enum will indicate the nature |
107 | | // of the given civil-time argument, and the pre, trans, and post |
108 | | // members will give the absolute time answers using the pre-transition |
109 | | // offset, the transition point itself, and the post-transition offset, |
110 | | // respectively (all three times are equal if kind == UNIQUE). If any |
111 | | // of these three absolute times is outside the representable range of a |
112 | | // time_point<seconds> the field is set to its maximum/minimum value. |
113 | | // |
114 | | // Example: |
115 | | // cctz::time_zone lax; |
116 | | // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } |
117 | | // |
118 | | // // A unique civil time. |
119 | | // auto jan01 = lax.lookup(cctz::civil_second(2011, 1, 1, 0, 0, 0)); |
120 | | // // jan01.kind == cctz::time_zone::civil_lookup::UNIQUE |
121 | | // // jan01.pre is 2011/01/01 00:00:00 -0800 |
122 | | // // jan01.trans is 2011/01/01 00:00:00 -0800 |
123 | | // // jan01.post is 2011/01/01 00:00:00 -0800 |
124 | | // |
125 | | // // A Spring DST transition, when there is a gap in civil time. |
126 | | // auto mar13 = lax.lookup(cctz::civil_second(2011, 3, 13, 2, 15, 0)); |
127 | | // // mar13.kind == cctz::time_zone::civil_lookup::SKIPPED |
128 | | // // mar13.pre is 2011/03/13 03:15:00 -0700 |
129 | | // // mar13.trans is 2011/03/13 03:00:00 -0700 |
130 | | // // mar13.post is 2011/03/13 01:15:00 -0800 |
131 | | // |
132 | | // // A Fall DST transition, when civil times are repeated. |
133 | | // auto nov06 = lax.lookup(cctz::civil_second(2011, 11, 6, 1, 15, 0)); |
134 | | // // nov06.kind == cctz::time_zone::civil_lookup::REPEATED |
135 | | // // nov06.pre is 2011/11/06 01:15:00 -0700 |
136 | | // // nov06.trans is 2011/11/06 01:00:00 -0800 |
137 | | // // nov06.post is 2011/11/06 01:15:00 -0800 |
138 | | struct civil_lookup { |
139 | | enum civil_kind { |
140 | | UNIQUE, // the civil time was singular (pre == trans == post) |
141 | | SKIPPED, // the civil time did not exist (pre >= trans > post) |
142 | | REPEATED, // the civil time was ambiguous (pre < trans <= post) |
143 | | } kind; |
144 | | time_point<seconds> pre; // uses the pre-transition offset |
145 | | time_point<seconds> trans; // instant of civil-offset change |
146 | | time_point<seconds> post; // uses the post-transition offset |
147 | | }; |
148 | | civil_lookup lookup(const civil_second& cs) const; |
149 | | |
150 | | // Finds the time of the next/previous offset change in this time zone. |
151 | | // |
152 | | // By definition, next_transition(tp, &trans) returns false when tp has |
153 | | // its maximum value, and prev_transition(tp, &trans) returns false |
154 | | // when tp has its minimum value. If the zone has no transitions, the |
155 | | // result will also be false no matter what the argument. |
156 | | // |
157 | | // Otherwise, when tp has its minimum value, next_transition(tp, &trans) |
158 | | // returns true and sets trans to the first recorded transition. Chains |
159 | | // of calls to next_transition()/prev_transition() will eventually return |
160 | | // false, but it is unspecified exactly when next_transition(tp, &trans) |
161 | | // jumps to false, or what time is set by prev_transition(tp, &trans) for |
162 | | // a very distant tp. |
163 | | // |
164 | | // Note: Enumeration of time-zone transitions is for informational purposes |
165 | | // only. Modern time-related code should not care about when offset changes |
166 | | // occur. |
167 | | // |
168 | | // Example: |
169 | | // cctz::time_zone nyc; |
170 | | // if (!cctz::load_time_zone("America/New_York", &nyc)) { ... } |
171 | | // const auto now = std::chrono::system_clock::now(); |
172 | | // auto tp = cctz::time_point<cctz::seconds>::min(); |
173 | | // cctz::time_zone::civil_transition trans; |
174 | | // while (tp <= now && nyc.next_transition(tp, &trans)) { |
175 | | // // transition: trans.from -> trans.to |
176 | | // tp = nyc.lookup(trans.to).trans; |
177 | | // } |
178 | | struct civil_transition { |
179 | | civil_second from; // the civil time we jump from |
180 | | civil_second to; // the civil time we jump to |
181 | | }; |
182 | | bool next_transition(const time_point<seconds>& tp, |
183 | | civil_transition* trans) const; |
184 | | template <typename D> |
185 | | bool next_transition(const time_point<D>& tp, |
186 | | civil_transition* trans) const { |
187 | | return next_transition(detail::split_seconds(tp).first, trans); |
188 | | } |
189 | | bool prev_transition(const time_point<seconds>& tp, |
190 | | civil_transition* trans) const; |
191 | | template <typename D> |
192 | | bool prev_transition(const time_point<D>& tp, |
193 | | civil_transition* trans) const { |
194 | | return prev_transition(detail::split_seconds(tp).first, trans); |
195 | | } |
196 | | |
197 | | // version() and description() provide additional information about the |
198 | | // time zone. The content of each of the returned strings is unspecified, |
199 | | // however, when the IANA Time Zone Database is the underlying data source |
200 | | // the version() string will be in the familar form (e.g, "2018e") or |
201 | | // empty when unavailable. |
202 | | // |
203 | | // Note: These functions are for informational or testing purposes only. |
204 | | std::string version() const; // empty when unknown |
205 | | std::string description() const; |
206 | | |
207 | | // Relational operators. |
208 | 0 | friend bool operator==(time_zone lhs, time_zone rhs) { |
209 | 0 | return &lhs.effective_impl() == &rhs.effective_impl(); |
210 | 0 | } |
211 | 0 | friend bool operator!=(time_zone lhs, time_zone rhs) { |
212 | 0 | return !(lhs == rhs); |
213 | 0 | } |
214 | | |
215 | | class Impl; |
216 | | |
217 | | private: |
218 | 0 | explicit time_zone(const Impl* impl) : impl_(impl) {} |
219 | | const Impl& effective_impl() const; // handles implicit UTC |
220 | | const Impl* impl_; |
221 | | }; |
222 | | |
223 | | // Loads the named time zone. May perform I/O on the initial load. |
224 | | // If the name is invalid, or some other kind of error occurs, returns |
225 | | // false and "*tz" is set to the UTC time zone. |
226 | | bool load_time_zone(const std::string& name, time_zone* tz); |
227 | | |
228 | | // Returns a time_zone representing UTC. Cannot fail. |
229 | | time_zone utc_time_zone(); |
230 | | |
231 | | // Returns a time zone that is a fixed offset (seconds east) from UTC. |
232 | | // Note: If the absolute value of the offset is greater than 24 hours |
233 | | // you'll get UTC (i.e., zero offset) instead. |
234 | | time_zone fixed_time_zone(const seconds& offset); |
235 | | |
236 | | // Returns a time zone representing the local time zone. Falls back to UTC. |
237 | | // Note: local_time_zone.name() may only be something like "localtime". |
238 | | time_zone local_time_zone(); |
239 | | |
240 | | // Returns the civil time (cctz::civil_second) within the given time zone at |
241 | | // the given absolute time (time_point). Since the additional fields provided |
242 | | // by the time_zone::absolute_lookup struct should rarely be needed in modern |
243 | | // code, this convert() function is simpler and should be preferred. |
244 | | template <typename D> |
245 | | inline civil_second convert(const time_point<D>& tp, const time_zone& tz) { |
246 | | return tz.lookup(tp).cs; |
247 | | } |
248 | | |
249 | | // Returns the absolute time (time_point) that corresponds to the given civil |
250 | | // time within the given time zone. If the civil time is not unique (i.e., if |
251 | | // it was either repeated or non-existent), then the returned time_point is |
252 | | // the best estimate that preserves relative order. That is, this function |
253 | | // guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz). |
254 | | inline time_point<seconds> convert(const civil_second& cs, |
255 | 0 | const time_zone& tz) { |
256 | 0 | const time_zone::civil_lookup cl = tz.lookup(cs); |
257 | 0 | if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans; |
258 | 0 | return cl.pre; |
259 | 0 | } |
260 | | |
261 | | namespace detail { |
262 | | using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>; |
263 | | std::string format(const std::string&, const time_point<seconds>&, |
264 | | const femtoseconds&, const time_zone&); |
265 | | bool parse(const std::string&, const std::string&, const time_zone&, |
266 | | time_point<seconds>*, femtoseconds*, std::string* err = nullptr); |
267 | | template <typename Rep, std::intmax_t Denom> |
268 | | bool join_seconds( |
269 | | const time_point<seconds>& sec, const femtoseconds& fs, |
270 | | time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp); |
271 | | template <typename Rep, std::intmax_t Num> |
272 | | bool join_seconds( |
273 | | const time_point<seconds>& sec, const femtoseconds& fs, |
274 | | time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp); |
275 | | template <typename Rep> |
276 | | bool join_seconds( |
277 | | const time_point<seconds>& sec, const femtoseconds& fs, |
278 | | time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp); |
279 | | bool join_seconds(const time_point<seconds>& sec, const femtoseconds&, |
280 | | time_point<seconds>* tpp); |
281 | | } // namespace detail |
282 | | |
283 | | // Formats the given time_point in the given cctz::time_zone according to |
284 | | // the provided format string. Uses strftime()-like formatting options, |
285 | | // with the following extensions: |
286 | | // |
287 | | // - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm) |
288 | | // - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss) |
289 | | // - %E#S - Seconds with # digits of fractional precision |
290 | | // - %E*S - Seconds with full fractional precision (a literal '*') |
291 | | // - %E#f - Fractional seconds with # digits of precision |
292 | | // - %E*f - Fractional seconds with full precision (a literal '*') |
293 | | // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) |
294 | | // - %ET - The RFC3339 "date-time" separator "T" |
295 | | // |
296 | | // Note that %E0S behaves like %S, and %E0f produces no characters. In |
297 | | // contrast %E*f always produces at least one digit, which may be '0'. |
298 | | // |
299 | | // Note that %Y produces as many characters as it takes to fully render the |
300 | | // year. A year outside of [-999:9999] when formatted with %E4Y will produce |
301 | | // more than four characters, just like %Y. |
302 | | // |
303 | | // Tip: Format strings should include the UTC offset (e.g., %z, %Ez, or %E*z) |
304 | | // so that the resulting string uniquely identifies an absolute time. |
305 | | // |
306 | | // Example: |
307 | | // cctz::time_zone lax; |
308 | | // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } |
309 | | // auto tp = cctz::convert(cctz::civil_second(2013, 1, 2, 3, 4, 5), lax); |
310 | | // std::string f = cctz::format("%H:%M:%S", tp, lax); // "03:04:05" |
311 | | // f = cctz::format("%H:%M:%E3S", tp, lax); // "03:04:05.000" |
312 | | template <typename D> |
313 | | inline std::string format(const std::string& fmt, const time_point<D>& tp, |
314 | 0 | const time_zone& tz) { |
315 | 0 | const auto p = detail::split_seconds(tp); |
316 | 0 | const auto n = std::chrono::duration_cast<detail::femtoseconds>(p.second); |
317 | 0 | return detail::format(fmt, p.first, n, tz); |
318 | 0 | } |
319 | | |
320 | | // Parses an input string according to the provided format string and |
321 | | // returns the corresponding time_point. Uses strftime()-like formatting |
322 | | // options, with the same extensions as cctz::format(), but with the |
323 | | // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez |
324 | | // and %E*z also accept the same inputs, which (along with %z) includes |
325 | | // 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'. |
326 | | // |
327 | | // %Y consumes as many numeric characters as it can, so the matching data |
328 | | // should always be terminated with a non-numeric. %E4Y always consumes |
329 | | // exactly four characters, including any sign. |
330 | | // |
331 | | // Unspecified fields are taken from the default date and time of ... |
332 | | // |
333 | | // "1970-01-01 00:00:00.0 +0000" |
334 | | // |
335 | | // For example, parsing a string of "15:45" (%H:%M) will return a time_point |
336 | | // that represents "1970-01-01 15:45:00.0 +0000". |
337 | | // |
338 | | // Note that parse() returns time instants, so it makes most sense to parse |
339 | | // fully-specified date/time strings that include a UTC offset (%z, %Ez, or |
340 | | // %E*z). |
341 | | // |
342 | | // Note also that parse() only heeds the fields year, month, day, hour, |
343 | | // minute, (fractional) second, and UTC offset. Other fields, like weekday (%a |
344 | | // or %A), while parsed for syntactic validity, are ignored in the conversion. |
345 | | // |
346 | | // Date and time fields that are out-of-range will be treated as errors rather |
347 | | // than normalizing them like cctz::civil_second() would do. For example, it |
348 | | // is an error to parse the date "Oct 32, 2013" because 32 is out of range. |
349 | | // |
350 | | // A second of ":60" is normalized to ":00" of the following minute with |
351 | | // fractional seconds discarded. The following table shows how the given |
352 | | // seconds and subseconds will be parsed: |
353 | | // |
354 | | // "59.x" -> 59.x // exact |
355 | | // "60.x" -> 00.0 // normalized |
356 | | // "00.x" -> 00.x // exact |
357 | | // |
358 | | // Errors are indicated by returning false. |
359 | | // |
360 | | // Example: |
361 | | // const cctz::time_zone tz = ... |
362 | | // std::chrono::system_clock::time_point tp; |
363 | | // if (cctz::parse("%Y-%m-%d", "2015-10-09", tz, &tp)) { |
364 | | // ... |
365 | | // } |
366 | | template <typename D> |
367 | | inline bool parse(const std::string& fmt, const std::string& input, |
368 | 0 | const time_zone& tz, time_point<D>* tpp) { |
369 | 0 | time_point<seconds> sec; |
370 | 0 | detail::femtoseconds fs; |
371 | 0 | return detail::parse(fmt, input, tz, &sec, &fs) && |
372 | 0 | detail::join_seconds(sec, fs, tpp); |
373 | 0 | } |
374 | | |
375 | | namespace detail { |
376 | | |
377 | | // Split a time_point<D> into a time_point<seconds> and a D subseconds. |
378 | | // Undefined behavior if time_point<seconds> is not of sufficient range. |
379 | | // Note that this means it is UB to call cctz::time_zone::lookup(tp) or |
380 | | // cctz::format(fmt, tp, tz) with a time_point that is outside the range |
381 | | // of a 64-bit std::time_t. |
382 | | template <typename D> |
383 | 0 | std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp) { |
384 | 0 | auto sec = std::chrono::time_point_cast<seconds>(tp); |
385 | 0 | auto sub = tp - sec; |
386 | 0 | if (sub.count() < 0) { |
387 | 0 | sec -= seconds(1); |
388 | 0 | sub += seconds(1); |
389 | 0 | } |
390 | 0 | return {sec, std::chrono::duration_cast<D>(sub)}; |
391 | 0 | } |
392 | | |
393 | | inline std::pair<time_point<seconds>, seconds> split_seconds( |
394 | 0 | const time_point<seconds>& tp) { |
395 | 0 | return {tp, seconds::zero()}; |
396 | 0 | } |
397 | | |
398 | | // Join a time_point<seconds> and femto subseconds into a time_point<D>. |
399 | | // Floors to the resolution of time_point<D>. Returns false if time_point<D> |
400 | | // is not of sufficient range. |
401 | | template <typename Rep, std::intmax_t Denom> |
402 | | bool join_seconds( |
403 | | const time_point<seconds>& sec, const femtoseconds& fs, |
404 | 0 | time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp) { |
405 | 0 | using D = std::chrono::duration<Rep, std::ratio<1, Denom>>; |
406 | | // TODO(#199): Return false if result unrepresentable as a time_point<D>. |
407 | 0 | *tpp = std::chrono::time_point_cast<D>(sec); |
408 | 0 | *tpp += std::chrono::duration_cast<D>(fs); |
409 | 0 | return true; |
410 | 0 | } |
411 | | |
412 | | template <typename Rep, std::intmax_t Num> |
413 | | bool join_seconds( |
414 | | const time_point<seconds>& sec, const femtoseconds&, |
415 | | time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp) { |
416 | | using D = std::chrono::duration<Rep, std::ratio<Num, 1>>; |
417 | | auto count = sec.time_since_epoch().count(); |
418 | | if (count >= 0 || count % Num == 0) { |
419 | | count /= Num; |
420 | | } else { |
421 | | count /= Num; |
422 | | count -= 1; |
423 | | } |
424 | | if (count > (std::numeric_limits<Rep>::max)()) return false; |
425 | | if (count < (std::numeric_limits<Rep>::min)()) return false; |
426 | | *tpp = time_point<D>() + D{static_cast<Rep>(count)}; |
427 | | return true; |
428 | | } |
429 | | |
430 | | template <typename Rep> |
431 | | bool join_seconds( |
432 | | const time_point<seconds>& sec, const femtoseconds&, |
433 | | time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp) { |
434 | | using D = std::chrono::duration<Rep, std::ratio<1, 1>>; |
435 | | auto count = sec.time_since_epoch().count(); |
436 | | if (count > (std::numeric_limits<Rep>::max)()) return false; |
437 | | if (count < (std::numeric_limits<Rep>::min)()) return false; |
438 | | *tpp = time_point<D>() + D{static_cast<Rep>(count)}; |
439 | | return true; |
440 | | } |
441 | | |
442 | | inline bool join_seconds(const time_point<seconds>& sec, const femtoseconds&, |
443 | 0 | time_point<seconds>* tpp) { |
444 | 0 | *tpp = sec; |
445 | 0 | return true; |
446 | 0 | } |
447 | | |
448 | | } // namespace detail |
449 | | } // namespace cctz |
450 | | |
451 | | #endif // CCTZ_TIME_ZONE_H_ |