time_zone.h 19.2 KB
Newer Older
1
2
3
4
5
6
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
7
//   https://www.apache.org/licenses/LICENSE-2.0
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

// A library for translating between absolute times (represented by
// std::chrono::time_points of the std::chrono::system_clock) and civil
// times (represented by cctz::civil_second) using the rules defined by
// a time zone (cctz::time_zone).

#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_

#include <chrono>
#include <cstdint>
25
#include <limits>
26
27
28
#include <string>
#include <utility>

29
#include "absl/base/config.h"
30
31
32
#include "absl/time/internal/cctz/include/cctz/civil_time.h"

namespace absl {
33
ABSL_NAMESPACE_BEGIN
34
35
36
37
38
39
namespace time_internal {
namespace cctz {

// Convenience aliases. Not intended as public API points.
template <typename D>
using time_point = std::chrono::time_point<std::chrono::system_clock, D>;
40
41
using seconds = std::chrono::duration<std::int_fast64_t>;
using sys_seconds = seconds;  // Deprecated.  Use cctz::seconds instead.
42
43
44

namespace detail {
template <typename D>
45
46
47
std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp);
std::pair<time_point<seconds>, seconds> split_seconds(
    const time_point<seconds>& tp);
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
}  // namespace detail

// cctz::time_zone is an opaque, small, value-type class representing a
// geo-political region within which particular rules are used for mapping
// between absolute and civil times. Time zones are named using the TZ
// identifiers from the IANA Time Zone Database, such as "America/Los_Angeles"
// or "Australia/Sydney". Time zones are created from factory functions such
// as load_time_zone(). Note: strings like "PST" and "EDT" are not valid TZ
// identifiers.
//
// Example:
//   cctz::time_zone utc = cctz::utc_time_zone();
//   cctz::time_zone pst = cctz::fixed_time_zone(std::chrono::hours(-8));
//   cctz::time_zone loc = cctz::local_time_zone();
//   cctz::time_zone lax;
//   if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
//
// See also:
// - http://www.iana.org/time-zones
67
// - https://en.wikipedia.org/wiki/Zoneinfo
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class time_zone {
 public:
  time_zone() : time_zone(nullptr) {}  // Equivalent to UTC
  time_zone(const time_zone&) = default;
  time_zone& operator=(const time_zone&) = default;

  std::string name() const;

  // An absolute_lookup represents the civil time (cctz::civil_second) within
  // this time_zone at the given absolute time (time_point). There are
  // additionally a few other fields that may be useful when working with
  // older APIs, such as std::tm.
  //
  // Example:
  //   const cctz::time_zone tz = ...
  //   const auto tp = std::chrono::system_clock::now();
  //   const cctz::time_zone::absolute_lookup al = tz.lookup(tp);
  struct absolute_lookup {
    civil_second cs;
    // Note: The following fields exist for backward compatibility with older
    // APIs. Accessing these fields directly is a sign of imprudent logic in
    // the calling code. Modern time-related code should only access this data
    // indirectly by way of cctz::format().
    int offset;        // civil seconds east of UTC
    bool is_dst;       // is offset non-standard?
    const char* abbr;  // time-zone abbreviation (e.g., "PST")
  };
95
  absolute_lookup lookup(const time_point<seconds>& tp) const;
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  template <typename D>
  absolute_lookup lookup(const time_point<D>& tp) const {
    return lookup(detail::split_seconds(tp).first);
  }

  // A civil_lookup represents the absolute time(s) (time_point) that
  // correspond to the given civil time (cctz::civil_second) within this
  // time_zone. Usually the given civil time represents a unique instant
  // in time, in which case the conversion is unambiguous. However,
  // within this time zone, the given civil time may be skipped (e.g.,
  // during a positive UTC offset shift), or repeated (e.g., during a
  // negative UTC offset shift). To account for these possibilities,
  // civil_lookup is richer than just a single time_point.
  //
  // In all cases the civil_lookup::kind enum will indicate the nature
  // of the given civil-time argument, and the pre, trans, and post
  // members will give the absolute time answers using the pre-transition
  // 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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  //
  // Example:
  //   cctz::time_zone lax;
  //   if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
  //
  //   // A unique civil time.
  //   auto jan01 = lax.lookup(cctz::civil_second(2011, 1, 1, 0, 0, 0));
  //   // jan01.kind == cctz::time_zone::civil_lookup::UNIQUE
  //   // jan01.pre    is 2011/01/01 00:00:00 -0800
  //   // jan01.trans  is 2011/01/01 00:00:00 -0800
  //   // jan01.post   is 2011/01/01 00:00:00 -0800
  //
  //   // A Spring DST transition, when there is a gap in civil time.
  //   auto mar13 = lax.lookup(cctz::civil_second(2011, 3, 13, 2, 15, 0));
  //   // mar13.kind == cctz::time_zone::civil_lookup::SKIPPED
  //   // mar13.pre   is 2011/03/13 03:15:00 -0700
  //   // mar13.trans is 2011/03/13 03:00:00 -0700
  //   // mar13.post  is 2011/03/13 01:15:00 -0800
  //
  //   // A Fall DST transition, when civil times are repeated.
  //   auto nov06 = lax.lookup(cctz::civil_second(2011, 11, 6, 1, 15, 0));
  //   // nov06.kind == cctz::time_zone::civil_lookup::REPEATED
  //   // nov06.pre   is 2011/11/06 01:15:00 -0700
  //   // nov06.trans is 2011/11/06 01:00:00 -0800
  //   // nov06.post  is 2011/11/06 01:15:00 -0800
  struct civil_lookup {
    enum civil_kind {
      UNIQUE,    // the civil time was singular (pre == trans == post)
      SKIPPED,   // the civil time did not exist (pre >= trans > post)
      REPEATED,  // the civil time was ambiguous (pre < trans <= post)
    } kind;
148
149
150
    time_point<seconds> pre;    // uses the pre-transition offset
    time_point<seconds> trans;  // instant of civil-offset change
    time_point<seconds> post;   // uses the post-transition offset
151
152
153
  };
  civil_lookup lookup(const civil_second& cs) const;

154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  // Finds the time of the next/previous offset change in this time zone.
  //
  // By definition, next_transition(tp, &trans) returns false when tp has
  // its maximum value, and prev_transition(tp, &trans) returns false
  // when tp has its minimum value. If the zone has no transitions, the
  // result will also be false no matter what the argument.
  //
  // Otherwise, when tp has its minimum value, next_transition(tp, &trans)
  // returns true and sets trans to the first recorded transition. Chains
  // of calls to next_transition()/prev_transition() will eventually return
  // false, but it is unspecified exactly when next_transition(tp, &trans)
  // jumps to false, or what time is set by prev_transition(tp, &trans) for
  // a very distant tp.
  //
  // Note: Enumeration of time-zone transitions is for informational purposes
  // only. Modern time-related code should not care about when offset changes
  // occur.
  //
  // Example:
  //   cctz::time_zone nyc;
  //   if (!cctz::load_time_zone("America/New_York", &nyc)) { ... }
  //   const auto now = std::chrono::system_clock::now();
  //   auto tp = cctz::time_point<cctz::seconds>::min();
  //   cctz::time_zone::civil_transition trans;
  //   while (tp <= now && nyc.next_transition(tp, &trans)) {
  //     // transition: trans.from -> trans.to
  //     tp = nyc.lookup(trans.to).trans;
  //   }
  struct civil_transition {
    civil_second from;  // the civil time we jump from
    civil_second to;    // the civil time we jump to
  };
  bool next_transition(const time_point<seconds>& tp,
                       civil_transition* trans) const;
  template <typename D>
189
  bool next_transition(const time_point<D>& tp, civil_transition* trans) const {
190
191
192
193
194
    return next_transition(detail::split_seconds(tp).first, trans);
  }
  bool prev_transition(const time_point<seconds>& tp,
                       civil_transition* trans) const;
  template <typename D>
195
  bool prev_transition(const time_point<D>& tp, civil_transition* trans) const {
196
197
198
199
200
201
    return prev_transition(detail::split_seconds(tp).first, trans);
  }

  // version() and description() provide additional information about the
  // time zone. The content of each of the returned strings is unspecified,
  // 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
204
205
206
207
208
209
210
211
212
  // empty when unavailable.
  //
  // Note: These functions are for informational or testing purposes only.
  std::string version() const;  // empty when unknown
  std::string description() const;

  // Relational operators.
  friend bool operator==(time_zone lhs, time_zone rhs) {
    return &lhs.effective_impl() == &rhs.effective_impl();
  }
213
  friend bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); }
214

215
216
217
218
219
  template <typename H>
  friend H AbslHashValue(H h, time_zone tz) {
    return H::combine(std::move(h), &tz.effective_impl());
  }

220
221
222
223
  class Impl;

