tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

time_zone_libc.cc (11042B)


      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 #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_WIN32)
     16 #define _CRT_SECURE_NO_WARNINGS 1
     17 #endif
     18 
     19 #include "absl/time/internal/cctz/src/time_zone_libc.h"
     20 
     21 #include <chrono>
     22 #include <ctime>
     23 #include <limits>
     24 #include <utility>
     25 
     26 #include "absl/base/config.h"
     27 #include "absl/time/internal/cctz/include/cctz/civil_time.h"
     28 #include "absl/time/internal/cctz/include/cctz/time_zone.h"
     29 
     30 #if defined(_AIX)
     31 extern "C" {
     32 extern long altzone;
     33 }
     34 #endif
     35 
     36 namespace absl {
     37 ABSL_NAMESPACE_BEGIN
     38 namespace time_internal {
     39 namespace cctz {
     40 
     41 namespace {
     42 
     43 #if defined(_WIN32) || defined(_WIN64)
     44 // Uses the globals: '_timezone', '_dstbias' and '_tzname'.
     45 auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) {
     46  const bool is_dst = tm.tm_isdst > 0;
     47  return _timezone + (is_dst ? _dstbias : 0);
     48 }
     49 auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) {
     50  const bool is_dst = tm.tm_isdst > 0;
     51  return _tzname[is_dst];
     52 }
     53 #elif defined(__sun) || defined(_AIX)
     54 // Uses the globals: 'timezone', 'altzone' and 'tzname'.
     55 auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
     56  const bool is_dst = tm.tm_isdst > 0;
     57  return is_dst ? altzone : timezone;
     58 }
     59 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
     60  const bool is_dst = tm.tm_isdst > 0;
     61  return tzname[is_dst];
     62 }
     63 #elif defined(__native_client__) || defined(__myriad2__) || \
     64    defined(__EMSCRIPTEN__)
     65 // Uses the globals: '_timezone' and 'tzname'.
     66 auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) {
     67  const bool is_dst = tm.tm_isdst > 0;
     68  return _timezone + (is_dst ? 60 * 60 : 0);
     69 }
     70 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
     71  const bool is_dst = tm.tm_isdst > 0;
     72  return tzname[is_dst];
     73 }
     74 #elif defined(__VXWORKS__)
     75 // Uses the globals: 'timezone' and 'tzname'.
     76 auto tm_gmtoff(const std::tm& tm) -> decltype(timezone + 0) {
     77  const bool is_dst = tm.tm_isdst > 0;
     78  return timezone + (is_dst ? 60 * 60 : 0);
     79 }
     80 auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
     81  const bool is_dst = tm.tm_isdst > 0;
     82  return tzname[is_dst];
     83 }
     84 #else
     85 // Adapt to different spellings of the struct std::tm extension fields.
     86 #if defined(tm_gmtoff)
     87 auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) {
     88  return tm.tm_gmtoff;
     89 }
     90 #elif defined(__tm_gmtoff)
     91 auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) {
     92  return tm.__tm_gmtoff;
     93 }
     94 #else
     95 template <typename T>
     96 auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
     97  return tm.tm_gmtoff;
     98 }
     99 template <typename T>
    100 auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
    101  return tm.__tm_gmtoff;
    102 }
    103 #endif  // tm_gmtoff
    104 #if defined(tm_zone)
    105 auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; }
    106 #elif defined(__tm_zone)
    107 auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
    108  return tm.__tm_zone;
    109 }
    110 #else
    111 template <typename T>
    112 auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
    113  return tm.tm_zone;
    114 }
    115 template <typename T>
    116 auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
    117  return tm.__tm_zone;
    118 }
    119 #endif  // tm_zone
    120 #endif
    121 using tm_gmtoff_t = decltype(tm_gmtoff(std::tm{}));
    122 
    123 inline std::tm* gm_time(const std::time_t* timep, std::tm* result) {
    124 #if defined(_WIN32) || defined(_WIN64)
    125  return gmtime_s(result, timep) ? nullptr : result;
    126 #else
    127  return gmtime_r(timep, result);
    128 #endif
    129 }
    130 
    131 inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
    132 #if defined(_WIN32) || defined(_WIN64)
    133  return localtime_s(result, timep) ? nullptr : result;
    134 #else
    135  return localtime_r(timep, result);
    136 #endif
    137 }
    138 
    139 // Converts a civil second and "dst" flag into a time_t and a struct tm.
    140 // Returns false if time_t cannot represent the requested civil second.
    141 // Caller must have already checked that cs.year() will fit into a tm_year.
    142 bool make_time(const civil_second& cs, int is_dst, std::time_t* t,
    143               std::tm* tm) {
    144  tm->tm_year = static_cast<int>(cs.year() - year_t{1900});
    145  tm->tm_mon = cs.month() - 1;
    146  tm->tm_mday = cs.day();
    147  tm->tm_hour = cs.hour();
    148  tm->tm_min = cs.minute();
    149  tm->tm_sec = cs.second();
    150  tm->tm_isdst = is_dst;
    151  *t = std::mktime(tm);
    152  if (*t == std::time_t{-1}) {
    153    std::tm tm2;
    154    const std::tm* tmp = local_time(t, &tm2);
    155    if (tmp == nullptr || tmp->tm_year != tm->tm_year ||
    156        tmp->tm_mon != tm->tm_mon || tmp->tm_mday != tm->tm_mday ||
    157        tmp->tm_hour != tm->tm_hour || tmp->tm_min != tm->tm_min ||
    158        tmp->tm_sec != tm->tm_sec) {
    159      // A true error (not just one second before the epoch).
    160      return false;
    161    }
    162  }
    163  return true;
    164 }
    165 
    166 // Find the least time_t in [lo:hi] where local time matches offset, given:
    167 // (1) lo doesn't match, (2) hi does, and (3) there is only one transition.
    168 std::time_t find_trans(std::time_t lo, std::time_t hi, tm_gmtoff_t offset) {
    169  std::tm tm;
    170  while (lo + 1 != hi) {
    171    const std::time_t mid = lo + (hi - lo) / 2;
    172    std::tm* tmp = local_time(&mid, &tm);
    173    if (tmp != nullptr) {
    174      if (tm_gmtoff(*tmp) == offset) {
    175        hi = mid;
    176      } else {
    177        lo = mid;
    178      }
    179    } else {
    180      // If std::tm cannot hold some result we resort to a linear search,
    181      // ignoring all failed conversions.  Slow, but never really happens.
    182      while (++lo != hi) {
    183        tmp = local_time(&lo, &tm);
    184        if (tmp != nullptr) {
    185          if (tm_gmtoff(*tmp) == offset) break;
    186        }
    187      }
    188      return lo;
    189    }
    190  }
    191  return hi;
    192 }
    193 
    194 }  // namespace
    195 
    196 std::unique_ptr<TimeZoneLibC> TimeZoneLibC::Make(const std::string& name) {
    197  return std::unique_ptr<TimeZoneLibC>(new TimeZoneLibC(name));
    198 }
    199 
    200 time_zone::absolute_lookup TimeZoneLibC::BreakTime(
    201    const time_point<seconds>& tp) const {
    202  time_zone::absolute_lookup al;
    203  al.offset = 0;
    204  al.is_dst = false;
    205  al.abbr = "-00";
    206 
    207  const std::int_fast64_t s = ToUnixSeconds(tp);
    208 
    209  // If std::time_t cannot hold the input we saturate the output.
    210  if (s < std::numeric_limits<std::time_t>::min()) {
    211    al.cs = civil_second::min();
    212    return al;
    213  }
    214  if (s > std::numeric_limits<std::time_t>::max()) {
    215    al.cs = civil_second::max();
    216    return al;
    217  }
    218 
    219  const std::time_t t = static_cast<std::time_t>(s);
    220  std::tm tm;
    221  std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
    222 
    223  // If std::tm cannot hold the result we saturate the output.
    224  if (tmp == nullptr) {
    225    al.cs = (s < 0) ? civil_second::min() : civil_second::max();
    226    return al;
    227  }
    228 
    229  const year_t year = tmp->tm_year + year_t{1900};
    230  al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour,
    231                       tmp->tm_min, tmp->tm_sec);
    232  al.offset = static_cast<int>(tm_gmtoff(*tmp));
    233  al.abbr = local_ ? tm_zone(*tmp) : "UTC";  // as expected by cctz
    234  al.is_dst = tmp->tm_isdst > 0;
    235  return al;
    236 }
    237 
    238 time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
    239  if (!local_) {
    240    // If time_point<seconds> cannot hold the result we saturate.
    241    static const civil_second min_tp_cs =
    242        civil_second() + ToUnixSeconds(time_point<seconds>::min());
    243    static const civil_second max_tp_cs =
    244        civil_second() + ToUnixSeconds(time_point<seconds>::max());
    245    const time_point<seconds> tp = (cs < min_tp_cs) ? time_point<seconds>::min()
    246                                   : (cs > max_tp_cs)
    247                                       ? time_point<seconds>::max()
    248                                       : FromUnixSeconds(cs - civil_second());
    249    return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
    250  }
    251 
    252  // If tm_year cannot hold the requested year we saturate the result.
    253  if (cs.year() < 0) {
    254    if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
    255      const time_point<seconds> tp = time_point<seconds>::min();
    256      return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
    257    }
    258  } else {
    259    if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
    260      const time_point<seconds> tp = time_point<seconds>::max();
    261      return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
    262    }
    263  }
    264 
    265  // We probe with "is_dst" values of 0 and 1 to try to distinguish unique
    266  // civil seconds from skipped or repeated ones.  This is not always possible
    267  // however, as the "dst" flag does not change over some offset transitions.
    268  // We are also subject to the vagaries of mktime() implementations. For
    269  // example, some implementations treat "tm_isdst" as a demand (useless),
    270  // and some as a disambiguator (useful).
    271  std::time_t t0, t1;
    272  std::tm tm0, tm1;
    273  if (make_time(cs, 0, &t0, &tm0) && make_time(cs, 1, &t1, &tm1)) {
    274    if (tm0.tm_isdst == tm1.tm_isdst) {
    275      // The civil time was singular (pre == trans == post).
    276      const time_point<seconds> tp = FromUnixSeconds(tm0.tm_isdst ? t1 : t0);
    277      return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
    278    }
    279 
    280    tm_gmtoff_t offset = tm_gmtoff(tm0);
    281    if (t0 < t1) {  // negative DST
    282      std::swap(t0, t1);
    283      offset = tm_gmtoff(tm1);
    284    }
    285 
    286    const std::time_t tt = find_trans(t1, t0, offset);
    287    const time_point<seconds> trans = FromUnixSeconds(tt);
    288 
    289    if (tm0.tm_isdst) {
    290      // The civil time did not exist (pre >= trans > post).
    291      const time_point<seconds> pre = FromUnixSeconds(t0);
    292      const time_point<seconds> post = FromUnixSeconds(t1);
    293      return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
    294    }
    295 
    296    // The civil time was ambiguous (pre < trans <= post).
    297    const time_point<seconds> pre = FromUnixSeconds(t1);
    298    const time_point<seconds> post = FromUnixSeconds(t0);
    299    return {time_zone::civil_lookup::REPEATED, pre, trans, post};
    300  }
    301 
    302  // make_time() failed somehow so we saturate the result.
    303  const time_point<seconds> tp = (cs < civil_second())
    304                                     ? time_point<seconds>::min()
    305                                     : time_point<seconds>::max();
    306  return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
    307 }
    308 
    309 bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
    310                                  time_zone::civil_transition*) const {
    311  return false;
    312 }
    313 
    314 bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
    315                                  time_zone::civil_transition*) const {
    316  return false;
    317 }
    318 
    319 std::string TimeZoneLibC::Version() const {
    320  return std::string();  // unknown
    321 }
    322 
    323 std::string TimeZoneLibC::Description() const {
    324  return local_ ? "localtime" : "UTC";
    325 }
    326 
    327 TimeZoneLibC::TimeZoneLibC(const std::string& name)
    328    : local_(name == "localtime") {}
    329 
    330 }  // namespace cctz
    331 }  // namespace time_internal
    332 ABSL_NAMESPACE_END
    333 }  // namespace absl