| 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 | // http://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 <string> |
| 26 | #include <utility> |
| 27 | |
| 28 | #include "cctz/civil_time.h" |
| 29 | |
| 30 | namespace cctz { |
| 31 | |
| 32 | // Convenience aliases. Not intended as public API points. |
| 33 | template <typename D> |
| 34 | using time_point = std::chrono::time_point<std::chrono::system_clock, D>; |
| 35 | using sys_seconds = std::chrono::duration<std::int_fast64_t>; |
| 36 | |
| 37 | namespace detail { |
| 38 | template <typename D> |
| 39 | inline std::pair<time_point<sys_seconds>, D> |
| 40 | split_seconds(const time_point<D>& tp) { |
| 41 | auto sec = std::chrono::time_point_cast<sys_seconds>(tp); |
| 42 | auto sub = tp - sec; |
| 43 | if (sub.count() < 0) { |
| 44 | sec -= sys_seconds(1); |
| 45 | sub += sys_seconds(1); |
| 46 | } |
| 47 | return {sec, std::chrono::duration_cast<D>(sub)}; |
| 48 | } |
| 49 | inline std::pair<time_point<sys_seconds>, sys_seconds> |
| 50 | split_seconds(const time_point<sys_seconds>& tp) { |
| 51 | return {tp, sys_seconds(0)}; |
| 52 | } |
| 53 | } // namespace detail |
| 54 | |
| 55 | // cctz::time_zone is an opaque, small, value-type class representing a |
| 56 | // geo-political region within which particular rules are used for mapping |
| 57 | // between absolute and civil times. Time zones are named using the TZ |
| 58 | // identifiers from the IANA Time Zone Database, such as "America/Los_Angeles" |
| 59 | // or "Australia/Sydney". Time zones are created from factory functions such |
| 60 | // as load_time_zone(). Note: strings like "PST" and "EDT" are not valid TZ |
| 61 | // identifiers. |
| 62 | // |
| 63 | // Example: |
| 64 | // cctz::time_zone utc = cctz::utc_time_zone(); |
| 65 | // cctz::time_zone pst = cctz::fixed_time_zone(std::chrono::hours(-8)); |
| 66 | // cctz::time_zone loc = cctz::local_time_zone(); |
| 67 | // cctz::time_zone lax; |
| 68 | // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } |
| 69 | // |
| 70 | // See also: |
| 71 | // - http://www.iana.org/time-zones |
| 72 | // - http://en.wikipedia.org/wiki/Zoneinfo |
| 73 | class time_zone { |
| 74 | public: |
| 75 | time_zone() : time_zone(nullptr) {} // Equivalent to UTC |
| 76 | time_zone(const time_zone&) = default; |
| 77 | time_zone& operator=(const time_zone&) = default; |
| 78 | |
| 79 | std::string name() const; |
| 80 | |
| 81 | // An absolute_lookup represents the civil time (cctz::civil_second) within |
| 82 | // this time_zone at the given absolute time (time_point). There are |
| 83 | // additionally a few other fields that may be useful when working with |
| 84 | // older APIs, such as std::tm. |
| 85 | // |
| 86 | // Example: |
| 87 | // const cctz::time_zone tz = ... |
| 88 | // const auto tp = std::chrono::system_clock::now(); |
| 89 | // const cctz::time_zone::absolute_lookup al = tz.lookup(tp); |
| 90 | struct absolute_lookup { |
| 91 | civil_second cs; |
| 92 | // Note: The following fields exist for backward compatibility with older |
| 93 | // APIs. Accessing these fields directly is a sign of imprudent logic in |
| 94 | // the calling code. Modern time-related code should only access this data |
| 95 | // indirectly by way of cctz::format(). |
| 96 | int offset; // civil seconds east of UTC |
| 97 | bool is_dst; // is offset non-standard? |
| 98 | const char* abbr; // time-zone abbreviation (e.g., "PST") |
| 99 | }; |
| 100 | absolute_lookup lookup(const time_point<sys_seconds>& tp) const; |
| 101 | template <typename D> |
| 102 | absolute_lookup lookup(const time_point<D>& tp) const { |
| 103 | return lookup(detail::split_seconds(tp).first); |
| 104 | } |
| 105 | |
| 106 | // A civil_lookup represents the absolute time(s) (time_point) that |
| 107 | // correspond to the given civil time (cctz::civil_second) within this |
| 108 | // time_zone. Usually the given civil time represents a unique instant |
| 109 | // in time, in which case the conversion is unambiguous. However, |
| 110 | // within this time zone, the given civil time may be skipped (e.g., |
| 111 | // during a positive UTC offset shift), or repeated (e.g., during a |
| 112 | // negative UTC offset shift). To account for these possibilities, |
| 113 | // civil_lookup is richer than just a single time_point. |
| 114 | // |
| 115 | // In all cases the civil_lookup::kind enum will indicate the nature |
| 116 | // of the given civil-time argument, and the pre, trans, and post |
| 117 | // members will give the absolute time answers using the pre-transition |
| 118 | // offset, the transition point itself, and the post-transition offset, |
| 119 | // respectively (all three times are equal if kind == UNIQUE). If any |
| 120 | // of these three absolute times is outside the representable range of a |
| 121 | // time_point<sys_seconds> the field is set to its maximum/minimum value. |
| 122 | // |
| 123 | // Example: |
| 124 | // cctz::time_zone lax; |
| 125 | // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } |
| 126 | // |
| 127 | // // A unique civil time. |
| 128 | // auto jan01 = lax.lookup(cctz::civil_second(2011, 1, 1, 0, 0, 0)); |
| 129 | // // jan01.kind == cctz::time_zone::civil_lookup::UNIQUE |
| 130 | // // jan01.pre is 2011/01/01 00:00:00 -0800 |
| 131 | // // jan01.trans is 2011/01/01 00:00:00 -0800 |
| 132 | // // jan01.post is 2011/01/01 00:00:00 -0800 |
| 133 | // |
| 134 | // // A Spring DST transition, when there is a gap in civil time. |
| 135 | // auto mar13 = lax.lookup(cctz::civil_second(2011, 3, 13, 2, 15, 0)); |
| 136 | // // mar13.kind == cctz::time_zone::civil_lookup::SKIPPED |
| 137 | // // mar13.pre is 2011/03/13 03:15:00 -0700 |
| 138 | // // mar13.trans is 2011/03/13 03:00:00 -0700 |
| 139 | // // mar13.post is 2011/03/13 01:15:00 -0800 |
| 140 | // |
| 141 | // // A Fall DST transition, when civil times are repeated. |
| 142 | // auto nov06 = lax.lookup(cctz::civil_second(2011, 11, 6, 1, 15, 0)); |
| 143 | // // nov06.kind == cctz::time_zone::civil_lookup::REPEATED |
| 144 | // // nov06.pre is 2011/11/06 01:15:00 -0700 |
| 145 | // // nov06.trans is 2011/11/06 01:00:00 -0800 |
| 146 | // // nov06.post is 2011/11/06 01:15:00 -0800 |
| 147 | struct civil_lookup { |
| 148 | enum civil_kind { |
| 149 | UNIQUE, // the civil time was singular (pre == trans == post) |
| 150 | SKIPPED, // the civil time did not exist (pre >= trans > post) |
| 151 | REPEATED, // the civil time was ambiguous (pre < trans <= post) |
| 152 | } kind; |
| 153 | time_point<sys_seconds> pre; // uses the pre-transition offset |
| 154 | time_point<sys_seconds> trans; // instant of civil-offset change |
| 155 | time_point<sys_seconds> post; // uses the post-transition offset |
| 156 | }; |
| 157 | civil_lookup lookup(const civil_second& cs) const; |
| 158 | |
| 159 | class Impl; |
| 160 | |
| 161 | private: |
| 162 | explicit time_zone(const Impl* impl) : impl_(impl) {} |
| 163 | const Impl* impl_; |
| 164 | }; |
| 165 | |
| 166 | // Relational operators. |
| 167 | bool operator==(time_zone lhs, time_zone rhs); |
| 168 | inline bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); } |
| 169 | |
| 170 | // Loads the named time zone. May perform I/O on the initial load. |
| 171 | // If the name is invalid, or some other kind of error occurs, returns |
| 172 | // false and "*tz" is set to the UTC time zone. |
| 173 | bool load_time_zone(const std::string& name, time_zone* tz); |
| 174 | |
| 175 | // Returns a time_zone representing UTC. Cannot fail. |
| 176 | time_zone utc_time_zone(); |
| 177 | |
| 178 | // Returns a time zone that is a fixed offset (seconds east) from UTC. |
| 179 | // Note: If the absolute value of the offset is greater than 24 hours |
| 180 | // you'll get UTC (i.e., zero offset) instead. |
| 181 | time_zone fixed_time_zone(const sys_seconds& offset); |
| 182 | |
| 183 | // Returns a time zone representing the local time zone. Falls back to UTC. |
| 184 | time_zone local_time_zone(); |
| 185 | |
| 186 | // Returns the civil time (cctz::civil_second) within the given time zone at |
| 187 | // the given absolute time (time_point). Since the additional fields provided |
| 188 | // by the time_zone::absolute_lookup struct should rarely be needed in modern |
| 189 | // code, this convert() function is simpler and should be preferred. |
| 190 | template <typename D> |
| 191 | inline civil_second convert(const time_point<D>& tp, const time_zone& tz) { |
| 192 | return tz.lookup(tp).cs; |
| 193 | } |
| 194 | |
| 195 | // Returns the absolute time (time_point) that corresponds to the given civil |
| 196 | // time within the given time zone. If the civil time is not unique (i.e., if |
| 197 | // it was either repeated or non-existent), then the returned time_point is |
| 198 | // the best estimate that preserves relative order. That is, this function |
| 199 | // guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz). |
| 200 | inline time_point<sys_seconds> convert(const civil_second& cs, |
| 201 | const time_zone& tz) { |
| 202 | const time_zone::civil_lookup cl = tz.lookup(cs); |
| 203 | if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans; |
| 204 | return cl.pre; |
| 205 | } |
| 206 | |
| 207 | namespace detail { |
| 208 | using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>; |
| 209 | std::string format(const std::string&, const time_point<sys_seconds>&, |
| 210 | const femtoseconds&, const time_zone&); |
| 211 | bool parse(const std::string&, const std::string&, const time_zone&, |
| 212 | time_point<sys_seconds>*, femtoseconds*, std::string* err = nullptr); |
| 213 | } // namespace detail |
| 214 | |
| 215 | // Formats the given time_point in the given cctz::time_zone according to |
| 216 | // the provided format string. Uses strftime()-like formatting options, |
| 217 | // with the following extensions: |
| 218 | // |
| 219 | // - %Ez - RFC3339-compatible numeric time zone (+hh:mm or -hh:mm) |
| 220 | // - %E#S - Seconds with # digits of fractional precision |
| 221 | // - %E*S - Seconds with full fractional precision (a literal '*') |
| 222 | // - %E#f - Fractional seconds with # digits of precision |
| 223 | // - %E*f - Fractional seconds with full precision (a literal '*') |
| 224 | // - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) |
| 225 | // |
| 226 | // Note that %E0S behaves like %S, and %E0f produces no characters. In |
| 227 | // contrast %E*f always produces at least one digit, which may be '0'. |
| 228 | // |
| 229 | // Note that %Y produces as many characters as it takes to fully render the |
| 230 | // year. A year outside of [-999:9999] when formatted with %E4Y will produce |
| 231 | // more than four characters, just like %Y. |
| 232 | // |
| 233 | // Tip: Format strings should include the UTC offset (e.g., %z or %Ez) so that |
| 234 | // the resultng string uniquely identifies an absolute time. |
| 235 | // |
| 236 | // Example: |
| 237 | // cctz::time_zone lax; |
| 238 | // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } |
| 239 | // auto tp = cctz::convert(cctz::civil_second(2013, 1, 2, 3, 4, 5), lax); |
| 240 | // std::string f = cctz::format("%H:%M:%S", tp, lax); // "03:04:05" |
| 241 | // f = cctz::format("%H:%M:%E3S", tp, lax); // "03:04:05.000" |
| 242 | template <typename D> |
| 243 | inline std::string format(const std::string& fmt, const time_point<D>& tp, |
| 244 | const time_zone& tz) { |
| 245 | const auto p = detail::split_seconds(tp); |
| 246 | const auto n = std::chrono::duration_cast<detail::femtoseconds>(p.second); |
| 247 | return detail::format(fmt, p.first, n, tz); |
| 248 | } |
| 249 | |
| 250 | // Parses an input string according to the provided format string and |
| 251 | // returns the corresponding time_point. Uses strftime()-like formatting |
| 252 | // options, with the same extensions as cctz::format(), but with the |
| 253 | // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. |
| 254 | // |
| 255 | // %Y consumes as many numeric characters as it can, so the matching data |
| 256 | // should always be terminated with a non-numeric. %E4Y always consumes |
| 257 | // exactly four characters, including any sign. |
| 258 | // |
| 259 | // Unspecified fields are taken from the default date and time of ... |
| 260 | // |
| 261 | // "1970-01-01 00:00:00.0 +0000" |
| 262 | // |
| 263 | // For example, parsing a string of "15:45" (%H:%M) will return a time_point |
| 264 | // that represents "1970-01-01 15:45:00.0 +0000". |
| 265 | // |
| 266 | // Note that parse() returns time instants, so it makes most sense to parse |
| 267 | // fully-specified date/time strings that include a UTC offset (%z or %Ez). |
| 268 | // |
| 269 | // Note also that parse() only heeds the fields year, month, day, hour, |
| 270 | // minute, (fractional) second, and UTC offset. Other fields, like weekday (%a |
| 271 | // or %A), while parsed for syntactic validity, are ignored in the conversion. |
| 272 | // |
| 273 | // Date and time fields that are out-of-range will be treated as errors rather |
| 274 | // than normalizing them like cctz::civil_second() would do. For example, it |
| 275 | // is an error to parse the date "Oct 32, 2013" because 32 is out of range. |
| 276 | // |
| 277 | // A second of ":60" is normalized to ":00" of the following minute with |
| 278 | // fractional seconds discarded. The following table shows how the given |
| 279 | // seconds and subseconds will be parsed: |
| 280 | // |
| 281 | // "59.x" -> 59.x // exact |
| 282 | // "60.x" -> 00.0 // normalized |
| 283 | // "00.x" -> 00.x // exact |
| 284 | // |
| 285 | // Errors are indicated by returning false. |
| 286 | // |
| 287 | // Example: |
| 288 | // const cctz::time_zone tz = ... |
| 289 | // std::chrono::system_clock::time_point tp; |
| 290 | // if (cctz::parse("%Y-%m-%d", "2015-10-09", tz, &tp)) { |
| 291 | // ... |
| 292 | // } |
| 293 | template <typename D> |
| 294 | inline bool parse(const std::string& fmt, const std::string& input, |
| 295 | const time_zone& tz, time_point<D>* tpp) { |
| 296 | time_point<sys_seconds> sec; |
| 297 | detail::femtoseconds fs; |
| 298 | const bool b = detail::parse(fmt, input, tz, &sec, &fs); |
| 299 | if (b) { |
| 300 | // TODO: Return false if unrepresentable as a time_point<D>. |
| 301 | *tpp = std::chrono::time_point_cast<D>(sec); |
| 302 | *tpp += std::chrono::duration_cast<D>(fs); |
| 303 | } |
| 304 | return b; |
| 305 | } |
| 306 | |
| 307 | } // namespace cctz |
| 308 | |
| 309 | #endif // CCTZ_TIME_ZONE_H_ |
| 310 | |