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