 private:
  explicit time_zone(const Impl* impl) : impl_(impl) {}
224
  const Impl& effective_impl() const;  // handles implicit UTC
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  const Impl* impl_;
};

// Loads the named time zone. May perform I/O on the initial load.
// If the name is invalid, or some other kind of error occurs, returns
// false and "*tz" is set to the UTC time zone.
bool load_time_zone(const std::string& name, time_zone* tz);

// Returns a time_zone representing UTC. Cannot fail.
time_zone utc_time_zone();

// Returns a time zone that is a fixed offset (seconds east) from UTC.
// Note: If the absolute value of the offset is greater than 24 hours
// 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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
time_zone local_time_zone();

// Returns the civil time (cctz::civil_second) within the given time zone at
// the given absolute time (time_point). Since the additional fields provided
// by the time_zone::absolute_lookup struct should rarely be needed in modern
// code, this convert() function is simpler and should be preferred.
template <typename D>
inline civil_second convert(const time_point<D>& tp, const time_zone& tz) {
  return tz.lookup(tp).cs;
}

// Returns the absolute time (time_point) that corresponds to the given civil
// time within the given time zone. If the civil time is not unique (i.e., if
// it was either repeated or non-existent), then the returned time_point is
// the best estimate that preserves relative order. That is, this function
// guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz).
259
260
inline time_point<seconds> convert(const civil_second& cs,
                                   const time_zone& tz) {
261
262
263
264
265
266
267
  const time_zone::civil_lookup cl = tz.lookup(cs);
  if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans;
  return cl.pre;
}

