time_zone_libc.cc
Go to the documentation of this file.
00001 // Copyright 2016 Google Inc. All Rights Reserved.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //   https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 //   Unless required by applicable law or agreed to in writing, software
00010 //   distributed under the License is distributed on an "AS IS" BASIS,
00011 //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 //   See the License for the specific language governing permissions and
00013 //   limitations under the License.
00014 
00015 #if defined(_WIN32) || defined(_WIN64)
00016 #define _CRT_SECURE_NO_WARNINGS 1
00017 #endif
00018 
00019 #include "time_zone_libc.h"
00020 
00021 #include <chrono>
00022 #include <ctime>
00023 #include <limits>
00024 #include <tuple>
00025 #include <utility>
00026 
00027 #include "absl/time/internal/cctz/include/cctz/civil_time.h"
00028 #include "absl/time/internal/cctz/include/cctz/time_zone.h"
00029 
00030 namespace absl {
00031 namespace time_internal {
00032 namespace cctz {
00033 
00034 namespace {
00035 
00036 // .first is seconds east of UTC; .second is the time-zone abbreviation.
00037 using OffsetAbbr = std::pair<int, const char*>;
00038 
00039 // Defines a function that can be called as follows:
00040 //
00041 //   std::tm tm = ...;
00042 //   OffsetAbbr off_abbr = get_offset_abbr(tm);
00043 //
00044 #if defined(_WIN32) || defined(_WIN64)
00045 // Uses the globals: '_timezone', '_dstbias' and '_tzname'.
00046 OffsetAbbr get_offset_abbr(const std::tm& tm) {
00047   const bool is_dst = tm.tm_isdst > 0;
00048   const int off = _timezone + (is_dst ? _dstbias : 0);
00049   const char* abbr = _tzname[is_dst];
00050   return {off, abbr};
00051 }
00052 #elif defined(__sun)
00053 // Uses the globals: 'timezone', 'altzone' and 'tzname'.
00054 OffsetAbbr get_offset_abbr(const std::tm& tm) {
00055   const bool is_dst = tm.tm_isdst > 0;
00056   const int off = is_dst ? altzone : timezone;
00057   const char* abbr = tzname[is_dst];
00058   return {off, abbr};
00059 }
00060 #elif defined(__native_client__) || defined(__myriad2__) || \
00061     defined(__EMSCRIPTEN__)
00062 // Uses the globals: 'timezone' and 'tzname'.
00063 OffsetAbbr get_offset_abbr(const std::tm& tm) {
00064   const bool is_dst = tm.tm_isdst > 0;
00065   const int off = _timezone + (is_dst ? 60 * 60 : 0);
00066   const char* abbr = tzname[is_dst];
00067   return {off, abbr};
00068 }
00069 #else
00070 //
00071 // Returns an OffsetAbbr using std::tm fields with various spellings.
00072 //
00073 #if !defined(tm_gmtoff) && !defined(tm_zone)
00074 template <typename T>
00075 OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::tm_gmtoff) = nullptr,
00076                            decltype(&T::tm_zone) = nullptr) {
00077   return {tm.tm_gmtoff, tm.tm_zone};
00078 }
00079 #endif  // !defined(tm_gmtoff) && !defined(tm_zone)
00080 #if !defined(__tm_gmtoff) && !defined(__tm_zone)
00081 template <typename T>
00082 OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::__tm_gmtoff) = nullptr,
00083                            decltype(&T::__tm_zone) = nullptr) {
00084   return {tm.__tm_gmtoff, tm.__tm_zone};
00085 }
00086 #endif  // !defined(__tm_gmtoff) && !defined(__tm_zone)
00087 #endif
00088 
00089 inline std::tm* gm_time(const std::time_t *timep, std::tm *result) {
00090 #if defined(_WIN32) || defined(_WIN64)
00091     return gmtime_s(result, timep) ? nullptr : result;
00092 #else
00093     return gmtime_r(timep, result);
00094 #endif
00095 }
00096 
00097 inline std::tm* local_time(const std::time_t *timep, std::tm *result) {
00098 #if defined(_WIN32) || defined(_WIN64)
00099     return localtime_s(result, timep) ? nullptr : result;
00100 #else
00101     return localtime_r(timep, result);
00102 #endif
00103 }
00104 
00105 // Converts a civil second and "dst" flag into a time_t and UTC offset.
00106 // Returns false if time_t cannot represent the requested civil second.
00107 // Caller must have already checked that cs.year() will fit into a tm_year.
00108 bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
00109   std::tm tm;
00110   tm.tm_year = static_cast<int>(cs.year() - year_t{1900});
00111   tm.tm_mon = cs.month() - 1;
00112   tm.tm_mday = cs.day();
00113   tm.tm_hour = cs.hour();
00114   tm.tm_min = cs.minute();
00115   tm.tm_sec = cs.second();
00116   tm.tm_isdst = is_dst;
00117   *t = std::mktime(&tm);
00118   if (*t == std::time_t{-1}) {
00119     std::tm tm2;
00120     const std::tm* tmp = local_time(t, &tm2);
00121     if (tmp == nullptr || tmp->tm_year != tm.tm_year ||
00122         tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday ||
00123         tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min ||
00124         tmp->tm_sec != tm.tm_sec) {
00125       // A true error (not just one second before the epoch).
00126       return false;
00127     }
00128   }
00129   *off = get_offset_abbr(tm).first;
00130   return true;
00131 }
00132 
00133 // Find the least time_t in [lo:hi] where local time matches offset, given:
00134 // (1) lo doesn't match, (2) hi does, and (3) there is only one transition.
00135 std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
00136   std::tm tm;
00137   while (lo + 1 != hi) {
00138     const std::time_t mid = lo + (hi - lo) / 2;
00139     if (std::tm* tmp = local_time(&mid, &tm)) {
00140       if (get_offset_abbr(*tmp).first == offset) {
00141         hi = mid;
00142       } else {
00143         lo = mid;
00144       }
00145     } else {
00146       // If std::tm cannot hold some result we resort to a linear search,
00147       // ignoring all failed conversions.  Slow, but never really happens.
00148       while (++lo != hi) {
00149         if (std::tm* tmp = local_time(&lo, &tm)) {
00150           if (get_offset_abbr(*tmp).first == offset) break;
00151         }
00152       }
00153       return lo;
00154     }
00155   }
00156   return hi;
00157 }
00158 
00159 }  // namespace
00160 
00161 TimeZoneLibC::TimeZoneLibC(const std::string& name)
00162     : local_(name == "localtime") {}
00163 
00164 time_zone::absolute_lookup TimeZoneLibC::BreakTime(
00165     const time_point<seconds>& tp) const {
00166   time_zone::absolute_lookup al;
00167   al.offset = 0;
00168   al.is_dst = false;
00169   al.abbr = "-00";
00170 
00171   const std::int_fast64_t s = ToUnixSeconds(tp);
00172 
00173   // If std::time_t cannot hold the input we saturate the output.
00174   if (s < std::numeric_limits<std::time_t>::min()) {
00175     al.cs = civil_second::min();
00176     return al;
00177   }
00178   if (s > std::numeric_limits<std::time_t>::max()) {
00179     al.cs = civil_second::max();
00180     return al;
00181   }
00182 
00183   const std::time_t t = static_cast<std::time_t>(s);
00184   std::tm tm;
00185   std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
00186 
00187   // If std::tm cannot hold the result we saturate the output.
00188   if (tmp == nullptr) {
00189     al.cs = (s < 0) ? civil_second::min() : civil_second::max();
00190     return al;
00191   }
00192 
00193   const year_t year = tmp->tm_year + year_t{1900};
00194   al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday,
00195                        tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
00196   std::tie(al.offset, al.abbr) = get_offset_abbr(*tmp);
00197   if (!local_) al.abbr = "UTC";  // as expected by cctz
00198   al.is_dst = tmp->tm_isdst > 0;
00199   return al;
00200 }
00201 
00202 time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
00203   if (!local_) {
00204     // If time_point<seconds> cannot hold the result we saturate.
00205     static const civil_second min_tp_cs =
00206         civil_second() + ToUnixSeconds(time_point<seconds>::min());
00207     static const civil_second max_tp_cs =
00208         civil_second() + ToUnixSeconds(time_point<seconds>::max());
00209     const time_point<seconds> tp =
00210         (cs < min_tp_cs)
00211             ? time_point<seconds>::min()
00212             : (cs > max_tp_cs) ? time_point<seconds>::max()
00213                                : FromUnixSeconds(cs - civil_second());
00214     return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
00215   }
00216 
00217   // If tm_year cannot hold the requested year we saturate the result.
00218   if (cs.year() < 0) {
00219     if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
00220       const time_point<seconds> tp = time_point<seconds>::min();
00221       return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
00222     }
00223   } else {
00224     if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
00225       const time_point<seconds> tp = time_point<seconds>::max();
00226       return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
00227     }
00228   }
00229 
00230   // We probe with "is_dst" values of 0 and 1 to try to distinguish unique
00231   // civil seconds from skipped or repeated ones.  This is not always possible
00232   // however, as the "dst" flag does not change over some offset transitions.
00233   // We are also subject to the vagaries of mktime() implementations.
00234   std::time_t t0, t1;
00235   int offset0, offset1;
00236   if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) {
00237     if (t0 == t1) {
00238       // The civil time was singular (pre == trans == post).
00239       const time_point<seconds> tp = FromUnixSeconds(t0);
00240       return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
00241     }
00242 
00243     if (t0 > t1) {
00244       std::swap(t0, t1);
00245       std::swap(offset0, offset1);
00246     }
00247     const std::time_t tt = find_trans(t0, t1, offset1);
00248     const time_point<seconds> trans = FromUnixSeconds(tt);
00249 
00250     if (offset0 < offset1) {
00251       // The civil time did not exist (pre >= trans > post).
00252       const time_point<seconds> pre = FromUnixSeconds(t1);
00253       const time_point<seconds> post = FromUnixSeconds(t0);
00254       return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
00255     }
00256 
00257     // The civil time was ambiguous (pre < trans <= post).
00258     const time_point<seconds> pre = FromUnixSeconds(t0);
00259     const time_point<seconds> post = FromUnixSeconds(t1);
00260     return {time_zone::civil_lookup::REPEATED, pre, trans, post};
00261   }
00262 
00263   // make_time() failed somehow so we saturate the result.
00264   const time_point<seconds> tp = (cs < civil_second())
00265                                      ? time_point<seconds>::min()
00266                                      : time_point<seconds>::max();
00267   return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
00268 }
00269 
00270 bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
00271                                   time_zone::civil_transition*) const {
00272   return false;
00273 }
00274 
00275 bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
00276                                   time_zone::civil_transition*) const {
00277   return false;
00278 }
00279 
00280 std::string TimeZoneLibC::Version() const {
00281   return std::string();  // unknown
00282 }
00283 
00284 std::string TimeZoneLibC::Description() const {
00285   return local_ ? "localtime" : "UTC";
00286 }
00287 
00288 }  // namespace cctz
00289 }  // namespace time_internal
00290 }  // namespace absl


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:15