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