namespace detail {
using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>;
268
std::string format(const std::string&, const time_point<seconds>&,
269
270
                   const femtoseconds&, const time_zone&);
bool parse(const std::string&, const std::string&, const time_zone&,
271
           time_point<seconds>*, femtoseconds*, std::string* err = nullptr);
272
273
274
275
276
277
278
279
280
281
282
283
284
285
template <typename Rep, std::intmax_t Denom>
bool join_seconds(
    const time_point<seconds>& sec, const femtoseconds& fs,
    time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp);
template <typename Rep, std::intmax_t Num>
bool join_seconds(
    const time_point<seconds>& sec, const femtoseconds& fs,
    time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp);
template <typename Rep>
bool join_seconds(
    const time_point<seconds>& sec, const femtoseconds& fs,
    time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp);
bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
                  time_point<seconds>* tpp);
286
287
288
}  // namespace detail

// Formats the given time_point in the given cctz::time_zone according to
289
// the provided format string. Uses strftime()-like formatting options,
290
291
292
293
294
295
296
297
298
// with the following extensions:
//
//   - %Ez  - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
//   - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
//   - %E#S - Seconds with # digits of fractional precision
//   - %E*S - Seconds with full fractional precision (a literal '*')
//   - %E#f - Fractional seconds with # digits of precision
//   - %E*f - Fractional seconds with full precision (a literal '*')
//   - %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
303
304
305
306
307
308
// contrast %E*f always produces at least one digit, which may be '0'.
//
// Note that %Y produces as many characters as it takes to fully render the
// year. A year outside of [-999:9999] when formatted with %E4Y will produce
// more than four characters, just like %Y.
//
// 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
312
313
314
315
316
317
318
319
320
321
322
323
324
//
// Example:
//   cctz::time_zone lax;
//   if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
//   auto tp = cctz::convert(cctz::civil_second(2013, 1, 2, 3, 4, 5), lax);
//   std::string f = cctz::format("%H:%M:%S", tp, lax);  // "03:04:05"
//   f = cctz::format("%H:%M:%E3S", tp, lax);            // "03:04:05.000"
template <typename D>
inline std::string format(const std::string& fmt, const time_point<D>& tp,
                          const time_zone& tz) {
  const auto p = detail::split_seconds(tp);
  const auto n = std::chrono::duration_cast<detail::femtoseconds>(p.second);
  return detail::format(fmt, p.first, n, tz);
}

325
// Parses an input string according to the provided format string and
326
327
// returns the corresponding time_point. Uses strftime()-like formatting
// 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
330
// and %E*z also accept the same inputs, which (along with %z) includes
// 'z' and 'Z' as synonyms for +00:00.  %ET accepts either 'T' or 't'.
331
332
333
334
335
336
337
338
339
//
// %Y consumes as many numeric characters as it can, so the matching data
// should always be terminated with a non-numeric. %E4Y always consumes
// exactly four characters, including any sign.
//
// Unspecified fields are taken from the default date and time of ...
//
//   "1970-01-01 00:00:00.0 +0000"
//
340
// For example, parsing a string of "15:45" (%H:%M) will return a time_point
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
// that represents "1970-01-01 15:45:00.0 +0000".
//
// Note that parse() returns time instants, so it makes most sense to parse
// fully-specified date/time strings that include a UTC offset (%z, %Ez, or
// %E*z).
//
// Note also that parse() only heeds the fields year, month, day, hour,
// minute, (fractional) second, and UTC offset. Other fields, like weekday (%a
// or %A), while parsed for syntactic validity, are ignored in the conversion.
//
// Date and time fields that are out-of-range will be treated as errors rather
// than normalizing them like cctz::civil_second() would do. For example, it
// is an error to parse the date "Oct 32, 2013" because 32 is out of range.
//
// A second of ":60" is normalized to ":00" of the following minute with
// fractional seconds discarded. The following table shows how the given
// seconds and subseconds will be parsed:
//
//   "59.x" -> 59.x  // exact
//   "60.x" -> 00.0  // normalized
//   "00.x" -> 00.x  // exact
//
// Errors are indicated by returning false.
//
// Example:
//   const cctz::time_zone tz = ...
//   std::chrono::system_clock::time_point tp;
//   if (cctz::parse("%Y-%m-%d", "2015-10-09", tz, &tp)) {
//     ...
//   }
template <typename D>
inline bool parse(const std::string& fmt, const std::string& input,
                  const time_zone& tz, time_point<D>* tpp) {
374
  time_point<seconds> sec;
375
  detail::femtoseconds fs;
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
  return detail::parse(fmt, input, tz, &sec, &fs) &&
         detail::join_seconds(sec, fs, tpp);
}

namespace detail {

// Split a time_point<D> into a time_point<seconds> and a D subseconds.
// Undefined behavior if time_point<seconds> is not of sufficient range.
// Note that this means it is UB to call cctz::time_zone::lookup(tp) or
// cctz::format(fmt, tp, tz) with a time_point that is outside the range
// of a 64-bit std::time_t.
template <typename D>
std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp) {
  auto sec = std::chrono::time_point_cast<seconds>(tp);
  auto sub = tp - sec;
  if (sub.count() < 0) {
    sec -= seconds(1);
    sub += seconds(1);
394
  }
395
396
397
398
399
400
  return {sec, std::chrono::duration_cast<D>(sub)};
}

inline std::pair<time_point<seconds>, seconds> split_seconds(
    const time_point<seconds>& tp) {
  return {tp, seconds::zero()};
401
402
}

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
// Join a time_point<seconds> and femto subseconds into a time_point<D>.
// Floors to the resolution of time_point<D>. Returns false if time_point<D>
// is not of sufficient range.
template <typename Rep, std::intmax_t Denom>
bool join_seconds(
    const time_point<seconds>& sec, const femtoseconds& fs,
    time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp) {
  using D = std::chrono::duration<Rep, std::ratio<1, Denom>>;
  // TODO(#199): Return false if result unrepresentable as a time_point<D>.
  *tpp = std::chrono::time_point_cast<D>(sec);
  *tpp += std::chrono::duration_cast<D>(fs);
  return true;
}

template <typename Rep, std::intmax_t Num>
bool join_seconds(
    const time_point<seconds>& sec, const femtoseconds&,
    time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp) {
  using D = std::chrono::duration<Rep, std::ratio<Num, 1>>;
  auto count = sec.time_since_epoch().count();
  if (count >= 0 || count % Num == 0) {
    count /= Num;
  } else {
    count /= Num;
    count -= 1;
  }
  if (count > (std::numeric_limits<Rep>::max)()) return false;
  if (count < (std::numeric_limits<Rep>::min)()) return false;
  *tpp = time_point<D>() + D{static_cast<Rep>(count)};
  return true;
}

template <typename Rep>
bool join_seconds(
    const time_point<seconds>& sec, const femtoseconds&,
    time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp) {
  using D = std::chrono::duration<Rep, std::ratio<1, 1>>;
  auto count = sec.time_since_epoch().count();
  if (count > (std::numeric_limits<Rep>::max)()) return false;
  if (count < (std::numeric_limits<Rep>::min)()) return false;
  *tpp = time_point<D>() + D{static_cast<Rep>(count)};
  return true;
}

inline bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
                         time_point<seconds>* tpp) {
  *tpp = sec;
  return true;
}

}  // namespace detail
454
455
}  // namespace cctz
}  // namespace time_internal
456
ABSL_NAMESPACE_END
457
458
459
}  // namespace absl

#endif  // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_