diff options
author | petrk <petrk@yandex-team.ru> | 2022-02-10 16:47:26 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:47:26 +0300 |
commit | af66956edf116b93d5a07894ccb61dd4447d0b34 (patch) | |
tree | 7b4cde47a2b3630f5a3048855949c4d46b893fc0 /contrib/libs/cctz/src | |
parent | dc5feacd15e76abe98d23fe0d4ea3c02cb8de60f (diff) | |
download | ydb-af66956edf116b93d5a07894ccb61dd4447d0b34.tar.gz |
Restoring authorship annotation for <petrk@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/cctz/src')
-rw-r--r-- | contrib/libs/cctz/src/civil_time_detail.cc | 142 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_fixed.cc | 164 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_fixed.h | 80 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_format.cc | 1496 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_if.cc | 66 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_if.h | 98 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_impl.cc | 148 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_impl.h | 132 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_info.cc | 1362 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_info.h | 224 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_libc.cc | 138 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_libc.h | 84 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_lookup.cc | 176 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_posix.cc | 290 | ||||
-rw-r--r-- | contrib/libs/cctz/src/time_zone_posix.h | 196 | ||||
-rw-r--r-- | contrib/libs/cctz/src/tzfile.h | 64 |
16 files changed, 2430 insertions, 2430 deletions
diff --git a/contrib/libs/cctz/src/civil_time_detail.cc b/contrib/libs/cctz/src/civil_time_detail.cc index b6856b8df4..4f9019f353 100644 --- a/contrib/libs/cctz/src/civil_time_detail.cc +++ b/contrib/libs/cctz/src/civil_time_detail.cc @@ -11,76 +11,76 @@ // 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. - + #include "cctz/civil_time_detail.h" -#include <iomanip> -#include <ostream> -#include <sstream> - -namespace cctz { -namespace detail { - -// Output stream operators output a format matching YYYY-MM-DDThh:mm:ss, -// while omitting fields inferior to the type's alignment. For example, -// civil_day is formatted only as YYYY-MM-DD. -std::ostream& operator<<(std::ostream& os, const civil_year& y) { - std::stringstream ss; - ss << y.year(); // No padding. - return os << ss.str(); -} -std::ostream& operator<<(std::ostream& os, const civil_month& m) { - std::stringstream ss; - ss << civil_year(m) << '-'; - ss << std::setfill('0') << std::setw(2) << m.month(); - return os << ss.str(); -} -std::ostream& operator<<(std::ostream& os, const civil_day& d) { - std::stringstream ss; - ss << civil_month(d) << '-'; - ss << std::setfill('0') << std::setw(2) << d.day(); - return os << ss.str(); -} -std::ostream& operator<<(std::ostream& os, const civil_hour& h) { - std::stringstream ss; - ss << civil_day(h) << 'T'; - ss << std::setfill('0') << std::setw(2) << h.hour(); - return os << ss.str(); -} -std::ostream& operator<<(std::ostream& os, const civil_minute& m) { - std::stringstream ss; - ss << civil_hour(m) << ':'; - ss << std::setfill('0') << std::setw(2) << m.minute(); - return os << ss.str(); -} -std::ostream& operator<<(std::ostream& os, const civil_second& s) { - std::stringstream ss; - ss << civil_minute(s) << ':'; - ss << std::setfill('0') << std::setw(2) << s.second(); - return os << ss.str(); -} - -//////////////////////////////////////////////////////////////////////// - -std::ostream& operator<<(std::ostream& os, weekday wd) { - switch (wd) { - case weekday::monday: - return os << "Monday"; - case weekday::tuesday: - return os << "Tuesday"; - case weekday::wednesday: - return os << "Wednesday"; - case weekday::thursday: - return os << "Thursday"; - case weekday::friday: - return os << "Friday"; - case weekday::saturday: - return os << "Saturday"; - case weekday::sunday: - return os << "Sunday"; - } - return os; // Should never get here, but -Wreturn-type may warn without this. -} - -} // namespace detail -} // namespace cctz +#include <iomanip> +#include <ostream> +#include <sstream> + +namespace cctz { +namespace detail { + +// Output stream operators output a format matching YYYY-MM-DDThh:mm:ss, +// while omitting fields inferior to the type's alignment. For example, +// civil_day is formatted only as YYYY-MM-DD. +std::ostream& operator<<(std::ostream& os, const civil_year& y) { + std::stringstream ss; + ss << y.year(); // No padding. + return os << ss.str(); +} +std::ostream& operator<<(std::ostream& os, const civil_month& m) { + std::stringstream ss; + ss << civil_year(m) << '-'; + ss << std::setfill('0') << std::setw(2) << m.month(); + return os << ss.str(); +} +std::ostream& operator<<(std::ostream& os, const civil_day& d) { + std::stringstream ss; + ss << civil_month(d) << '-'; + ss << std::setfill('0') << std::setw(2) << d.day(); + return os << ss.str(); +} +std::ostream& operator<<(std::ostream& os, const civil_hour& h) { + std::stringstream ss; + ss << civil_day(h) << 'T'; + ss << std::setfill('0') << std::setw(2) << h.hour(); + return os << ss.str(); +} +std::ostream& operator<<(std::ostream& os, const civil_minute& m) { + std::stringstream ss; + ss << civil_hour(m) << ':'; + ss << std::setfill('0') << std::setw(2) << m.minute(); + return os << ss.str(); +} +std::ostream& operator<<(std::ostream& os, const civil_second& s) { + std::stringstream ss; + ss << civil_minute(s) << ':'; + ss << std::setfill('0') << std::setw(2) << s.second(); + return os << ss.str(); +} + +//////////////////////////////////////////////////////////////////////// + +std::ostream& operator<<(std::ostream& os, weekday wd) { + switch (wd) { + case weekday::monday: + return os << "Monday"; + case weekday::tuesday: + return os << "Tuesday"; + case weekday::wednesday: + return os << "Wednesday"; + case weekday::thursday: + return os << "Thursday"; + case weekday::friday: + return os << "Friday"; + case weekday::saturday: + return os << "Saturday"; + case weekday::sunday: + return os << "Sunday"; + } + return os; // Should never get here, but -Wreturn-type may warn without this. +} + +} // namespace detail +} // namespace cctz diff --git a/contrib/libs/cctz/src/time_zone_fixed.cc b/contrib/libs/cctz/src/time_zone_fixed.cc index ce79822ada..427bc050e0 100644 --- a/contrib/libs/cctz/src/time_zone_fixed.cc +++ b/contrib/libs/cctz/src/time_zone_fixed.cc @@ -1,32 +1,32 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#include "time_zone_fixed.h" - -#include <algorithm> +// +// 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. + +#include "time_zone_fixed.h" + +#include <algorithm> #include <cassert> -#include <chrono> -#include <cstring> -#include <string> - -namespace cctz { - -namespace { - -// The prefix used for the internal names of fixed-offset zones. +#include <chrono> +#include <cstring> +#include <string> + +namespace cctz { + +namespace { + +// The prefix used for the internal names of fixed-offset zones. const char kFixedZonePrefix[] = "Fixed/UTC"; - + const char kDigits[] = "0123456789"; char* Format02d(char* p, int v) { @@ -35,69 +35,69 @@ char* Format02d(char* p, int v) { return p; } -int Parse02d(const char* p) { - if (const char* ap = std::strchr(kDigits, *p)) { - int v = static_cast<int>(ap - kDigits); - if (const char* bp = std::strchr(kDigits, *++p)) { - return (v * 10) + static_cast<int>(bp - kDigits); - } - } - return -1; -} - -} // namespace - +int Parse02d(const char* p) { + if (const char* ap = std::strchr(kDigits, *p)) { + int v = static_cast<int>(ap - kDigits); + if (const char* bp = std::strchr(kDigits, *++p)) { + return (v * 10) + static_cast<int>(bp - kDigits); + } + } + return -1; +} + +} // namespace + bool FixedOffsetFromName(const std::string& name, seconds* offset) { - if (name.compare(0, std::string::npos, "UTC", 3) == 0) { + if (name.compare(0, std::string::npos, "UTC", 3) == 0) { *offset = seconds::zero(); - return true; - } - + return true; + } + const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; const char* const ep = kFixedZonePrefix + prefix_len; if (name.size() != prefix_len + 9) // <prefix>+99:99:99 - return false; + return false; if (!std::equal(kFixedZonePrefix, ep, name.begin())) - return false; - const char* np = name.data() + prefix_len; - if (np[0] != '+' && np[0] != '-') - return false; - if (np[3] != ':' || np[6] != ':') // see note below about large offsets - return false; - - int hours = Parse02d(np + 1); - if (hours == -1) return false; - int mins = Parse02d(np + 4); - if (mins == -1) return false; - int secs = Parse02d(np + 7); - if (secs == -1) return false; - - secs += ((hours * 60) + mins) * 60; - if (secs > 24 * 60 * 60) return false; // outside supported offset range + return false; + const char* np = name.data() + prefix_len; + if (np[0] != '+' && np[0] != '-') + return false; + if (np[3] != ':' || np[6] != ':') // see note below about large offsets + return false; + + int hours = Parse02d(np + 1); + if (hours == -1) return false; + int mins = Parse02d(np + 4); + if (mins == -1) return false; + int secs = Parse02d(np + 7); + if (secs == -1) return false; + + secs += ((hours * 60) + mins) * 60; + if (secs > 24 * 60 * 60) return false; // outside supported offset range *offset = seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west - return true; -} - + return true; +} + std::string FixedOffsetToName(const seconds& offset) { if (offset == seconds::zero()) return "UTC"; - if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) { - // We don't support fixed-offset zones more than 24 hours - // away from UTC to avoid complications in rendering such - // offsets and to (somewhat) limit the total number of zones. - return "UTC"; - } + if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) { + // We don't support fixed-offset zones more than 24 hours + // away from UTC to avoid complications in rendering such + // offsets and to (somewhat) limit the total number of zones. + return "UTC"; + } int offset_seconds = static_cast<int>(offset.count()); const char sign = (offset_seconds < 0 ? '-' : '+'); int offset_minutes = offset_seconds / 60; offset_seconds %= 60; - if (sign == '-') { + if (sign == '-') { if (offset_seconds > 0) { offset_seconds -= 60; offset_minutes += 1; - } + } offset_seconds = -offset_seconds; offset_minutes = -offset_minutes; - } + } int offset_hours = offset_minutes / 60; offset_minutes %= 60; const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; @@ -111,11 +111,11 @@ std::string FixedOffsetToName(const seconds& offset) { ep = Format02d(ep, offset_seconds); *ep++ = '\0'; assert(ep == buf + sizeof(buf)); - return buf; -} - + return buf; +} + std::string FixedOffsetToAbbr(const seconds& offset) { - std::string abbr = FixedOffsetToName(offset); + std::string abbr = FixedOffsetToName(offset); const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1; if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99 abbr.erase(0, prefix_len); // +99:99:99 @@ -125,10 +125,10 @@ std::string FixedOffsetToAbbr(const seconds& offset) { abbr.erase(5, 2); // +9999 if (abbr[3] == '0' && abbr[4] == '0') { // +9900 abbr.erase(3, 2); // +99 - } - } - } - return abbr; -} - -} // namespace cctz + } + } + } + return abbr; +} + +} // namespace cctz diff --git a/contrib/libs/cctz/src/time_zone_fixed.h b/contrib/libs/cctz/src/time_zone_fixed.h index f37226ff46..1bf07cb258 100644 --- a/contrib/libs/cctz/src/time_zone_fixed.h +++ b/contrib/libs/cctz/src/time_zone_fixed.h @@ -1,45 +1,45 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#ifndef CCTZ_TIME_ZONE_FIXED_H_ -#define CCTZ_TIME_ZONE_FIXED_H_ - -#include <string> - +// +// 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. + +#ifndef CCTZ_TIME_ZONE_FIXED_H_ +#define CCTZ_TIME_ZONE_FIXED_H_ + +#include <string> + #include "cctz/time_zone.h" - -namespace cctz { - -// Helper functions for dealing with the names and abbreviations -// of time zones that are a fixed offset (seconds east) from UTC. -// FixedOffsetFromName() extracts the offset from a valid fixed-offset -// name, while FixedOffsetToName() and FixedOffsetToAbbr() generate -// the canonical zone name and abbreviation respectively for the given -// offset. -// -// A fixed-offset name looks like "Fixed/UTC<+-><hours>:<mins>:<secs>". -// Its abbreviation is of the form "UTC(<+->H?H(MM(SS)?)?)?" where the -// optional pieces are omitted when their values are zero. (Note that -// the sign is the opposite of that used in a POSIX TZ specification.) -// -// Note: FixedOffsetFromName() fails on syntax errors or when the parsed -// offset exceeds 24 hours. FixedOffsetToName() and FixedOffsetToAbbr() -// both produce "UTC" when the argument offset exceeds 24 hours. + +namespace cctz { + +// Helper functions for dealing with the names and abbreviations +// of time zones that are a fixed offset (seconds east) from UTC. +// FixedOffsetFromName() extracts the offset from a valid fixed-offset +// name, while FixedOffsetToName() and FixedOffsetToAbbr() generate +// the canonical zone name and abbreviation respectively for the given +// offset. +// +// A fixed-offset name looks like "Fixed/UTC<+-><hours>:<mins>:<secs>". +// Its abbreviation is of the form "UTC(<+->H?H(MM(SS)?)?)?" where the +// optional pieces are omitted when their values are zero. (Note that +// the sign is the opposite of that used in a POSIX TZ specification.) +// +// Note: FixedOffsetFromName() fails on syntax errors or when the parsed +// offset exceeds 24 hours. FixedOffsetToName() and FixedOffsetToAbbr() +// both produce "UTC" when the argument offset exceeds 24 hours. bool FixedOffsetFromName(const std::string& name, seconds* offset); std::string FixedOffsetToName(const seconds& offset); std::string FixedOffsetToAbbr(const seconds& offset); - -} // namespace cctz - -#endif // CCTZ_TIME_ZONE_FIXED_H_ + +} // namespace cctz + +#endif // CCTZ_TIME_ZONE_FIXED_H_ diff --git a/contrib/libs/cctz/src/time_zone_format.cc b/contrib/libs/cctz/src/time_zone_format.cc index b57371314e..21f000955f 100644 --- a/contrib/libs/cctz/src/time_zone_format.cc +++ b/contrib/libs/cctz/src/time_zone_format.cc @@ -1,68 +1,68 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#if !defined(HAS_STRPTIME) +// +// 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. + +#if !defined(HAS_STRPTIME) # if !defined(_MSC_VER) && !defined(__MINGW32__) -# define HAS_STRPTIME 1 // assume everyone has strptime() except windows -# endif -#endif - +# define HAS_STRPTIME 1 // assume everyone has strptime() except windows +# endif +#endif + #if defined(HAS_STRPTIME) && HAS_STRPTIME # if !defined(_XOPEN_SOURCE) # define _XOPEN_SOURCE // Definedness suffices for strptime. # endif #endif - + #include "cctz/time_zone.h" // Include time.h directly since, by C++ standards, ctime doesn't have to // declare strptime. #include <time.h> -#include <cctype> -#include <chrono> -#include <cstddef> -#include <cstdint> -#include <cstring> -#include <ctime> -#include <limits> -#include <string> -#include <vector> -#if !HAS_STRPTIME -#include <iomanip> -#include <sstream> -#endif - +#include <cctype> +#include <chrono> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <ctime> +#include <limits> +#include <string> +#include <vector> +#if !HAS_STRPTIME +#include <iomanip> +#include <sstream> +#endif + #include "cctz/civil_time.h" -#include "time_zone_if.h" - -namespace cctz { -namespace detail { - -namespace { - -#if !HAS_STRPTIME -// Build a strptime() using C++11's std::get_time(). -char* strptime(const char* s, const char* fmt, std::tm* tm) { - std::istringstream input(s); - input >> std::get_time(tm, fmt); - if (input.fail()) return nullptr; +#include "time_zone_if.h" + +namespace cctz { +namespace detail { + +namespace { + +#if !HAS_STRPTIME +// Build a strptime() using C++11's std::get_time(). +char* strptime(const char* s, const char* fmt, std::tm* tm) { + std::istringstream input(s); + input >> std::get_time(tm, fmt); + if (input.fail()) return nullptr; return const_cast<char*>(s) + (input.eof() ? strlen(s) : static_cast<std::size_t>(input.tellg())); -} -#endif - +} +#endif + // Convert a cctz::weekday to a tm_wday value (0-6, Sunday = 0). int ToTmWday(weekday wd) { switch (wd) { @@ -105,29 +105,29 @@ weekday FromTmWday(int tm_wday) { return weekday::sunday; /*NOTREACHED*/ } -std::tm ToTM(const time_zone::absolute_lookup& al) { - std::tm tm{}; - tm.tm_sec = al.cs.second(); - tm.tm_min = al.cs.minute(); - tm.tm_hour = al.cs.hour(); - tm.tm_mday = al.cs.day(); - tm.tm_mon = al.cs.month() - 1; - - // Saturate tm.tm_year is cases of over/underflow. - if (al.cs.year() < std::numeric_limits<int>::min() + 1900) { - tm.tm_year = std::numeric_limits<int>::min(); - } else if (al.cs.year() - 1900 > std::numeric_limits<int>::max()) { - tm.tm_year = std::numeric_limits<int>::max(); - } else { - tm.tm_year = static_cast<int>(al.cs.year() - 1900); - } - +std::tm ToTM(const time_zone::absolute_lookup& al) { + std::tm tm{}; + tm.tm_sec = al.cs.second(); + tm.tm_min = al.cs.minute(); + tm.tm_hour = al.cs.hour(); + tm.tm_mday = al.cs.day(); + tm.tm_mon = al.cs.month() - 1; + + // Saturate tm.tm_year is cases of over/underflow. + if (al.cs.year() < std::numeric_limits<int>::min() + 1900) { + tm.tm_year = std::numeric_limits<int>::min(); + } else if (al.cs.year() - 1900 > std::numeric_limits<int>::max()) { + tm.tm_year = std::numeric_limits<int>::max(); + } else { + tm.tm_year = static_cast<int>(al.cs.year() - 1900); + } + tm.tm_wday = ToTmWday(get_weekday(al.cs)); tm.tm_yday = get_yearday(al.cs) - 1; - tm.tm_isdst = al.is_dst ? 1 : 0; - return tm; -} - + tm.tm_isdst = al.is_dst ? 1 : 0; + return tm; +} + // Returns the week of the year [0:53] given a civil day and the day on // which weeks are defined to start. int ToWeek(const civil_day& cd, weekday week_start) { @@ -135,55 +135,55 @@ int ToWeek(const civil_day& cd, weekday week_start) { return static_cast<int>((d - prev_weekday(civil_year(d), week_start)) / 7); } -const char kDigits[] = "0123456789"; - -// Formats a 64-bit integer in the given field width. Note that it is up -// to the caller of Format64() [and Format02d()/FormatOffset()] to ensure -// that there is sufficient space before ep to hold the conversion. -char* Format64(char* ep, int width, std::int_fast64_t v) { - bool neg = false; - if (v < 0) { - --width; - neg = true; - if (v == std::numeric_limits<std::int_fast64_t>::min()) { - // Avoid negating minimum value. - std::int_fast64_t last_digit = -(v % 10); - v /= 10; - if (last_digit < 0) { - ++v; - last_digit += 10; - } - --width; - *--ep = kDigits[last_digit]; - } - v = -v; - } - do { - --width; - *--ep = kDigits[v % 10]; - } while (v /= 10); - while (--width >= 0) *--ep = '0'; // zero pad - if (neg) *--ep = '-'; - return ep; -} - -// Formats [0 .. 99] as %02d. -char* Format02d(char* ep, int v) { - *--ep = kDigits[v % 10]; - *--ep = kDigits[(v / 10) % 10]; - return ep; -} - -// Formats a UTC offset, like +00:00. +const char kDigits[] = "0123456789"; + +// Formats a 64-bit integer in the given field width. Note that it is up +// to the caller of Format64() [and Format02d()/FormatOffset()] to ensure +// that there is sufficient space before ep to hold the conversion. +char* Format64(char* ep, int width, std::int_fast64_t v) { + bool neg = false; + if (v < 0) { + --width; + neg = true; + if (v == std::numeric_limits<std::int_fast64_t>::min()) { + // Avoid negating minimum value. + std::int_fast64_t last_digit = -(v % 10); + v /= 10; + if (last_digit < 0) { + ++v; + last_digit += 10; + } + --width; + *--ep = kDigits[last_digit]; + } + v = -v; + } + do { + --width; + *--ep = kDigits[v % 10]; + } while (v /= 10); + while (--width >= 0) *--ep = '0'; // zero pad + if (neg) *--ep = '-'; + return ep; +} + +// Formats [0 .. 99] as %02d. +char* Format02d(char* ep, int v) { + *--ep = kDigits[v % 10]; + *--ep = kDigits[(v / 10) % 10]; + return ep; +} + +// Formats a UTC offset, like +00:00. char* FormatOffset(char* ep, int offset, const char* mode) { // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and // generate a "negative zero" when we're formatting a zero offset // as the result of a failed load_time_zone(). - char sign = '+'; + char sign = '+'; if (offset < 0) { offset = -offset; // bounded by 24h so no overflow - sign = '-'; - } + sign = '-'; + } const int seconds = offset % 60; const int minutes = (offset /= 60) % 60; const int hours = offset /= 60; @@ -203,202 +203,202 @@ char* FormatOffset(char* ep, int offset, const char* mode) { if (sep != '\0') *--ep = sep; } ep = Format02d(ep, hours); - *--ep = sign; - return ep; -} - -// Formats a std::tm using strftime(3). -void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) { - // strftime(3) returns the number of characters placed in the output - // array (which may be 0 characters). It also returns 0 to indicate + *--ep = sign; + return ep; +} + +// Formats a std::tm using strftime(3). +void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) { + // strftime(3) returns the number of characters placed in the output + // array (which may be 0 characters). It also returns 0 to indicate // an error, like the array wasn't large enough. To accommodate this, - // the following code grows the buffer size from 2x the format string - // length up to 32x. - for (std::size_t i = 2; i != 32; i *= 2) { - std::size_t buf_size = fmt.size() * i; - std::vector<char> buf(buf_size); - if (std::size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) { - out->append(&buf[0], len); - return; - } - } -} - -// Used for %E#S/%E#f specifiers and for data values in parse(). -template <typename T> -const char* ParseInt(const char* dp, int width, T min, T max, T* vp) { - if (dp != nullptr) { - const T kmin = std::numeric_limits<T>::min(); - bool erange = false; - bool neg = false; - T value = 0; - if (*dp == '-') { - neg = true; - if (width <= 0 || --width != 0) { - ++dp; - } else { - dp = nullptr; // width was 1 - } - } - if (const char* const bp = dp) { - while (const char* cp = strchr(kDigits, *dp)) { - int d = static_cast<int>(cp - kDigits); - if (d >= 10) break; - if (value < kmin / 10) { - erange = true; - break; - } - value *= 10; - if (value < kmin + d) { - erange = true; - break; - } - value -= d; - dp += 1; - if (width > 0 && --width == 0) break; - } - if (dp != bp && !erange && (neg || value != kmin)) { - if (!neg || value != 0) { - if (!neg) value = -value; // make positive - if (min <= value && value <= max) { - *vp = value; - } else { - dp = nullptr; - } - } else { - dp = nullptr; - } - } else { - dp = nullptr; - } - } - } - return dp; -} - -// The number of base-10 digits that can be represented by a signed 64-bit -// integer. That is, 10^kDigits10_64 <= 2^63 - 1 < 10^(kDigits10_64 + 1). -const int kDigits10_64 = 18; - -// 10^n for everything that can be represented by a signed 64-bit integer. -const std::int_fast64_t kExp10[kDigits10_64 + 1] = { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, -}; - -} // namespace - -// Uses strftime(3) to format the given Time. The following extended format -// specifiers are also supported: -// + // the following code grows the buffer size from 2x the format string + // length up to 32x. + for (std::size_t i = 2; i != 32; i *= 2) { + std::size_t buf_size = fmt.size() * i; + std::vector<char> buf(buf_size); + if (std::size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) { + out->append(&buf[0], len); + return; + } + } +} + +// Used for %E#S/%E#f specifiers and for data values in parse(). +template <typename T> +const char* ParseInt(const char* dp, int width, T min, T max, T* vp) { + if (dp != nullptr) { + const T kmin = std::numeric_limits<T>::min(); + bool erange = false; + bool neg = false; + T value = 0; + if (*dp == '-') { + neg = true; + if (width <= 0 || --width != 0) { + ++dp; + } else { + dp = nullptr; // width was 1 + } + } + if (const char* const bp = dp) { + while (const char* cp = strchr(kDigits, *dp)) { + int d = static_cast<int>(cp - kDigits); + if (d >= 10) break; + if (value < kmin / 10) { + erange = true; + break; + } + value *= 10; + if (value < kmin + d) { + erange = true; + break; + } + value -= d; + dp += 1; + if (width > 0 && --width == 0) break; + } + if (dp != bp && !erange && (neg || value != kmin)) { + if (!neg || value != 0) { + if (!neg) value = -value; // make positive + if (min <= value && value <= max) { + *vp = value; + } else { + dp = nullptr; + } + } else { + dp = nullptr; + } + } else { + dp = nullptr; + } + } + } + return dp; +} + +// The number of base-10 digits that can be represented by a signed 64-bit +// integer. That is, 10^kDigits10_64 <= 2^63 - 1 < 10^(kDigits10_64 + 1). +const int kDigits10_64 = 18; + +// 10^n for everything that can be represented by a signed 64-bit integer. +const std::int_fast64_t kExp10[kDigits10_64 + 1] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, +}; + +} // namespace + +// Uses strftime(3) to format the given Time. The following extended format +// specifiers are also supported: +// // - %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 '*') -// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) +// - %E#S - Seconds with # digits of fractional precision +// - %E*S - Seconds with full fractional precision (a literal '*') +// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) // - %ET - The RFC3339 "date-time" separator "T" -// -// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are -// handled internally for performance reasons. strftime(3) is slow due to -// a POSIX requirement to respect changes to ${TZ}. -// -// The TZ/GNU %s extension is handled internally because strftime() has -// to use mktime() to generate it, and that assumes the local time zone. -// -// We also handle the %z and %Z specifiers to accommodate platforms that do -// not support the tm_gmtoff and tm_zone extensions to std::tm. -// -// Requires that zero() <= fs < seconds(1). +// +// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are +// handled internally for performance reasons. strftime(3) is slow due to +// a POSIX requirement to respect changes to ${TZ}. +// +// The TZ/GNU %s extension is handled internally because strftime() has +// to use mktime() to generate it, and that assumes the local time zone. +// +// We also handle the %z and %Z specifiers to accommodate platforms that do +// not support the tm_gmtoff and tm_zone extensions to std::tm. +// +// Requires that zero() <= fs < seconds(1). std::string format(const std::string& format, const time_point<seconds>& tp, - const detail::femtoseconds& fs, const time_zone& tz) { - std::string result; - result.reserve(format.size()); // A reasonable guess for the result size. - const time_zone::absolute_lookup al = tz.lookup(tp); - const std::tm tm = ToTM(al); - - // Scratch buffer for internal conversions. - char buf[3 + kDigits10_64]; // enough for longest conversion - char* const ep = buf + sizeof(buf); - char* bp; // works back from ep - - // Maintain three, disjoint subsequences that span format. - // [format.begin() ... pending) : already formatted into result - // [pending ... cur) : formatting pending, but no special cases - // [cur ... format.end()) : unexamined - // Initially, everything is in the unexamined part. - const char* pending = format.c_str(); // NUL terminated - const char* cur = pending; - const char* end = pending + format.length(); - - while (cur != end) { // while something is unexamined - // Moves cur to the next percent sign. - const char* start = cur; - while (cur != end && *cur != '%') ++cur; - - // If the new pending text is all ordinary, copy it out. - if (cur != start && pending == start) { - result.append(pending, static_cast<std::size_t>(cur - pending)); - pending = start = cur; - } - - // Span the sequential percent signs. - const char* percent = cur; - while (cur != end && *cur == '%') ++cur; - - // If the new pending text is all percents, copy out one - // percent for every matched pair, then skip those pairs. - if (cur != start && pending == start) { - std::size_t escaped = static_cast<std::size_t>(cur - pending) / 2; - result.append(pending, escaped); - pending += escaped * 2; - // Also copy out a single trailing percent. - if (pending != cur && cur == end) { - result.push_back(*pending++); - } - } - - // Loop unless we have an unescaped percent. - if (cur == end || (cur - percent) % 2 == 0) continue; - - // Simple specifiers that we handle ourselves. + const detail::femtoseconds& fs, const time_zone& tz) { + std::string result; + result.reserve(format.size()); // A reasonable guess for the result size. + const time_zone::absolute_lookup al = tz.lookup(tp); + const std::tm tm = ToTM(al); + + // Scratch buffer for internal conversions. + char buf[3 + kDigits10_64]; // enough for longest conversion + char* const ep = buf + sizeof(buf); + char* bp; // works back from ep + + // Maintain three, disjoint subsequences that span format. + // [format.begin() ... pending) : already formatted into result + // [pending ... cur) : formatting pending, but no special cases + // [cur ... format.end()) : unexamined + // Initially, everything is in the unexamined part. + const char* pending = format.c_str(); // NUL terminated + const char* cur = pending; + const char* end = pending + format.length(); + + while (cur != end) { // while something is unexamined + // Moves cur to the next percent sign. + const char* start = cur; + while (cur != end && *cur != '%') ++cur; + + // If the new pending text is all ordinary, copy it out. + if (cur != start && pending == start) { + result.append(pending, static_cast<std::size_t>(cur - pending)); + pending = start = cur; + } + + // Span the sequential percent signs. + const char* percent = cur; + while (cur != end && *cur == '%') ++cur; + + // If the new pending text is all percents, copy out one + // percent for every matched pair, then skip those pairs. + if (cur != start && pending == start) { + std::size_t escaped = static_cast<std::size_t>(cur - pending) / 2; + result.append(pending, escaped); + pending += escaped * 2; + // Also copy out a single trailing percent. + if (pending != cur && cur == end) { + result.push_back(*pending++); + } + } + + // Loop unless we have an unescaped percent. + if (cur == end || (cur - percent) % 2 == 0) continue; + + // Simple specifiers that we handle ourselves. if (strchr("YmdeUuWwHMSzZs%", *cur)) { - if (cur - 1 != pending) { - FormatTM(&result, std::string(pending, cur - 1), tm); - } - switch (*cur) { - case 'Y': - // This avoids the tm.tm_year overflow problem for %Y, however - // tm.tm_year will still be used by other specifiers like %D. - bp = Format64(ep, 0, al.cs.year()); - result.append(bp, static_cast<std::size_t>(ep - bp)); - break; - case 'm': - bp = Format02d(ep, al.cs.month()); - result.append(bp, static_cast<std::size_t>(ep - bp)); - break; - case 'd': - case 'e': - bp = Format02d(ep, al.cs.day()); - if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows - result.append(bp, static_cast<std::size_t>(ep - bp)); - break; + if (cur - 1 != pending) { + FormatTM(&result, std::string(pending, cur - 1), tm); + } + switch (*cur) { + case 'Y': + // This avoids the tm.tm_year overflow problem for %Y, however + // tm.tm_year will still be used by other specifiers like %D. + bp = Format64(ep, 0, al.cs.year()); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; + case 'm': + bp = Format02d(ep, al.cs.month()); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; + case 'd': + case 'e': + bp = Format02d(ep, al.cs.day()); + if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; case 'U': bp = Format02d(ep, ToWeek(civil_day(al.cs), weekday::sunday)); result.append(bp, static_cast<std::size_t>(ep - bp)); @@ -415,37 +415,37 @@ std::string format(const std::string& format, const time_point<seconds>& tp, bp = Format64(ep, 0, tm.tm_wday); result.append(bp, static_cast<std::size_t>(ep - bp)); break; - case 'H': - bp = Format02d(ep, al.cs.hour()); - result.append(bp, static_cast<std::size_t>(ep - bp)); - break; - case 'M': - bp = Format02d(ep, al.cs.minute()); - result.append(bp, static_cast<std::size_t>(ep - bp)); - break; - case 'S': - bp = Format02d(ep, al.cs.second()); - result.append(bp, static_cast<std::size_t>(ep - bp)); - break; - case 'z': + case 'H': + bp = Format02d(ep, al.cs.hour()); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; + case 'M': + bp = Format02d(ep, al.cs.minute()); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; + case 'S': + bp = Format02d(ep, al.cs.second()); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; + case 'z': bp = FormatOffset(ep, al.offset, ""); - result.append(bp, static_cast<std::size_t>(ep - bp)); - break; - case 'Z': - result.append(al.abbr); - break; - case 's': - bp = Format64(ep, 0, ToUnixSeconds(tp)); - result.append(bp, static_cast<std::size_t>(ep - bp)); - break; + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; + case 'Z': + result.append(al.abbr); + break; + case 's': + bp = Format64(ep, 0, ToUnixSeconds(tp)); + result.append(bp, static_cast<std::size_t>(ep - bp)); + break; case '%': result.push_back('%'); break; - } - pending = ++cur; - continue; - } - + } + pending = ++cur; + continue; + } + // More complex specifiers that we handle ourselves. if (*cur == ':' && cur + 1 != end) { if (*(cur + 1) == 'z') { @@ -484,10 +484,10 @@ std::string format(const std::string& format, const time_point<seconds>& tp, } } - // Loop if there is no E modifier. - if (*cur != 'E' || ++cur == end) continue; - - // Format our extensions. + // Loop if there is no E modifier. + if (*cur != 'E' || ++cur == end) continue; + + // Format our extensions. if (*cur == 'T') { // Formats %ET. if (cur - 2 != pending) { @@ -496,13 +496,13 @@ std::string format(const std::string& format, const time_point<seconds>& tp, result.append("T"); pending = ++cur; } else if (*cur == 'z') { - // Formats %Ez. - if (cur - 2 != pending) { - FormatTM(&result, std::string(pending, cur - 2), tm); - } + // Formats %Ez. + if (cur - 2 != pending) { + FormatTM(&result, std::string(pending, cur - 2), tm); + } bp = FormatOffset(ep, al.offset, ":"); - result.append(bp, static_cast<std::size_t>(ep - bp)); - pending = ++cur; + result.append(bp, static_cast<std::size_t>(ep - bp)); + pending = ++cur; } else if (*cur == '*' && cur + 1 != end && *(cur + 1) == 'z') { // Formats %E*z. if (cur - 2 != pending) { @@ -511,81 +511,81 @@ std::string format(const std::string& format, const time_point<seconds>& tp, bp = FormatOffset(ep, al.offset, ":*"); result.append(bp, static_cast<std::size_t>(ep - bp)); pending = cur += 2; - } else if (*cur == '*' && cur + 1 != end && - (*(cur + 1) == 'S' || *(cur + 1) == 'f')) { - // Formats %E*S or %E*F. - if (cur - 2 != pending) { - FormatTM(&result, std::string(pending, cur - 2), tm); - } - char* cp = ep; - bp = Format64(cp, 15, fs.count()); - while (cp != bp && cp[-1] == '0') --cp; - switch (*(cur + 1)) { - case 'S': - if (cp != bp) *--bp = '.'; - bp = Format02d(bp, al.cs.second()); - break; - case 'f': - if (cp == bp) *--bp = '0'; - break; - } - result.append(bp, static_cast<std::size_t>(cp - bp)); - pending = cur += 2; - } else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') { - // Formats %E4Y. - if (cur - 2 != pending) { - FormatTM(&result, std::string(pending, cur - 2), tm); - } - bp = Format64(ep, 4, al.cs.year()); - result.append(bp, static_cast<std::size_t>(ep - bp)); - pending = cur += 2; - } else if (std::isdigit(*cur)) { - // Possibly found %E#S or %E#f. - int n = 0; - if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) { - if (*np == 'S' || *np == 'f') { - // Formats %E#S or %E#f. - if (cur - 2 != pending) { - FormatTM(&result, std::string(pending, cur - 2), tm); - } - bp = ep; - if (n > 0) { - if (n > kDigits10_64) n = kDigits10_64; - bp = Format64(bp, n, (n > 15) ? fs.count() * kExp10[n - 15] - : fs.count() / kExp10[15 - n]); - if (*np == 'S') *--bp = '.'; - } - if (*np == 'S') bp = Format02d(bp, al.cs.second()); - result.append(bp, static_cast<std::size_t>(ep - bp)); - pending = cur = ++np; - } - } - } - } - - // Formats any remaining data. - if (end != pending) { - FormatTM(&result, std::string(pending, end), tm); - } - - return result; -} - -namespace { - + } else if (*cur == '*' && cur + 1 != end && + (*(cur + 1) == 'S' || *(cur + 1) == 'f')) { + // Formats %E*S or %E*F. + if (cur - 2 != pending) { + FormatTM(&result, std::string(pending, cur - 2), tm); + } + char* cp = ep; + bp = Format64(cp, 15, fs.count()); + while (cp != bp && cp[-1] == '0') --cp; + switch (*(cur + 1)) { + case 'S': + if (cp != bp) *--bp = '.'; + bp = Format02d(bp, al.cs.second()); + break; + case 'f': + if (cp == bp) *--bp = '0'; + break; + } + result.append(bp, static_cast<std::size_t>(cp - bp)); + pending = cur += 2; + } else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') { + // Formats %E4Y. + if (cur - 2 != pending) { + FormatTM(&result, std::string(pending, cur - 2), tm); + } + bp = Format64(ep, 4, al.cs.year()); + result.append(bp, static_cast<std::size_t>(ep - bp)); + pending = cur += 2; + } else if (std::isdigit(*cur)) { + // Possibly found %E#S or %E#f. + int n = 0; + if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) { + if (*np == 'S' || *np == 'f') { + // Formats %E#S or %E#f. + if (cur - 2 != pending) { + FormatTM(&result, std::string(pending, cur - 2), tm); + } + bp = ep; + if (n > 0) { + if (n > kDigits10_64) n = kDigits10_64; + bp = Format64(bp, n, (n > 15) ? fs.count() * kExp10[n - 15] + : fs.count() / kExp10[15 - n]); + if (*np == 'S') *--bp = '.'; + } + if (*np == 'S') bp = Format02d(bp, al.cs.second()); + result.append(bp, static_cast<std::size_t>(ep - bp)); + pending = cur = ++np; + } + } + } + } + + // Formats any remaining data. + if (end != pending) { + FormatTM(&result, std::string(pending, end), tm); + } + + return result; +} + +namespace { + const char* ParseOffset(const char* dp, const char* mode, int* offset) { - if (dp != nullptr) { + if (dp != nullptr) { const char first = *dp++; if (first == '+' || first == '-') { char sep = mode[0]; - int hours = 0; + int hours = 0; int minutes = 0; int seconds = 0; - const char* ap = ParseInt(dp, 2, 0, 23, &hours); - if (ap != nullptr && ap - dp == 2) { - dp = ap; - if (sep != '\0' && *ap == sep) ++ap; - const char* bp = ParseInt(ap, 2, 0, 59, &minutes); + const char* ap = ParseInt(dp, 2, 0, 23, &hours); + if (ap != nullptr && ap - dp == 2) { + dp = ap; + if (sep != '\0' && *ap == sep) ++ap; + const char* bp = ParseInt(ap, 2, 0, 59, &minutes); if (bp != nullptr && bp - ap == 2) { dp = bp; if (sep != '\0' && *bp == sep) ++bp; @@ -594,60 +594,60 @@ const char* ParseOffset(const char* dp, const char* mode, int* offset) { } *offset = ((hours * 60 + minutes) * 60) + seconds; if (first == '-') *offset = -*offset; - } else { - dp = nullptr; - } + } else { + dp = nullptr; + } } else if (first == 'Z' || first == 'z') { // Zulu *offset = 0; - } else { - dp = nullptr; - } - } - return dp; -} - -const char* ParseZone(const char* dp, std::string* zone) { - zone->clear(); - if (dp != nullptr) { - while (*dp != '\0' && !std::isspace(*dp)) zone->push_back(*dp++); - if (zone->empty()) dp = nullptr; - } - return dp; -} - -const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) { - if (dp != nullptr) { - std::int_fast64_t v = 0; - std::int_fast64_t exp = 0; - const char* const bp = dp; - while (const char* cp = strchr(kDigits, *dp)) { - int d = static_cast<int>(cp - kDigits); - if (d >= 10) break; - if (exp < 15) { - exp += 1; - v *= 10; - v += d; - } - ++dp; - } - if (dp != bp) { - v *= kExp10[15 - exp]; - *subseconds = detail::femtoseconds(v); - } else { - dp = nullptr; - } - } - return dp; -} - -// Parses a string into a std::tm using strptime(3). -const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) { - if (dp != nullptr) { - dp = strptime(dp, fmt, tm); - } - return dp; -} - + } else { + dp = nullptr; + } + } + return dp; +} + +const char* ParseZone(const char* dp, std::string* zone) { + zone->clear(); + if (dp != nullptr) { + while (*dp != '\0' && !std::isspace(*dp)) zone->push_back(*dp++); + if (zone->empty()) dp = nullptr; + } + return dp; +} + +const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) { + if (dp != nullptr) { + std::int_fast64_t v = 0; + std::int_fast64_t exp = 0; + const char* const bp = dp; + while (const char* cp = strchr(kDigits, *dp)) { + int d = static_cast<int>(cp - kDigits); + if (d >= 10) break; + if (exp < 15) { + exp += 1; + v *= 10; + v += d; + } + ++dp; + } + if (dp != bp) { + v *= kExp10[15 - exp]; + *subseconds = detail::femtoseconds(v); + } else { + dp = nullptr; + } + } + return dp; +} + +// Parses a string into a std::tm using strptime(3). +const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) { + if (dp != nullptr) { + dp = strptime(dp, fmt, tm); + } + return dp; +} + // Sets year, tm_mon and tm_mday given the year, week_num, and tm_wday, // and the day on which weeks are defined to start. Returns false if year // would need to move outside its bounds. @@ -668,102 +668,102 @@ bool FromWeek(int week_num, weekday week_start, year_t* year, std::tm* tm) { return true; } -} // namespace - -// Uses strptime(3) to parse the given input. Supports the same extended -// format specifiers as format(), although %E#S and %E*S are treated +} // namespace + +// Uses strptime(3) to parse the given input. Supports the same extended +// format specifiers as format(), although %E#S and %E*S are treated // identically (and similarly for %E#f and %E*f). %Ez and %E*z also accept // the same inputs. %ET accepts either 'T' or 't'. -// -// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are -// handled internally so that we can normally avoid strptime() altogether -// (which is particularly helpful when the native implementation is broken). -// -// The TZ/GNU %s extension is handled internally because strptime() has to -// use localtime_r() to generate it, and that assumes the local time zone. -// -// We also handle the %z specifier to accommodate platforms that do not -// support the tm_gmtoff extension to std::tm. %Z is parsed but ignored. -bool parse(const std::string& format, const std::string& input, +// +// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are +// handled internally so that we can normally avoid strptime() altogether +// (which is particularly helpful when the native implementation is broken). +// +// The TZ/GNU %s extension is handled internally because strptime() has to +// use localtime_r() to generate it, and that assumes the local time zone. +// +// We also handle the %z specifier to accommodate platforms that do not +// support the tm_gmtoff extension to std::tm. %Z is parsed but ignored. +bool parse(const std::string& format, const std::string& input, const time_zone& tz, time_point<seconds>* sec, - detail::femtoseconds* fs, std::string* err) { - // The unparsed input. - const char* data = input.c_str(); // NUL terminated - - // Skips leading whitespace. - while (std::isspace(*data)) ++data; - - const year_t kyearmax = std::numeric_limits<year_t>::max(); - const year_t kyearmin = std::numeric_limits<year_t>::min(); - - // Sets default values for unspecified fields. - bool saw_year = false; - year_t year = 1970; - std::tm tm{}; - tm.tm_year = 1970 - 1900; - tm.tm_mon = 1 - 1; // Jan - tm.tm_mday = 1; - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - tm.tm_wday = 4; // Thu - tm.tm_yday = 0; - tm.tm_isdst = 0; - auto subseconds = detail::femtoseconds::zero(); - bool saw_offset = false; - int offset = 0; // No offset from passed tz. - std::string zone = "UTC"; - - const char* fmt = format.c_str(); // NUL terminated - bool twelve_hour = false; - bool afternoon = false; + detail::femtoseconds* fs, std::string* err) { + // The unparsed input. + const char* data = input.c_str(); // NUL terminated + + // Skips leading whitespace. + while (std::isspace(*data)) ++data; + + const year_t kyearmax = std::numeric_limits<year_t>::max(); + const year_t kyearmin = std::numeric_limits<year_t>::min(); + + // Sets default values for unspecified fields. + bool saw_year = false; + year_t year = 1970; + std::tm tm{}; + tm.tm_year = 1970 - 1900; + tm.tm_mon = 1 - 1; // Jan + tm.tm_mday = 1; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_wday = 4; // Thu + tm.tm_yday = 0; + tm.tm_isdst = 0; + auto subseconds = detail::femtoseconds::zero(); + bool saw_offset = false; + int offset = 0; // No offset from passed tz. + std::string zone = "UTC"; + + const char* fmt = format.c_str(); // NUL terminated + bool twelve_hour = false; + bool afternoon = false; int week_num = -1; weekday week_start = weekday::sunday; - - bool saw_percent_s = false; - std::int_fast64_t percent_s = 0; - - // Steps through format, one specifier at a time. - while (data != nullptr && *fmt != '\0') { - if (std::isspace(*fmt)) { - while (std::isspace(*data)) ++data; - while (std::isspace(*++fmt)) continue; - continue; - } - - if (*fmt != '%') { - if (*data == *fmt) { - ++data; - ++fmt; - } else { - data = nullptr; - } - continue; - } - - const char* percent = fmt; - if (*++fmt == '\0') { - data = nullptr; - continue; - } - switch (*fmt++) { - case 'Y': - // Symmetrically with FormatTime(), directly handing %Y avoids the - // tm.tm_year overflow problem. However, tm.tm_year will still be - // used by other specifiers like %D. - data = ParseInt(data, 0, kyearmin, kyearmax, &year); - if (data != nullptr) saw_year = true; - continue; - case 'm': - data = ParseInt(data, 2, 1, 12, &tm.tm_mon); - if (data != nullptr) tm.tm_mon -= 1; + + bool saw_percent_s = false; + std::int_fast64_t percent_s = 0; + + // Steps through format, one specifier at a time. + while (data != nullptr && *fmt != '\0') { + if (std::isspace(*fmt)) { + while (std::isspace(*data)) ++data; + while (std::isspace(*++fmt)) continue; + continue; + } + + if (*fmt != '%') { + if (*data == *fmt) { + ++data; + ++fmt; + } else { + data = nullptr; + } + continue; + } + + const char* percent = fmt; + if (*++fmt == '\0') { + data = nullptr; + continue; + } + switch (*fmt++) { + case 'Y': + // Symmetrically with FormatTime(), directly handing %Y avoids the + // tm.tm_year overflow problem. However, tm.tm_year will still be + // used by other specifiers like %D. + data = ParseInt(data, 0, kyearmin, kyearmax, &year); + if (data != nullptr) saw_year = true; + continue; + case 'm': + data = ParseInt(data, 2, 1, 12, &tm.tm_mon); + if (data != nullptr) tm.tm_mon -= 1; week_num = -1; - continue; - case 'd': + continue; + case 'd': case 'e': - data = ParseInt(data, 2, 1, 31, &tm.tm_mday); + data = ParseInt(data, 2, 1, 31, &tm.tm_mday); week_num = -1; - continue; + continue; case 'U': data = ParseInt(data, 0, 0, 53, &week_num); week_start = weekday::sunday; @@ -779,41 +779,41 @@ bool parse(const std::string& format, const std::string& input, case 'w': data = ParseInt(data, 0, 0, 6, &tm.tm_wday); continue; - case 'H': - data = ParseInt(data, 2, 0, 23, &tm.tm_hour); - twelve_hour = false; - continue; - case 'M': - data = ParseInt(data, 2, 0, 59, &tm.tm_min); - continue; - case 'S': - data = ParseInt(data, 2, 0, 60, &tm.tm_sec); - continue; - case 'I': - case 'l': - case 'r': // probably uses %I - twelve_hour = true; - break; - case 'R': // uses %H - case 'T': // uses %H - case 'c': // probably uses %H - case 'X': // probably uses %H - twelve_hour = false; - break; - case 'z': + case 'H': + data = ParseInt(data, 2, 0, 23, &tm.tm_hour); + twelve_hour = false; + continue; + case 'M': + data = ParseInt(data, 2, 0, 59, &tm.tm_min); + continue; + case 'S': + data = ParseInt(data, 2, 0, 60, &tm.tm_sec); + continue; + case 'I': + case 'l': + case 'r': // probably uses %I + twelve_hour = true; + break; + case 'R': // uses %H + case 'T': // uses %H + case 'c': // probably uses %H + case 'X': // probably uses %H + twelve_hour = false; + break; + case 'z': data = ParseOffset(data, "", &offset); - if (data != nullptr) saw_offset = true; - continue; - case 'Z': // ignored; zone abbreviations are ambiguous - data = ParseZone(data, &zone); - continue; - case 's': - data = ParseInt(data, 0, - std::numeric_limits<std::int_fast64_t>::min(), - std::numeric_limits<std::int_fast64_t>::max(), - &percent_s); - if (data != nullptr) saw_percent_s = true; - continue; + if (data != nullptr) saw_offset = true; + continue; + case 'Z': // ignored; zone abbreviations are ambiguous + data = ParseZone(data, &zone); + continue; + case 's': + data = ParseInt(data, 0, + std::numeric_limits<std::int_fast64_t>::min(), + std::numeric_limits<std::int_fast64_t>::max(), + &percent_s); + if (data != nullptr) saw_percent_s = true; + continue; case ':': if (fmt[0] == 'z' || (fmt[0] == ':' && @@ -827,148 +827,148 @@ bool parse(const std::string& format, const std::string& input, case '%': data = (*data == '%' ? data + 1 : nullptr); continue; - case 'E': + case 'E': if (fmt[0] == 'T') { if (*data == 'T' || *data == 't') { ++data; ++fmt; - } else { + } else { data = nullptr; - } + } continue; } if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) { data = ParseOffset(data, ":", &offset); - if (data != nullptr) saw_offset = true; + if (data != nullptr) saw_offset = true; fmt += (fmt[0] == 'z') ? 1 : 2; - continue; - } + continue; + } if (fmt[0] == '*' && fmt[1] == 'S') { - data = ParseInt(data, 2, 0, 60, &tm.tm_sec); - if (data != nullptr && *data == '.') { - data = ParseSubSeconds(data + 1, &subseconds); - } - fmt += 2; - continue; - } + data = ParseInt(data, 2, 0, 60, &tm.tm_sec); + if (data != nullptr && *data == '.') { + data = ParseSubSeconds(data + 1, &subseconds); + } + fmt += 2; + continue; + } if (fmt[0] == '*' && fmt[1] == 'f') { - if (data != nullptr && std::isdigit(*data)) { - data = ParseSubSeconds(data, &subseconds); - } - fmt += 2; - continue; - } + if (data != nullptr && std::isdigit(*data)) { + data = ParseSubSeconds(data, &subseconds); + } + fmt += 2; + continue; + } if (fmt[0] == '4' && fmt[1] == 'Y') { - const char* bp = data; - data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year); - if (data != nullptr) { - if (data - bp == 4) { - saw_year = true; - } else { - data = nullptr; // stopped too soon - } - } - fmt += 2; - continue; - } - if (std::isdigit(*fmt)) { - int n = 0; // value ignored - if (const char* np = ParseInt(fmt, 0, 0, 1024, &n)) { - if (*np == 'S') { - data = ParseInt(data, 2, 0, 60, &tm.tm_sec); - if (data != nullptr && *data == '.') { - data = ParseSubSeconds(data + 1, &subseconds); - } - fmt = ++np; - continue; - } - if (*np == 'f') { - if (data != nullptr && std::isdigit(*data)) { - data = ParseSubSeconds(data, &subseconds); - } - fmt = ++np; - continue; - } - } - } - if (*fmt == 'c') twelve_hour = false; // probably uses %H - if (*fmt == 'X') twelve_hour = false; // probably uses %H - if (*fmt != '\0') ++fmt; - break; - case 'O': - if (*fmt == 'H') twelve_hour = false; - if (*fmt == 'I') twelve_hour = true; - if (*fmt != '\0') ++fmt; - break; - } - - // Parses the current specifier. - const char* orig_data = data; - std::string spec(percent, static_cast<std::size_t>(fmt - percent)); - data = ParseTM(data, spec.c_str(), &tm); - - // If we successfully parsed %p we need to remember whether the result + const char* bp = data; + data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year); + if (data != nullptr) { + if (data - bp == 4) { + saw_year = true; + } else { + data = nullptr; // stopped too soon + } + } + fmt += 2; + continue; + } + if (std::isdigit(*fmt)) { + int n = 0; // value ignored + if (const char* np = ParseInt(fmt, 0, 0, 1024, &n)) { + if (*np == 'S') { + data = ParseInt(data, 2, 0, 60, &tm.tm_sec); + if (data != nullptr && *data == '.') { + data = ParseSubSeconds(data + 1, &subseconds); + } + fmt = ++np; + continue; + } + if (*np == 'f') { + if (data != nullptr && std::isdigit(*data)) { + data = ParseSubSeconds(data, &subseconds); + } + fmt = ++np; + continue; + } + } + } + if (*fmt == 'c') twelve_hour = false; // probably uses %H + if (*fmt == 'X') twelve_hour = false; // probably uses %H + if (*fmt != '\0') ++fmt; + break; + case 'O': + if (*fmt == 'H') twelve_hour = false; + if (*fmt == 'I') twelve_hour = true; + if (*fmt != '\0') ++fmt; + break; + } + + // Parses the current specifier. + const char* orig_data = data; + std::string spec(percent, static_cast<std::size_t>(fmt - percent)); + data = ParseTM(data, spec.c_str(), &tm); + + // If we successfully parsed %p we need to remember whether the result // was AM or PM so that we can adjust tm_hour before time_zone::lookup(). - // So reparse the input with a known AM hour, and check if it is shifted - // to a PM hour. - if (spec == "%p" && data != nullptr) { - std::string test_input = "1"; - test_input.append(orig_data, static_cast<std::size_t>(data - orig_data)); - const char* test_data = test_input.c_str(); - std::tm tmp{}; - ParseTM(test_data, "%I%p", &tmp); - afternoon = (tmp.tm_hour == 13); - } - } - - // Adjust a 12-hour tm_hour value if it should be in the afternoon. - if (twelve_hour && afternoon && tm.tm_hour < 12) { - tm.tm_hour += 12; - } - - if (data == nullptr) { - if (err != nullptr) *err = "Failed to parse input"; - return false; - } - - // Skip any remaining whitespace. - while (std::isspace(*data)) ++data; - - // parse() must consume the entire input string. - if (*data != '\0') { - if (err != nullptr) *err = "Illegal trailing data in input string"; - return false; - } - - // If we saw %s then we ignore anything else and return that time. - if (saw_percent_s) { - *sec = FromUnixSeconds(percent_s); - *fs = detail::femtoseconds::zero(); - return true; - } - + // So reparse the input with a known AM hour, and check if it is shifted + // to a PM hour. + if (spec == "%p" && data != nullptr) { + std::string test_input = "1"; + test_input.append(orig_data, static_cast<std::size_t>(data - orig_data)); + const char* test_data = test_input.c_str(); + std::tm tmp{}; + ParseTM(test_data, "%I%p", &tmp); + afternoon = (tmp.tm_hour == 13); + } + } + + // Adjust a 12-hour tm_hour value if it should be in the afternoon. + if (twelve_hour && afternoon && tm.tm_hour < 12) { + tm.tm_hour += 12; + } + + if (data == nullptr) { + if (err != nullptr) *err = "Failed to parse input"; + return false; + } + + // Skip any remaining whitespace. + while (std::isspace(*data)) ++data; + + // parse() must consume the entire input string. + if (*data != '\0') { + if (err != nullptr) *err = "Illegal trailing data in input string"; + return false; + } + + // If we saw %s then we ignore anything else and return that time. + if (saw_percent_s) { + *sec = FromUnixSeconds(percent_s); + *fs = detail::femtoseconds::zero(); + return true; + } + // If we saw %z, %Ez, or %E*z then we want to interpret the parsed fields // in UTC and then shift by that offset. Otherwise we want to interpret - // the fields directly in the passed time_zone. - time_zone ptz = saw_offset ? utc_time_zone() : tz; - - // Allows a leap second of 60 to normalize forward to the following ":00". - if (tm.tm_sec == 60) { - tm.tm_sec -= 1; - offset -= 1; - subseconds = detail::femtoseconds::zero(); - } - - if (!saw_year) { - year = year_t{tm.tm_year}; - if (year > kyearmax - 1900) { - // Platform-dependent, maybe unreachable. - if (err != nullptr) *err = "Out-of-range year"; - return false; - } - year += 1900; - } - + // the fields directly in the passed time_zone. + time_zone ptz = saw_offset ? utc_time_zone() : tz; + + // Allows a leap second of 60 to normalize forward to the following ":00". + if (tm.tm_sec == 60) { + tm.tm_sec -= 1; + offset -= 1; + subseconds = detail::femtoseconds::zero(); + } + + if (!saw_year) { + year = year_t{tm.tm_year}; + if (year > kyearmax - 1900) { + // Platform-dependent, maybe unreachable. + if (err != nullptr) *err = "Out-of-range year"; + return false; + } + year += 1900; + } + // Compute year, tm.tm_mon and tm.tm_mday if we parsed a week number. if (week_num != -1) { if (!FromWeek(week_num, week_start, &year, &tm)) { @@ -977,46 +977,46 @@ bool parse(const std::string& format, const std::string& input, } } - const int month = tm.tm_mon + 1; - civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); - - // parse() should not allow normalization. Due to the restricted field - // ranges above (see ParseInt()), the only possibility is for days to roll - // into months. That is, parsing "Sep 31" should not produce "Oct 1". - if (cs.month() != month || cs.day() != tm.tm_mday) { - if (err != nullptr) *err = "Out-of-range field"; - return false; - } - - // Accounts for the offset adjustment before converting to absolute time. - if ((offset < 0 && cs > civil_second::max() + offset) || - (offset > 0 && cs < civil_second::min() + offset)) { - if (err != nullptr) *err = "Out-of-range field"; - return false; - } - cs -= offset; - - const auto tp = ptz.lookup(cs).pre; - // Checks for overflow/underflow and returns an error as necessary. + const int month = tm.tm_mon + 1; + civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + + // parse() should not allow normalization. Due to the restricted field + // ranges above (see ParseInt()), the only possibility is for days to roll + // into months. That is, parsing "Sep 31" should not produce "Oct 1". + if (cs.month() != month || cs.day() != tm.tm_mday) { + if (err != nullptr) *err = "Out-of-range field"; + return false; + } + + // Accounts for the offset adjustment before converting to absolute time. + if ((offset < 0 && cs > civil_second::max() + offset) || + (offset > 0 && cs < civil_second::min() + offset)) { + if (err != nullptr) *err = "Out-of-range field"; + return false; + } + cs -= offset; + + const auto tp = ptz.lookup(cs).pre; + // Checks for overflow/underflow and returns an error as necessary. if (tp == time_point<seconds>::max()) { const auto al = ptz.lookup(time_point<seconds>::max()); - if (cs > al.cs) { - if (err != nullptr) *err = "Out-of-range field"; - return false; - } - } + if (cs > al.cs) { + if (err != nullptr) *err = "Out-of-range field"; + return false; + } + } if (tp == time_point<seconds>::min()) { const auto al = ptz.lookup(time_point<seconds>::min()); - if (cs < al.cs) { - if (err != nullptr) *err = "Out-of-range field"; - return false; - } - } - - *sec = tp; - *fs = subseconds; - return true; -} - -} // namespace detail -} // namespace cctz + if (cs < al.cs) { + if (err != nullptr) *err = "Out-of-range field"; + return false; + } + } + + *sec = tp; + *fs = subseconds; + return true; +} + +} // namespace detail +} // namespace cctz diff --git a/contrib/libs/cctz/src/time_zone_if.cc b/contrib/libs/cctz/src/time_zone_if.cc index a80dbcd60d..e2b5381e2c 100644 --- a/contrib/libs/cctz/src/time_zone_if.cc +++ b/contrib/libs/cctz/src/time_zone_if.cc @@ -1,36 +1,36 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#include "time_zone_if.h" -#include "time_zone_info.h" -#include "time_zone_libc.h" - -namespace cctz { - -std::unique_ptr<TimeZoneIf> TimeZoneIf::Load(const std::string& name) { - // Support "libc:localtime" and "libc:*" to access the legacy - // localtime and UTC support respectively from the C library. - if (name.compare(0, 5, "libc:") == 0) { - return std::unique_ptr<TimeZoneIf>(new TimeZoneLibC(name.substr(5))); - } - - // Otherwise use the "zoneinfo" implementation by default. - std::unique_ptr<TimeZoneInfo> tz(new TimeZoneInfo); - if (!tz->Load(name)) tz.reset(); - return std::unique_ptr<TimeZoneIf>(tz.release()); -} - +// +// 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. + +#include "time_zone_if.h" +#include "time_zone_info.h" +#include "time_zone_libc.h" + +namespace cctz { + +std::unique_ptr<TimeZoneIf> TimeZoneIf::Load(const std::string& name) { + // Support "libc:localtime" and "libc:*" to access the legacy + // localtime and UTC support respectively from the C library. + if (name.compare(0, 5, "libc:") == 0) { + return std::unique_ptr<TimeZoneIf>(new TimeZoneLibC(name.substr(5))); + } + + // Otherwise use the "zoneinfo" implementation by default. + std::unique_ptr<TimeZoneInfo> tz(new TimeZoneInfo); + if (!tz->Load(name)) tz.reset(); + return std::unique_ptr<TimeZoneIf>(tz.release()); +} + // Defined out-of-line to avoid emitting a weak vtable in all TUs. TimeZoneIf::~TimeZoneIf() {} @@ -42,4 +42,4 @@ std::int_fast64_t TimePointToUnixSeconds(const time_point<seconds>& tp) { return ToUnixSeconds(tp); } -} // namespace cctz +} // namespace cctz diff --git a/contrib/libs/cctz/src/time_zone_if.h b/contrib/libs/cctz/src/time_zone_if.h index f925c6c55a..c7d7f9ae69 100644 --- a/contrib/libs/cctz/src/time_zone_if.h +++ b/contrib/libs/cctz/src/time_zone_if.h @@ -1,68 +1,68 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#ifndef CCTZ_TIME_ZONE_IF_H_ -#define CCTZ_TIME_ZONE_IF_H_ - -#include <chrono> -#include <cstdint> -#include <memory> -#include <string> - +// +// 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. + +#ifndef CCTZ_TIME_ZONE_IF_H_ +#define CCTZ_TIME_ZONE_IF_H_ + +#include <chrono> +#include <cstdint> +#include <memory> +#include <string> + #include "cctz/civil_time.h" #include "cctz/time_zone.h" - -namespace cctz { - -// A simple interface used to hide time-zone complexities from time_zone::Impl. -// Subclasses implement the functions for civil-time conversions in the zone. -class TimeZoneIf { - public: - // A factory function for TimeZoneIf implementations. - static std::unique_ptr<TimeZoneIf> Load(const std::string& name); - + +namespace cctz { + +// A simple interface used to hide time-zone complexities from time_zone::Impl. +// Subclasses implement the functions for civil-time conversions in the zone. +class TimeZoneIf { + public: + // A factory function for TimeZoneIf implementations. + static std::unique_ptr<TimeZoneIf> Load(const std::string& name); + virtual ~TimeZoneIf(); - - virtual time_zone::absolute_lookup BreakTime( + + virtual time_zone::absolute_lookup BreakTime( const time_point<seconds>& tp) const = 0; - virtual time_zone::civil_lookup MakeTime( - const civil_second& cs) const = 0; - + virtual time_zone::civil_lookup MakeTime( + const civil_second& cs) const = 0; + virtual bool NextTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const = 0; virtual bool PrevTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const = 0; virtual std::string Version() const = 0; - virtual std::string Description() const = 0; - - protected: - TimeZoneIf() {} -}; - + virtual std::string Description() const = 0; + + protected: + TimeZoneIf() {} +}; + // Convert between time_point<seconds> and a count of seconds since the // Unix epoch. We assume that the std::chrono::system_clock and the -// Unix clock are second aligned, but not that they share an epoch. +// Unix clock are second aligned, but not that they share an epoch. inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) { return (tp - std::chrono::time_point_cast<seconds>( std::chrono::system_clock::from_time_t(0))).count(); -} +} inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) { return std::chrono::time_point_cast<seconds>( std::chrono::system_clock::from_time_t(0)) + seconds(t); -} - -} // namespace cctz - -#endif // CCTZ_TIME_ZONE_IF_H_ +} + +} // namespace cctz + +#endif // CCTZ_TIME_ZONE_IF_H_ diff --git a/contrib/libs/cctz/src/time_zone_impl.cc b/contrib/libs/cctz/src/time_zone_impl.cc index 6e077505c1..1df9d33a3f 100644 --- a/contrib/libs/cctz/src/time_zone_impl.cc +++ b/contrib/libs/cctz/src/time_zone_impl.cc @@ -1,90 +1,90 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#include "time_zone_impl.h" - +// +// 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. + +#include "time_zone_impl.h" + #include <deque> #include <memory> -#include <mutex> -#include <string> -#include <unordered_map> -#include <utility> - -#include "time_zone_fixed.h" - -namespace cctz { - -namespace { - -// time_zone::Impls are linked into a map to support fast lookup by name. -using TimeZoneImplByName = - std::unordered_map<std::string, const time_zone::Impl*>; -TimeZoneImplByName* time_zone_map = nullptr; - -// Mutual exclusion for time_zone_map. +#include <mutex> +#include <string> +#include <unordered_map> +#include <utility> + +#include "time_zone_fixed.h" + +namespace cctz { + +namespace { + +// time_zone::Impls are linked into a map to support fast lookup by name. +using TimeZoneImplByName = + std::unordered_map<std::string, const time_zone::Impl*>; +TimeZoneImplByName* time_zone_map = nullptr; + +// Mutual exclusion for time_zone_map. std::mutex& TimeZoneMutex() { // This mutex is intentionally "leaked" to avoid the static deinitialization // order fiasco (std::mutex's destructor is not trivial on many platforms). static std::mutex* time_zone_mutex = new std::mutex; return *time_zone_mutex; } - -} // namespace - -time_zone time_zone::Impl::UTC() { - return time_zone(UTCImpl()); -} - -bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { + +} // namespace + +time_zone time_zone::Impl::UTC() { + return time_zone(UTCImpl()); +} + +bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) { const Impl* const utc_impl = UTCImpl(); - + // Check for UTC (which is never a key in time_zone_map). auto offset = seconds::zero(); if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) { - *tz = time_zone(utc_impl); - return true; - } - + *tz = time_zone(utc_impl); + return true; + } + // Check whether the time zone has already been loaded. - { + { std::lock_guard<std::mutex> lock(TimeZoneMutex()); - if (time_zone_map != nullptr) { - TimeZoneImplByName::const_iterator itr = time_zone_map->find(name); - if (itr != time_zone_map->end()) { - *tz = time_zone(itr->second); - return itr->second != utc_impl; - } - } - } - + if (time_zone_map != nullptr) { + TimeZoneImplByName::const_iterator itr = time_zone_map->find(name); + if (itr != time_zone_map->end()) { + *tz = time_zone(itr->second); + return itr->second != utc_impl; + } + } + } + // Load the new time zone (outside the lock). std::unique_ptr<const Impl> new_impl(new Impl(name)); // Add the new time zone to the map. std::lock_guard<std::mutex> lock(TimeZoneMutex()); - if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName; - const Impl*& impl = (*time_zone_map)[name]; + if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName; + const Impl*& impl = (*time_zone_map)[name]; if (impl == nullptr) { // this thread won any load race impl = new_impl->zone_ ? new_impl.release() : utc_impl; - } - *tz = time_zone(impl); - return impl != utc_impl; -} - -void time_zone::Impl::ClearTimeZoneMapTestOnly() { + } + *tz = time_zone(impl); + return impl != utc_impl; +} + +void time_zone::Impl::ClearTimeZoneMapTestOnly() { std::lock_guard<std::mutex> lock(TimeZoneMutex()); - if (time_zone_map != nullptr) { + if (time_zone_map != nullptr) { // Existing time_zone::Impl* entries are in the wild, so we can't delete // them. Instead, we move them to a private container, where they are // logically unreachable but not "leaked". Future requests will result @@ -93,16 +93,16 @@ void time_zone::Impl::ClearTimeZoneMapTestOnly() { for (const auto& element : *time_zone_map) { cleared->push_back(element.second); } - time_zone_map->clear(); - } -} - + time_zone_map->clear(); + } +} + time_zone::Impl::Impl(const std::string& name) : name_(name), zone_(TimeZoneIf::Load(name_)) {} - -const time_zone::Impl* time_zone::Impl::UTCImpl() { + +const time_zone::Impl* time_zone::Impl::UTCImpl() { static const Impl* utc_impl = new Impl("UTC"); // never fails - return utc_impl; -} - -} // namespace cctz + return utc_impl; +} + +} // namespace cctz diff --git a/contrib/libs/cctz/src/time_zone_impl.h b/contrib/libs/cctz/src/time_zone_impl.h index 23fcddb690..00341c4d82 100644 --- a/contrib/libs/cctz/src/time_zone_impl.h +++ b/contrib/libs/cctz/src/time_zone_impl.h @@ -1,86 +1,86 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#ifndef CCTZ_TIME_ZONE_IMPL_H_ -#define CCTZ_TIME_ZONE_IMPL_H_ - -#include <memory> -#include <string> - +// +// 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. + +#ifndef CCTZ_TIME_ZONE_IMPL_H_ +#define CCTZ_TIME_ZONE_IMPL_H_ + +#include <memory> +#include <string> + #include "cctz/civil_time.h" #include "cctz/time_zone.h" -#include "time_zone_if.h" -#include "time_zone_info.h" - -namespace cctz { - -// time_zone::Impl is the internal object referenced by a cctz::time_zone. -class time_zone::Impl { - public: - // The UTC time zone. Also used for other time zones that fail to load. - static time_zone UTC(); - - // Load a named time zone. Returns false if the name is invalid, or if - // some other kind of error occurs. Note that loading "UTC" never fails. - static bool LoadTimeZone(const std::string& name, time_zone* tz); - - // Clears the map of cached time zones. Primarily for use in benchmarks - // that gauge the performance of loading/parsing the time-zone data. - static void ClearTimeZoneMapTestOnly(); - - // The primary key is the time-zone ID (e.g., "America/New_York"). +#include "time_zone_if.h" +#include "time_zone_info.h" + +namespace cctz { + +// time_zone::Impl is the internal object referenced by a cctz::time_zone. +class time_zone::Impl { + public: + // The UTC time zone. Also used for other time zones that fail to load. + static time_zone UTC(); + + // Load a named time zone. Returns false if the name is invalid, or if + // some other kind of error occurs. Note that loading "UTC" never fails. + static bool LoadTimeZone(const std::string& name, time_zone* tz); + + // Clears the map of cached time zones. Primarily for use in benchmarks + // that gauge the performance of loading/parsing the time-zone data. + static void ClearTimeZoneMapTestOnly(); + + // The primary key is the time-zone ID (e.g., "America/New_York"). const std::string& Name() const { // TODO: It would nice if the zoneinfo data included the zone name. return name_; } - - // Breaks a time_point down to civil-time components in this time zone. + + // Breaks a time_point down to civil-time components in this time zone. time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const { - return zone_->BreakTime(tp); - } - - // Converts the civil-time components in this time zone into a time_point. - // That is, the opposite of BreakTime(). The requested civil time may be - // ambiguous or illegal due to a change of UTC offset. - time_zone::civil_lookup MakeTime(const civil_second& cs) const { - return zone_->MakeTime(cs); - } - - // Finds the time of the next/previous offset change in this time zone. + return zone_->BreakTime(tp); + } + + // Converts the civil-time components in this time zone into a time_point. + // That is, the opposite of BreakTime(). The requested civil time may be + // ambiguous or illegal due to a change of UTC offset. + time_zone::civil_lookup MakeTime(const civil_second& cs) const { + return zone_->MakeTime(cs); + } + + // Finds the time of the next/previous offset change in this time zone. bool NextTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const { return zone_->NextTransition(tp, trans); - } + } bool PrevTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const { return zone_->PrevTransition(tp, trans); - } - + } + // Returns an implementation-defined version string for this time zone. std::string Version() const { return zone_->Version(); } // Returns an implementation-defined description of this time zone. std::string Description() const { return zone_->Description(); } - private: - explicit Impl(const std::string& name); - static const Impl* UTCImpl(); - - const std::string name_; - std::unique_ptr<TimeZoneIf> zone_; -}; - -} // namespace cctz - -#endif // CCTZ_TIME_ZONE_IMPL_H_ + private: + explicit Impl(const std::string& name); + static const Impl* UTCImpl(); + + const std::string name_; + std::unique_ptr<TimeZoneIf> zone_; +}; + +} // namespace cctz + +#endif // CCTZ_TIME_ZONE_IMPL_H_ diff --git a/contrib/libs/cctz/src/time_zone_info.cc b/contrib/libs/cctz/src/time_zone_info.cc index 1b3dc15cf1..17ca9e50f2 100644 --- a/contrib/libs/cctz/src/time_zone_info.cc +++ b/contrib/libs/cctz/src/time_zone_info.cc @@ -1,83 +1,83 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -// This file implements the TimeZoneIf interface using the "zoneinfo" -// data provided by the IANA Time Zone Database (i.e., the only real game -// in town). -// -// TimeZoneInfo represents the history of UTC-offset changes within a time -// zone. Most changes are due to daylight-saving rules, but occasionally -// shifts are made to the time-zone's base offset. The database only attempts -// to be definitive for times since 1970, so be wary of local-time conversions -// before that. Also, rule and zone-boundary changes are made at the whim -// of governments, so the conversion of future times needs to be taken with -// a grain of salt. -// -// For more information see tzfile(5), http://www.iana.org/time-zones, or +// +// 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. + +// This file implements the TimeZoneIf interface using the "zoneinfo" +// data provided by the IANA Time Zone Database (i.e., the only real game +// in town). +// +// TimeZoneInfo represents the history of UTC-offset changes within a time +// zone. Most changes are due to daylight-saving rules, but occasionally +// shifts are made to the time-zone's base offset. The database only attempts +// to be definitive for times since 1970, so be wary of local-time conversions +// before that. Also, rule and zone-boundary changes are made at the whim +// of governments, so the conversion of future times needs to be taken with +// a grain of salt. +// +// For more information see tzfile(5), http://www.iana.org/time-zones, or // https://en.wikipedia.org/wiki/Zoneinfo. -// -// Note that we assume the proleptic Gregorian calendar and 60-second -// minutes throughout. - -#include "time_zone_info.h" - -#include <algorithm> -#include <cassert> -#include <chrono> -#include <cstdint> -#include <cstdio> -#include <cstdlib> -#include <cstring> -#include <functional> -#include <memory> -#include <sstream> -#include <string> - +// +// Note that we assume the proleptic Gregorian calendar and 60-second +// minutes throughout. + +#include "time_zone_info.h" + +#include <algorithm> +#include <cassert> +#include <chrono> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <functional> +#include <memory> +#include <sstream> +#include <string> + #include "cctz/civil_time.h" -#include "time_zone_fixed.h" -#include "time_zone_posix.h" - -namespace cctz { - -namespace { - +#include "time_zone_fixed.h" +#include "time_zone_posix.h" + +namespace cctz { + +namespace { + inline bool IsLeap(year_t year) { - return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); -} - -// The number of days in non-leap and leap years respectively. -const std::int_least32_t kDaysPerYear[2] = {365, 366}; - -// The day offsets of the beginning of each (1-based) month in non-leap and -// leap years respectively (e.g., 335 days before December in a leap year). -const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = { - {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, - {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, -}; - -// We reject leap-second encoded zoneinfo and so assume 60-second minutes. -const std::int_least32_t kSecsPerDay = 24 * 60 * 60; - -// 400-year chunks always have 146097 days (20871 weeks). -const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay; - -// Like kDaysPerYear[] but scaled up by a factor of kSecsPerDay. -const std::int_least32_t kSecsPerYear[2] = { - 365 * kSecsPerDay, - 366 * kSecsPerDay, -}; - + return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); +} + +// The number of days in non-leap and leap years respectively. +const std::int_least32_t kDaysPerYear[2] = {365, 366}; + +// The day offsets of the beginning of each (1-based) month in non-leap and +// leap years respectively (e.g., 335 days before December in a leap year). +const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = { + {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, + {-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}, +}; + +// We reject leap-second encoded zoneinfo and so assume 60-second minutes. +const std::int_least32_t kSecsPerDay = 24 * 60 * 60; + +// 400-year chunks always have 146097 days (20871 weeks). +const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay; + +// Like kDaysPerYear[] but scaled up by a factor of kSecsPerDay. +const std::int_least32_t kSecsPerYear[2] = { + 365 * kSecsPerDay, + 366 * kSecsPerDay, +}; + // Convert a cctz::weekday to a POSIX TZ weekday number (0==Sun, ..., 6=Sat). inline int ToPosixWeekday(weekday wd) { switch (wd) { @@ -99,195 +99,195 @@ inline int ToPosixWeekday(weekday wd) { return 0; /*NOTREACHED*/ } -// Single-byte, unsigned numeric values are encoded directly. -inline std::uint_fast8_t Decode8(const char* cp) { - return static_cast<std::uint_fast8_t>(*cp) & 0xff; -} - -// Multi-byte, numeric values are encoded using a MSB first, -// twos-complement representation. These helpers decode, from -// the given address, 4-byte and 8-byte values respectively. -// Note: If int_fastXX_t == intXX_t and this machine is not -// twos complement, then there will be at least one input value -// we cannot represent. -std::int_fast32_t Decode32(const char* cp) { - std::uint_fast32_t v = 0; - for (int i = 0; i != (32 / 8); ++i) v = (v << 8) | Decode8(cp++); - const std::int_fast32_t s32max = 0x7fffffff; - const auto s32maxU = static_cast<std::uint_fast32_t>(s32max); - if (v <= s32maxU) return static_cast<std::int_fast32_t>(v); - return static_cast<std::int_fast32_t>(v - s32maxU - 1) - s32max - 1; -} - -std::int_fast64_t Decode64(const char* cp) { - std::uint_fast64_t v = 0; - for (int i = 0; i != (64 / 8); ++i) v = (v << 8) | Decode8(cp++); - const std::int_fast64_t s64max = 0x7fffffffffffffff; - const auto s64maxU = static_cast<std::uint_fast64_t>(s64max); - if (v <= s64maxU) return static_cast<std::int_fast64_t>(v); - return static_cast<std::int_fast64_t>(v - s64maxU - 1) - s64max - 1; -} - -// Generate a year-relative offset for a PosixTransition. -std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday, - const PosixTransition& pt) { - std::int_fast64_t days = 0; - switch (pt.date.fmt) { - case PosixTransition::J: { - days = pt.date.j.day; - if (!leap_year || days < kMonthOffsets[1][3]) days -= 1; - break; - } - case PosixTransition::N: { - days = pt.date.n.day; - break; - } - case PosixTransition::M: { - const bool last_week = (pt.date.m.week == 5); - days = kMonthOffsets[leap_year][pt.date.m.month + last_week]; - const std::int_fast64_t weekday = (jan1_weekday + days) % 7; - if (last_week) { - days -= (weekday + 7 - 1 - pt.date.m.weekday) % 7 + 1; - } else { - days += (pt.date.m.weekday + 7 - weekday) % 7; - days += (pt.date.m.week - 1) * 7; - } - break; - } - } - return (days * kSecsPerDay) + pt.time.offset; -} - +// Single-byte, unsigned numeric values are encoded directly. +inline std::uint_fast8_t Decode8(const char* cp) { + return static_cast<std::uint_fast8_t>(*cp) & 0xff; +} + +// Multi-byte, numeric values are encoded using a MSB first, +// twos-complement representation. These helpers decode, from +// the given address, 4-byte and 8-byte values respectively. +// Note: If int_fastXX_t == intXX_t and this machine is not +// twos complement, then there will be at least one input value +// we cannot represent. +std::int_fast32_t Decode32(const char* cp) { + std::uint_fast32_t v = 0; + for (int i = 0; i != (32 / 8); ++i) v = (v << 8) | Decode8(cp++); + const std::int_fast32_t s32max = 0x7fffffff; + const auto s32maxU = static_cast<std::uint_fast32_t>(s32max); + if (v <= s32maxU) return static_cast<std::int_fast32_t>(v); + return static_cast<std::int_fast32_t>(v - s32maxU - 1) - s32max - 1; +} + +std::int_fast64_t Decode64(const char* cp) { + std::uint_fast64_t v = 0; + for (int i = 0; i != (64 / 8); ++i) v = (v << 8) | Decode8(cp++); + const std::int_fast64_t s64max = 0x7fffffffffffffff; + const auto s64maxU = static_cast<std::uint_fast64_t>(s64max); + if (v <= s64maxU) return static_cast<std::int_fast64_t>(v); + return static_cast<std::int_fast64_t>(v - s64maxU - 1) - s64max - 1; +} + +// Generate a year-relative offset for a PosixTransition. +std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday, + const PosixTransition& pt) { + std::int_fast64_t days = 0; + switch (pt.date.fmt) { + case PosixTransition::J: { + days = pt.date.j.day; + if (!leap_year || days < kMonthOffsets[1][3]) days -= 1; + break; + } + case PosixTransition::N: { + days = pt.date.n.day; + break; + } + case PosixTransition::M: { + const bool last_week = (pt.date.m.week == 5); + days = kMonthOffsets[leap_year][pt.date.m.month + last_week]; + const std::int_fast64_t weekday = (jan1_weekday + days) % 7; + if (last_week) { + days -= (weekday + 7 - 1 - pt.date.m.weekday) % 7 + 1; + } else { + days += (pt.date.m.weekday + 7 - weekday) % 7; + days += (pt.date.m.week - 1) * 7; + } + break; + } + } + return (days * kSecsPerDay) + pt.time.offset; +} + inline time_zone::civil_lookup MakeUnique(const time_point<seconds>& tp) { - time_zone::civil_lookup cl; - cl.kind = time_zone::civil_lookup::UNIQUE; - cl.pre = cl.trans = cl.post = tp; - return cl; -} - -inline time_zone::civil_lookup MakeUnique(std::int_fast64_t unix_time) { - return MakeUnique(FromUnixSeconds(unix_time)); -} - -inline time_zone::civil_lookup MakeSkipped(const Transition& tr, - const civil_second& cs) { - time_zone::civil_lookup cl; - cl.kind = time_zone::civil_lookup::SKIPPED; - cl.pre = FromUnixSeconds(tr.unix_time - 1 + (cs - tr.prev_civil_sec)); - cl.trans = FromUnixSeconds(tr.unix_time); - cl.post = FromUnixSeconds(tr.unix_time - (tr.civil_sec - cs)); - return cl; -} - -inline time_zone::civil_lookup MakeRepeated(const Transition& tr, - const civil_second& cs) { - time_zone::civil_lookup cl; - cl.kind = time_zone::civil_lookup::REPEATED; - cl.pre = FromUnixSeconds(tr.unix_time - 1 - (tr.prev_civil_sec - cs)); - cl.trans = FromUnixSeconds(tr.unix_time); - cl.post = FromUnixSeconds(tr.unix_time + (cs - tr.civil_sec)); - return cl; -} - + time_zone::civil_lookup cl; + cl.kind = time_zone::civil_lookup::UNIQUE; + cl.pre = cl.trans = cl.post = tp; + return cl; +} + +inline time_zone::civil_lookup MakeUnique(std::int_fast64_t unix_time) { + return MakeUnique(FromUnixSeconds(unix_time)); +} + +inline time_zone::civil_lookup MakeSkipped(const Transition& tr, + const civil_second& cs) { + time_zone::civil_lookup cl; + cl.kind = time_zone::civil_lookup::SKIPPED; + cl.pre = FromUnixSeconds(tr.unix_time - 1 + (cs - tr.prev_civil_sec)); + cl.trans = FromUnixSeconds(tr.unix_time); + cl.post = FromUnixSeconds(tr.unix_time - (tr.civil_sec - cs)); + return cl; +} + +inline time_zone::civil_lookup MakeRepeated(const Transition& tr, + const civil_second& cs) { + time_zone::civil_lookup cl; + cl.kind = time_zone::civil_lookup::REPEATED; + cl.pre = FromUnixSeconds(tr.unix_time - 1 - (tr.prev_civil_sec - cs)); + cl.trans = FromUnixSeconds(tr.unix_time); + cl.post = FromUnixSeconds(tr.unix_time + (cs - tr.civil_sec)); + return cl; +} + inline civil_second YearShift(const civil_second& cs, year_t shift) { - return civil_second(cs.year() + shift, cs.month(), cs.day(), - cs.hour(), cs.minute(), cs.second()); -} - -} // namespace - -// What (no leap-seconds) UTC+seconds zoneinfo would look like. + return civil_second(cs.year() + shift, cs.month(), cs.day(), + cs.hour(), cs.minute(), cs.second()); +} + +} // namespace + +// What (no leap-seconds) UTC+seconds zoneinfo would look like. bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) { - transition_types_.resize(1); - TransitionType& tt(transition_types_.back()); - tt.utc_offset = static_cast<std::int_least32_t>(offset.count()); - tt.is_dst = false; - tt.abbr_index = 0; - + transition_types_.resize(1); + TransitionType& tt(transition_types_.back()); + tt.utc_offset = static_cast<std::int_least32_t>(offset.count()); + tt.is_dst = false; + tt.abbr_index = 0; + // We temporarily add some redundant, contemporary (2015 through 2025) - // transitions for performance reasons. See TimeZoneInfo::LocalTime(). - // TODO: Fix the performance issue and remove the extra transitions. - transitions_.clear(); - transitions_.reserve(12); - for (const std::int_fast64_t unix_time : { + // transitions for performance reasons. See TimeZoneInfo::LocalTime(). + // TODO: Fix the performance issue and remove the extra transitions. + transitions_.clear(); + transitions_.reserve(12); + for (const std::int_fast64_t unix_time : { -(1LL << 59), // a "first half" transition - 1420070400LL, // 2015-01-01T00:00:00+00:00 - 1451606400LL, // 2016-01-01T00:00:00+00:00 - 1483228800LL, // 2017-01-01T00:00:00+00:00 - 1514764800LL, // 2018-01-01T00:00:00+00:00 - 1546300800LL, // 2019-01-01T00:00:00+00:00 - 1577836800LL, // 2020-01-01T00:00:00+00:00 - 1609459200LL, // 2021-01-01T00:00:00+00:00 + 1420070400LL, // 2015-01-01T00:00:00+00:00 + 1451606400LL, // 2016-01-01T00:00:00+00:00 + 1483228800LL, // 2017-01-01T00:00:00+00:00 + 1514764800LL, // 2018-01-01T00:00:00+00:00 + 1546300800LL, // 2019-01-01T00:00:00+00:00 + 1577836800LL, // 2020-01-01T00:00:00+00:00 + 1609459200LL, // 2021-01-01T00:00:00+00:00 1640995200LL, // 2022-01-01T00:00:00+00:00 1672531200LL, // 2023-01-01T00:00:00+00:00 1704067200LL, // 2024-01-01T00:00:00+00:00 1735689600LL, // 2025-01-01T00:00:00+00:00 - }) { - Transition& tr(*transitions_.emplace(transitions_.end())); - tr.unix_time = unix_time; - tr.type_index = 0; - tr.civil_sec = LocalTime(tr.unix_time, tt).cs; - tr.prev_civil_sec = tr.civil_sec - 1; - } - - default_transition_type_ = 0; - abbreviations_ = FixedOffsetToAbbr(offset); + }) { + Transition& tr(*transitions_.emplace(transitions_.end())); + tr.unix_time = unix_time; + tr.type_index = 0; + tr.civil_sec = LocalTime(tr.unix_time, tt).cs; + tr.prev_civil_sec = tr.civil_sec - 1; + } + + default_transition_type_ = 0; + abbreviations_ = FixedOffsetToAbbr(offset); abbreviations_.append(1, '\0'); - future_spec_.clear(); // never needed for a fixed-offset zone - extended_ = false; - + future_spec_.clear(); // never needed for a fixed-offset zone + extended_ = false; + tt.civil_max = LocalTime(seconds::max().count(), tt).cs; tt.civil_min = LocalTime(seconds::min().count(), tt).cs; - - transitions_.shrink_to_fit(); - return true; -} - -// Builds the in-memory header using the raw bytes from the file. -bool TimeZoneInfo::Header::Build(const tzhead& tzh) { - std::int_fast32_t v; - if ((v = Decode32(tzh.tzh_timecnt)) < 0) return false; - timecnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_typecnt)) < 0) return false; - typecnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_charcnt)) < 0) return false; - charcnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_leapcnt)) < 0) return false; - leapcnt = static_cast<std::size_t>(v); - if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false; - ttisstdcnt = static_cast<std::size_t>(v); + + transitions_.shrink_to_fit(); + return true; +} + +// Builds the in-memory header using the raw bytes from the file. +bool TimeZoneInfo::Header::Build(const tzhead& tzh) { + std::int_fast32_t v; + if ((v = Decode32(tzh.tzh_timecnt)) < 0) return false; + timecnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_typecnt)) < 0) return false; + typecnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_charcnt)) < 0) return false; + charcnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_leapcnt)) < 0) return false; + leapcnt = static_cast<std::size_t>(v); + if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false; + ttisstdcnt = static_cast<std::size_t>(v); if ((v = Decode32(tzh.tzh_ttisutcnt)) < 0) return false; ttisutcnt = static_cast<std::size_t>(v); - return true; -} - -// How many bytes of data are associated with this header. The result -// depends upon whether this is a section with 4-byte or 8-byte times. -std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const { - std::size_t len = 0; - len += (time_len + 1) * timecnt; // unix_time + type_index - len += (4 + 1 + 1) * typecnt; // utc_offset + is_dst + abbr_index - len += 1 * charcnt; // abbreviations - len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC - len += 1 * ttisstdcnt; // UTC/local indicators + return true; +} + +// How many bytes of data are associated with this header. The result +// depends upon whether this is a section with 4-byte or 8-byte times. +std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const { + std::size_t len = 0; + len += (time_len + 1) * timecnt; // unix_time + type_index + len += (4 + 1 + 1) * typecnt; // utc_offset + is_dst + abbr_index + len += 1 * charcnt; // abbreviations + len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC + len += 1 * ttisstdcnt; // UTC/local indicators len += 1 * ttisutcnt; // standard/wall indicators - return len; -} - -// zic(8) can generate no-op transitions when a zone changes rules at an -// instant when there is actually no discontinuity. So we check whether -// two transitions have equivalent types (same offset/is_dst/abbr). -bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index, - std::uint_fast8_t tt2_index) const { - if (tt1_index == tt2_index) return true; - const TransitionType& tt1(transition_types_[tt1_index]); - const TransitionType& tt2(transition_types_[tt2_index]); + return len; +} + +// zic(8) can generate no-op transitions when a zone changes rules at an +// instant when there is actually no discontinuity. So we check whether +// two transitions have equivalent types (same offset/is_dst/abbr). +bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index, + std::uint_fast8_t tt2_index) const { + if (tt1_index == tt2_index) return true; + const TransitionType& tt1(transition_types_[tt1_index]); + const TransitionType& tt2(transition_types_[tt2_index]); if (tt1.utc_offset != tt2.utc_offset) return false; - if (tt1.is_dst != tt2.is_dst) return false; - if (tt1.abbr_index != tt2.abbr_index) return false; - return true; -} - + if (tt1.is_dst != tt2.is_dst) return false; + if (tt1.abbr_index != tt2.abbr_index) return false; + return true; +} + // Find/make a transition type with these attributes. bool TimeZoneInfo::GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, const std::string& abbr, @@ -320,38 +320,38 @@ bool TimeZoneInfo::GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, return true; } -// Use the POSIX-TZ-environment-variable-style string to handle times -// in years after the last transition stored in the zoneinfo data. +// Use the POSIX-TZ-environment-variable-style string to handle times +// in years after the last transition stored in the zoneinfo data. bool TimeZoneInfo::ExtendTransitions() { - extended_ = false; + extended_ = false; if (future_spec_.empty()) return true; // last transition prevails - - PosixTimeZone posix; + + PosixTimeZone posix; if (!ParsePosixSpec(future_spec_, &posix)) return false; - + // Find transition type for the future std specification. std::uint_least8_t std_ti; if (!GetTransitionType(posix.std_offset, false, posix.std_abbr, &std_ti)) return false; - + if (posix.dst_abbr.empty()) { // std only // The future specification should match the last transition, and // that means that handling the future will fall out naturally. return EquivTransitions(transitions_.back().type_index, std_ti); - } - + } + // Find transition type for the future dst specification. std::uint_least8_t dst_ti; if (!GetTransitionType(posix.dst_offset, true, posix.dst_abbr, &dst_ti)) return false; - - // Extend the transitions for an additional 400 years using the - // future specification. Years beyond those can be handled by - // mapping back to a cycle-equivalent year within that range. + + // Extend the transitions for an additional 400 years using the + // future specification. Years beyond those can be handled by + // mapping back to a cycle-equivalent year within that range. // We may need two additional transitions for the current year. transitions_.reserve(transitions_.size() + 400 * 2 + 2); - extended_ = true; - + extended_ = true; + const Transition& last(transitions_.back()); const std::int_fast64_t last_time = last.unix_time; const TransitionType& last_tt(transition_types_[last.type_index]); @@ -360,7 +360,7 @@ bool TimeZoneInfo::ExtendTransitions() { const civil_second jan1(last_year_); std::int_fast64_t jan1_time = jan1 - civil_second(); int jan1_weekday = ToPosixWeekday(get_weekday(jan1)); - + Transition dst = {0, dst_ti, civil_second(), civil_second()}; Transition std = {0, std_ti, civil_second(), civil_second()}; for (const year_t limit = last_year_ + 400;; ++last_year_) { @@ -375,146 +375,146 @@ bool TimeZoneInfo::ExtendTransitions() { transitions_.push_back(*tb); } if (last_year_ == limit) break; - jan1_time += kSecsPerYear[leap_year]; - jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7; + jan1_time += kSecsPerYear[leap_year]; + jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7; leap_year = !leap_year && IsLeap(last_year_ + 1); - } + } return true; -} - +} + bool TimeZoneInfo::Load(ZoneInfoSource* zip) { - // Read and validate the header. - tzhead tzh; - if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) - return false; - if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) - return false; - Header hdr; - if (!hdr.Build(tzh)) - return false; - std::size_t time_len = 4; - if (tzh.tzh_version[0] != '\0') { - // Skip the 4-byte data. - if (zip->Skip(hdr.DataLength(time_len)) != 0) - return false; - // Read and validate the header for the 8-byte data. - if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) - return false; - if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) - return false; - if (tzh.tzh_version[0] == '\0') - return false; - if (!hdr.Build(tzh)) - return false; - time_len = 8; - } - if (hdr.typecnt == 0) - return false; - if (hdr.leapcnt != 0) { - // This code assumes 60-second minutes so we do not want - // the leap-second encoded zoneinfo. We could reverse the - // compensation, but the "right" encoding is rarely used - // so currently we simply reject such data. - return false; - } - if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) - return false; + // Read and validate the header. + tzhead tzh; + if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) + return false; + if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) + return false; + Header hdr; + if (!hdr.Build(tzh)) + return false; + std::size_t time_len = 4; + if (tzh.tzh_version[0] != '\0') { + // Skip the 4-byte data. + if (zip->Skip(hdr.DataLength(time_len)) != 0) + return false; + // Read and validate the header for the 8-byte data. + if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) + return false; + if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0) + return false; + if (tzh.tzh_version[0] == '\0') + return false; + if (!hdr.Build(tzh)) + return false; + time_len = 8; + } + if (hdr.typecnt == 0) + return false; + if (hdr.leapcnt != 0) { + // This code assumes 60-second minutes so we do not want + // the leap-second encoded zoneinfo. We could reverse the + // compensation, but the "right" encoding is rarely used + // so currently we simply reject such data. + return false; + } + if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) + return false; if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) - return false; - - // Read the data into a local buffer. - std::size_t len = hdr.DataLength(time_len); - std::vector<char> tbuf(len); - if (zip->Read(tbuf.data(), len) != len) - return false; - const char* bp = tbuf.data(); - - // Decode and validate the transitions. + return false; + + // Read the data into a local buffer. + std::size_t len = hdr.DataLength(time_len); + std::vector<char> tbuf(len); + if (zip->Read(tbuf.data(), len) != len) + return false; + const char* bp = tbuf.data(); + + // Decode and validate the transitions. transitions_.reserve(hdr.timecnt + 2); - transitions_.resize(hdr.timecnt); - for (std::size_t i = 0; i != hdr.timecnt; ++i) { - transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp); - bp += time_len; - if (i != 0) { - // Check that the transitions are ordered by time (as zic guarantees). - if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i])) - return false; // out of order - } - } - bool seen_type_0 = false; - for (std::size_t i = 0; i != hdr.timecnt; ++i) { - transitions_[i].type_index = Decode8(bp++); - if (transitions_[i].type_index >= hdr.typecnt) - return false; - if (transitions_[i].type_index == 0) - seen_type_0 = true; - } - - // Decode and validate the transition types. + transitions_.resize(hdr.timecnt); + for (std::size_t i = 0; i != hdr.timecnt; ++i) { + transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp); + bp += time_len; + if (i != 0) { + // Check that the transitions are ordered by time (as zic guarantees). + if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i])) + return false; // out of order + } + } + bool seen_type_0 = false; + for (std::size_t i = 0; i != hdr.timecnt; ++i) { + transitions_[i].type_index = Decode8(bp++); + if (transitions_[i].type_index >= hdr.typecnt) + return false; + if (transitions_[i].type_index == 0) + seen_type_0 = true; + } + + // Decode and validate the transition types. transition_types_.reserve(hdr.typecnt + 2); - transition_types_.resize(hdr.typecnt); - for (std::size_t i = 0; i != hdr.typecnt; ++i) { - transition_types_[i].utc_offset = - static_cast<std::int_least32_t>(Decode32(bp)); - if (transition_types_[i].utc_offset >= kSecsPerDay || - transition_types_[i].utc_offset <= -kSecsPerDay) - return false; - bp += 4; - transition_types_[i].is_dst = (Decode8(bp++) != 0); - transition_types_[i].abbr_index = Decode8(bp++); - if (transition_types_[i].abbr_index >= hdr.charcnt) - return false; - } - - // Determine the before-first-transition type. - default_transition_type_ = 0; - if (seen_type_0 && hdr.timecnt != 0) { - std::uint_fast8_t index = 0; - if (transition_types_[0].is_dst) { - index = transitions_[0].type_index; - while (index != 0 && transition_types_[index].is_dst) - --index; - } - while (index != hdr.typecnt && transition_types_[index].is_dst) - ++index; - if (index != hdr.typecnt) - default_transition_type_ = index; - } - - // Copy all the abbreviations. + transition_types_.resize(hdr.typecnt); + for (std::size_t i = 0; i != hdr.typecnt; ++i) { + transition_types_[i].utc_offset = + static_cast<std::int_least32_t>(Decode32(bp)); + if (transition_types_[i].utc_offset >= kSecsPerDay || + transition_types_[i].utc_offset <= -kSecsPerDay) + return false; + bp += 4; + transition_types_[i].is_dst = (Decode8(bp++) != 0); + transition_types_[i].abbr_index = Decode8(bp++); + if (transition_types_[i].abbr_index >= hdr.charcnt) + return false; + } + + // Determine the before-first-transition type. + default_transition_type_ = 0; + if (seen_type_0 && hdr.timecnt != 0) { + std::uint_fast8_t index = 0; + if (transition_types_[0].is_dst) { + index = transitions_[0].type_index; + while (index != 0 && transition_types_[index].is_dst) + --index; + } + while (index != hdr.typecnt && transition_types_[index].is_dst) + ++index; + if (index != hdr.typecnt) + default_transition_type_ = index; + } + + // Copy all the abbreviations. abbreviations_.reserve(hdr.charcnt + 10); - abbreviations_.assign(bp, hdr.charcnt); - bp += hdr.charcnt; - - // Skip the unused portions. We've already dispensed with leap-second - // encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when - // interpreting a POSIX spec that does not include start/end rules, and - // that isn't the case here (see "zic -p"). - bp += (8 + 4) * hdr.leapcnt; // leap-time + TAI-UTC - bp += 1 * hdr.ttisstdcnt; // UTC/local indicators + abbreviations_.assign(bp, hdr.charcnt); + bp += hdr.charcnt; + + // Skip the unused portions. We've already dispensed with leap-second + // encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when + // interpreting a POSIX spec that does not include start/end rules, and + // that isn't the case here (see "zic -p"). + bp += (8 + 4) * hdr.leapcnt; // leap-time + TAI-UTC + bp += 1 * hdr.ttisstdcnt; // UTC/local indicators bp += 1 * hdr.ttisutcnt; // standard/wall indicators - assert(bp == tbuf.data() + tbuf.size()); - - future_spec_.clear(); - if (tzh.tzh_version[0] != '\0') { - // Snarf up the NL-enclosed future POSIX spec. Note - // that version '3' files utilize an extended format. + assert(bp == tbuf.data() + tbuf.size()); + + future_spec_.clear(); + if (tzh.tzh_version[0] != '\0') { + // Snarf up the NL-enclosed future POSIX spec. Note + // that version '3' files utilize an extended format. auto get_char = [](ZoneInfoSource* azip) -> int { - unsigned char ch; // all non-EOF results are positive + unsigned char ch; // all non-EOF results are positive return (azip->Read(&ch, 1) == 1) ? ch : EOF; - }; - if (get_char(zip) != '\n') - return false; - for (int c = get_char(zip); c != '\n'; c = get_char(zip)) { - if (c == EOF) - return false; - future_spec_.push_back(static_cast<char>(c)); - } - } - - // We don't check for EOF so that we're forwards compatible. - + }; + if (get_char(zip) != '\n') + return false; + for (int c = get_char(zip); c != '\n'; c = get_char(zip)) { + if (c == EOF) + return false; + future_spec_.push_back(static_cast<char>(c)); + } + } + + // We don't check for EOF so that we're forwards compatible. + // If we did not find version information during the standard loading // process (as of tzh_version '3' that is unsupported), then ask the // ZoneInfoSource for any out-of-bound version string it may be privy to. @@ -522,32 +522,32 @@ bool TimeZoneInfo::Load(ZoneInfoSource* zip) { version_ = zip->Version(); } - // Trim redundant transitions. zic may have added these to work around - // differences between the glibc and reference implementations (see - // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071). - // For us, they just get in the way when we do future_spec_ extension. - while (hdr.timecnt > 1) { - if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index, - transitions_[hdr.timecnt - 2].type_index)) { - break; - } - hdr.timecnt -= 1; - } - transitions_.resize(hdr.timecnt); - - // Ensure that there is always a transition in the first half of the + // Trim redundant transitions. zic may have added these to work around + // differences between the glibc and reference implementations (see + // zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071). + // For us, they just get in the way when we do future_spec_ extension. + while (hdr.timecnt > 1) { + if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index, + transitions_[hdr.timecnt - 2].type_index)) { + break; + } + hdr.timecnt -= 1; + } + transitions_.resize(hdr.timecnt); + + // Ensure that there is always a transition in the first half of the // time line (the second half is handled below) so that the signed // difference between a civil_second and the civil_second of its // previous transition is always representable, without overflow. - if (transitions_.empty() || transitions_.front().unix_time >= 0) { - Transition& tr(*transitions_.emplace(transitions_.begin())); + if (transitions_.empty() || transitions_.front().unix_time >= 0) { + Transition& tr(*transitions_.emplace(transitions_.begin())); tr.unix_time = -(1LL << 59); // -18267312070-10-26T17:01:52+00:00 - tr.type_index = default_transition_type_; - } - - // Extend the transitions using the future specification. + tr.type_index = default_transition_type_; + } + + // Extend the transitions using the future specification. if (!ExtendTransitions()) return false; - + // Ensure that there is always a transition in the second half of the // time line (the first half is handled above) so that the signed // difference between a civil_second and the civil_second of its @@ -561,35 +561,35 @@ bool TimeZoneInfo::Load(ZoneInfoSource* zip) { } // Compute the local civil time for each transition and the preceding - // second. These will be used for reverse conversions in MakeTime(). - const TransitionType* ttp = &transition_types_[default_transition_type_]; - for (std::size_t i = 0; i != transitions_.size(); ++i) { - Transition& tr(transitions_[i]); - tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1; - ttp = &transition_types_[tr.type_index]; - tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs; - if (i != 0) { - // Check that the transitions are ordered by civil time. Essentially - // this means that an offset change cannot cross another such change. - // No one does this in practice, and we depend on it in MakeTime(). - if (!Transition::ByCivilTime()(transitions_[i - 1], tr)) - return false; // out of order - } - } - - // Compute the maximum/minimum civil times that can be converted to a + // second. These will be used for reverse conversions in MakeTime(). + const TransitionType* ttp = &transition_types_[default_transition_type_]; + for (std::size_t i = 0; i != transitions_.size(); ++i) { + Transition& tr(transitions_[i]); + tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1; + ttp = &transition_types_[tr.type_index]; + tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs; + if (i != 0) { + // Check that the transitions are ordered by civil time. Essentially + // this means that an offset change cannot cross another such change. + // No one does this in practice, and we depend on it in MakeTime(). + if (!Transition::ByCivilTime()(transitions_[i - 1], tr)) + return false; // out of order + } + } + + // Compute the maximum/minimum civil times that can be converted to a // time_point<seconds> for each of the zone's transition types. - for (auto& tt : transition_types_) { + for (auto& tt : transition_types_) { tt.civil_max = LocalTime(seconds::max().count(), tt).cs; tt.civil_min = LocalTime(seconds::min().count(), tt).cs; - } - - transitions_.shrink_to_fit(); - return true; -} - -namespace { - + } + + transitions_.shrink_to_fit(); + return true; +} + +namespace { + // fopen(3) adaptor. inline FILE* FOpen(const char* path, const char* mode) { #if defined(_MSC_VER) @@ -601,65 +601,65 @@ inline FILE* FOpen(const char* path, const char* mode) { #endif } -// A stdio(3)-backed implementation of ZoneInfoSource. -class FileZoneInfoSource : public ZoneInfoSource { - public: - static std::unique_ptr<ZoneInfoSource> Open(const std::string& name); - - std::size_t Read(void* ptr, std::size_t size) override { +// A stdio(3)-backed implementation of ZoneInfoSource. +class FileZoneInfoSource : public ZoneInfoSource { + public: + static std::unique_ptr<ZoneInfoSource> Open(const std::string& name); + + std::size_t Read(void* ptr, std::size_t size) override { size = std::min(size, len_); std::size_t nread = fread(ptr, 1, size, fp_.get()); len_ -= nread; return nread; - } - int Skip(std::size_t offset) override { + } + int Skip(std::size_t offset) override { offset = std::min(offset, len_); int rc = fseek(fp_.get(), static_cast<long>(offset), SEEK_CUR); if (rc == 0) len_ -= offset; return rc; - } + } std::string Version() const override { // TODO: It would nice if the zoneinfo data included the tzdb version. return std::string(); } - + protected: explicit FileZoneInfoSource( FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max()) : fp_(fp, fclose), len_(len) {} - private: + private: std::unique_ptr<FILE, int(*)(FILE*)> fp_; std::size_t len_; -}; - -std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open( - const std::string& name) { - // Use of the "file:" prefix is intended for testing purposes only. +}; + +std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open( + const std::string& name) { + // Use of the "file:" prefix is intended for testing purposes only. const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0; - - // Map the time-zone name to a path name. - std::string path; + + // Map the time-zone name to a path name. + std::string path; if (pos == name.size() || name[pos] != '/') { - const char* tzdir = "/usr/share/zoneinfo"; - char* tzdir_env = nullptr; -#if defined(_MSC_VER) - _dupenv_s(&tzdir_env, nullptr, "TZDIR"); -#else - tzdir_env = std::getenv("TZDIR"); -#endif - if (tzdir_env && *tzdir_env) tzdir = tzdir_env; - path += tzdir; - path += '/'; -#if defined(_MSC_VER) - free(tzdir_env); -#endif - } + const char* tzdir = "/usr/share/zoneinfo"; + char* tzdir_env = nullptr; +#if defined(_MSC_VER) + _dupenv_s(&tzdir_env, nullptr, "TZDIR"); +#else + tzdir_env = std::getenv("TZDIR"); +#endif + if (tzdir_env && *tzdir_env) tzdir = tzdir_env; + path += tzdir; + path += '/'; +#if defined(_MSC_VER) + free(tzdir_env); +#endif + } path.append(name, pos, std::string::npos); - - // Open the zoneinfo file. + + // Open the zoneinfo file. FILE* fp = FOpen(path.c_str(), "rb"); - if (fp == nullptr) return nullptr; + if (fp == nullptr) return nullptr; std::size_t length = 0; if (fseek(fp, 0, SEEK_END) == 0) { long offset = ftell(fp); @@ -669,8 +669,8 @@ std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open( rewind(fp); } return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length)); -} - +} + class AndroidZoneInfoSource : public FileZoneInfoSource { public: static std::unique_ptr<ZoneInfoSource> Open(const std::string& name); @@ -725,254 +725,254 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( return nullptr; } -} // namespace - -bool TimeZoneInfo::Load(const std::string& name) { - // We can ensure that the loading of UTC or any other fixed-offset - // zone never fails because the simple, fixed-offset state can be - // internally generated. Note that this depends on our choice to not - // accept leap-second encoded ("right") zoneinfo. +} // namespace + +bool TimeZoneInfo::Load(const std::string& name) { + // We can ensure that the loading of UTC or any other fixed-offset + // zone never fails because the simple, fixed-offset state can be + // internally generated. Note that this depends on our choice to not + // accept leap-second encoded ("right") zoneinfo. auto offset = seconds::zero(); - if (FixedOffsetFromName(name, &offset)) { - return ResetToBuiltinUTC(offset); - } - - // Find and use a ZoneInfoSource to load the named zone. - auto zip = cctz_extension::zone_info_source_factory( + if (FixedOffsetFromName(name, &offset)) { + return ResetToBuiltinUTC(offset); + } + + // Find and use a ZoneInfoSource to load the named zone. + auto zip = cctz_extension::zone_info_source_factory( name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> { if (auto z = FileZoneInfoSource::Open(n)) return z; if (auto z = AndroidZoneInfoSource::Open(n)) return z; return nullptr; - }); + }); return zip != nullptr && Load(zip.get()); -} - -// BreakTime() translation for a particular transition type. -time_zone::absolute_lookup TimeZoneInfo::LocalTime( - std::int_fast64_t unix_time, const TransitionType& tt) const { - // A civil time in "+offset" looks like (time+offset) in UTC. - // Note: We perform two additions in the civil_second domain to - // sidestep the chance of overflow in (unix_time + tt.utc_offset). - return {(civil_second() + unix_time) + tt.utc_offset, - tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]}; -} - -// BreakTime() translation for a particular transition. -time_zone::absolute_lookup TimeZoneInfo::LocalTime( - std::int_fast64_t unix_time, const Transition& tr) const { - const TransitionType& tt = transition_types_[tr.type_index]; - // Note: (unix_time - tr.unix_time) will never overflow as we - // have ensured that there is always a "nearby" transition. - return {tr.civil_sec + (unix_time - tr.unix_time), // TODO: Optimize. - tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]}; -} - -// MakeTime() translation with a conversion-preserving +N * 400-year shift. -time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs, +} + +// BreakTime() translation for a particular transition type. +time_zone::absolute_lookup TimeZoneInfo::LocalTime( + std::int_fast64_t unix_time, const TransitionType& tt) const { + // A civil time in "+offset" looks like (time+offset) in UTC. + // Note: We perform two additions in the civil_second domain to + // sidestep the chance of overflow in (unix_time + tt.utc_offset). + return {(civil_second() + unix_time) + tt.utc_offset, + tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]}; +} + +// BreakTime() translation for a particular transition. +time_zone::absolute_lookup TimeZoneInfo::LocalTime( + std::int_fast64_t unix_time, const Transition& tr) const { + const TransitionType& tt = transition_types_[tr.type_index]; + // Note: (unix_time - tr.unix_time) will never overflow as we + // have ensured that there is always a "nearby" transition. + return {tr.civil_sec + (unix_time - tr.unix_time), // TODO: Optimize. + tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]}; +} + +// MakeTime() translation with a conversion-preserving +N * 400-year shift. +time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs, year_t c4_shift) const { - assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_); - time_zone::civil_lookup cl = MakeTime(cs); + assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_); + time_zone::civil_lookup cl = MakeTime(cs); if (c4_shift > seconds::max().count() / kSecsPer400Years) { cl.pre = cl.trans = cl.post = time_point<seconds>::max(); - } else { + } else { const auto offset = seconds(c4_shift * kSecsPer400Years); const auto limit = time_point<seconds>::max() - offset; - for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) { - if (*tp > limit) { + for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) { + if (*tp > limit) { *tp = time_point<seconds>::max(); - } else { - *tp += offset; - } - } - } - return cl; -} - -time_zone::absolute_lookup TimeZoneInfo::BreakTime( + } else { + *tp += offset; + } + } + } + return cl; +} + +time_zone::absolute_lookup TimeZoneInfo::BreakTime( const time_point<seconds>& tp) const { - std::int_fast64_t unix_time = ToUnixSeconds(tp); - const std::size_t timecnt = transitions_.size(); - assert(timecnt != 0); // We always add a transition. - - if (unix_time < transitions_[0].unix_time) { - return LocalTime(unix_time, transition_types_[default_transition_type_]); - } - if (unix_time >= transitions_[timecnt - 1].unix_time) { - // After the last transition. If we extended the transitions using - // future_spec_, shift back to a supported year using the 400-year - // cycle of calendaric equivalence and then compensate accordingly. - if (extended_) { - const std::int_fast64_t diff = - unix_time - transitions_[timecnt - 1].unix_time; + std::int_fast64_t unix_time = ToUnixSeconds(tp); + const std::size_t timecnt = transitions_.size(); + assert(timecnt != 0); // We always add a transition. + + if (unix_time < transitions_[0].unix_time) { + return LocalTime(unix_time, transition_types_[default_transition_type_]); + } + if (unix_time >= transitions_[timecnt - 1].unix_time) { + // After the last transition. If we extended the transitions using + // future_spec_, shift back to a supported year using the 400-year + // cycle of calendaric equivalence and then compensate accordingly. + if (extended_) { + const std::int_fast64_t diff = + unix_time - transitions_[timecnt - 1].unix_time; const year_t shift = diff / kSecsPer400Years + 1; const auto d = seconds(shift * kSecsPer400Years); - time_zone::absolute_lookup al = BreakTime(tp - d); - al.cs = YearShift(al.cs, shift * 400); - return al; - } - return LocalTime(unix_time, transitions_[timecnt - 1]); - } - - const std::size_t hint = local_time_hint_.load(std::memory_order_relaxed); - if (0 < hint && hint < timecnt) { - if (transitions_[hint - 1].unix_time <= unix_time) { - if (unix_time < transitions_[hint].unix_time) { - return LocalTime(unix_time, transitions_[hint - 1]); - } - } - } - - const Transition target = {unix_time, 0, civil_second(), civil_second()}; - const Transition* begin = &transitions_[0]; - const Transition* tr = std::upper_bound(begin, begin + timecnt, target, - Transition::ByUnixTime()); - local_time_hint_.store(static_cast<std::size_t>(tr - begin), - std::memory_order_relaxed); - return LocalTime(unix_time, *--tr); -} - -time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const { - const std::size_t timecnt = transitions_.size(); - assert(timecnt != 0); // We always add a transition. - - // Find the first transition after our target civil time. - const Transition* tr = nullptr; - const Transition* begin = &transitions_[0]; - const Transition* end = begin + timecnt; - if (cs < begin->civil_sec) { - tr = begin; - } else if (cs >= transitions_[timecnt - 1].civil_sec) { - tr = end; - } else { - const std::size_t hint = time_local_hint_.load(std::memory_order_relaxed); - if (0 < hint && hint < timecnt) { - if (transitions_[hint - 1].civil_sec <= cs) { - if (cs < transitions_[hint].civil_sec) { - tr = begin + hint; - } - } - } - if (tr == nullptr) { - const Transition target = {0, 0, cs, civil_second()}; - tr = std::upper_bound(begin, end, target, Transition::ByCivilTime()); - time_local_hint_.store(static_cast<std::size_t>(tr - begin), - std::memory_order_relaxed); - } - } - - if (tr == begin) { - if (tr->prev_civil_sec >= cs) { - // Before first transition, so use the default offset. - const TransitionType& tt(transition_types_[default_transition_type_]); + time_zone::absolute_lookup al = BreakTime(tp - d); + al.cs = YearShift(al.cs, shift * 400); + return al; + } + return LocalTime(unix_time, transitions_[timecnt - 1]); + } + + const std::size_t hint = local_time_hint_.load(std::memory_order_relaxed); + if (0 < hint && hint < timecnt) { + if (transitions_[hint - 1].unix_time <= unix_time) { + if (unix_time < transitions_[hint].unix_time) { + return LocalTime(unix_time, transitions_[hint - 1]); + } + } + } + + const Transition target = {unix_time, 0, civil_second(), civil_second()}; + const Transition* begin = &transitions_[0]; + const Transition* tr = std::upper_bound(begin, begin + timecnt, target, + Transition::ByUnixTime()); + local_time_hint_.store(static_cast<std::size_t>(tr - begin), + std::memory_order_relaxed); + return LocalTime(unix_time, *--tr); +} + +time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const { + const std::size_t timecnt = transitions_.size(); + assert(timecnt != 0); // We always add a transition. + + // Find the first transition after our target civil time. + const Transition* tr = nullptr; + const Transition* begin = &transitions_[0]; + const Transition* end = begin + timecnt; + if (cs < begin->civil_sec) { + tr = begin; + } else if (cs >= transitions_[timecnt - 1].civil_sec) { + tr = end; + } else { + const std::size_t hint = time_local_hint_.load(std::memory_order_relaxed); + if (0 < hint && hint < timecnt) { + if (transitions_[hint - 1].civil_sec <= cs) { + if (cs < transitions_[hint].civil_sec) { + tr = begin + hint; + } + } + } + if (tr == nullptr) { + const Transition target = {0, 0, cs, civil_second()}; + tr = std::upper_bound(begin, end, target, Transition::ByCivilTime()); + time_local_hint_.store(static_cast<std::size_t>(tr - begin), + std::memory_order_relaxed); + } + } + + if (tr == begin) { + if (tr->prev_civil_sec >= cs) { + // Before first transition, so use the default offset. + const TransitionType& tt(transition_types_[default_transition_type_]); if (cs < tt.civil_min) return MakeUnique(time_point<seconds>::min()); - return MakeUnique(cs - (civil_second() + tt.utc_offset)); - } - // tr->prev_civil_sec < cs < tr->civil_sec - return MakeSkipped(*tr, cs); - } - - if (tr == end) { - if (cs > (--tr)->prev_civil_sec) { - // After the last transition. If we extended the transitions using - // future_spec_, shift back to a supported year using the 400-year - // cycle of calendaric equivalence and then compensate accordingly. - if (extended_ && cs.year() > last_year_) { + return MakeUnique(cs - (civil_second() + tt.utc_offset)); + } + // tr->prev_civil_sec < cs < tr->civil_sec + return MakeSkipped(*tr, cs); + } + + if (tr == end) { + if (cs > (--tr)->prev_civil_sec) { + // After the last transition. If we extended the transitions using + // future_spec_, shift back to a supported year using the 400-year + // cycle of calendaric equivalence and then compensate accordingly. + if (extended_ && cs.year() > last_year_) { const year_t shift = (cs.year() - last_year_ - 1) / 400 + 1; - return TimeLocal(YearShift(cs, shift * -400), shift); - } - const TransitionType& tt(transition_types_[tr->type_index]); + return TimeLocal(YearShift(cs, shift * -400), shift); + } + const TransitionType& tt(transition_types_[tr->type_index]); if (cs > tt.civil_max) return MakeUnique(time_point<seconds>::max()); - return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); - } - // tr->civil_sec <= cs <= tr->prev_civil_sec - return MakeRepeated(*tr, cs); - } - - if (tr->prev_civil_sec < cs) { - // tr->prev_civil_sec < cs < tr->civil_sec - return MakeSkipped(*tr, cs); - } - - if (cs <= (--tr)->prev_civil_sec) { - // tr->civil_sec <= cs <= tr->prev_civil_sec - return MakeRepeated(*tr, cs); - } - - // In between transitions. - return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); -} - + return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); + } + // tr->civil_sec <= cs <= tr->prev_civil_sec + return MakeRepeated(*tr, cs); + } + + if (tr->prev_civil_sec < cs) { + // tr->prev_civil_sec < cs < tr->civil_sec + return MakeSkipped(*tr, cs); + } + + if (cs <= (--tr)->prev_civil_sec) { + // tr->civil_sec <= cs <= tr->prev_civil_sec + return MakeRepeated(*tr, cs); + } + + // In between transitions. + return MakeUnique(tr->unix_time + (cs - tr->civil_sec)); +} + std::string TimeZoneInfo::Version() const { return version_; } -std::string TimeZoneInfo::Description() const { - std::ostringstream oss; - oss << "#trans=" << transitions_.size(); - oss << " #types=" << transition_types_.size(); - oss << " spec='" << future_spec_ << "'"; - return oss.str(); -} - +std::string TimeZoneInfo::Description() const { + std::ostringstream oss; + oss << "#trans=" << transitions_.size(); + oss << " #types=" << transition_types_.size(); + oss << " spec='" << future_spec_ << "'"; + return oss.str(); +} + bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const { - if (transitions_.empty()) return false; - const Transition* begin = &transitions_[0]; - const Transition* end = begin + transitions_.size(); - if (begin->unix_time <= -(1LL << 59)) { + if (transitions_.empty()) return false; + const Transition* begin = &transitions_[0]; + const Transition* end = begin + transitions_.size(); + if (begin->unix_time <= -(1LL << 59)) { // Do not report the BIG_BANG found in some zoneinfo data as it is // really a sentinel, not a transition. See pre-2018f tz/zic.c. - ++begin; - } + ++begin; + } std::int_fast64_t unix_time = ToUnixSeconds(tp); const Transition target = {unix_time, 0, civil_second(), civil_second()}; - const Transition* tr = std::upper_bound(begin, end, target, - Transition::ByUnixTime()); + const Transition* tr = std::upper_bound(begin, end, target, + Transition::ByUnixTime()); for (; tr != end; ++tr) { // skip no-op transitions std::uint_fast8_t prev_type_index = (tr == begin) ? default_transition_type_ : tr[-1].type_index; if (!EquivTransitions(prev_type_index, tr[0].type_index)) break; - } - // When tr == end we return false, ignoring future_spec_. - if (tr == end) return false; + } + // When tr == end we return false, ignoring future_spec_. + if (tr == end) return false; trans->from = tr->prev_civil_sec + 1; trans->to = tr->civil_sec; - return true; -} - + return true; +} + bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const { - if (transitions_.empty()) return false; - const Transition* begin = &transitions_[0]; - const Transition* end = begin + transitions_.size(); - if (begin->unix_time <= -(1LL << 59)) { + if (transitions_.empty()) return false; + const Transition* begin = &transitions_[0]; + const Transition* end = begin + transitions_.size(); + if (begin->unix_time <= -(1LL << 59)) { // Do not report the BIG_BANG found in some zoneinfo data as it is // really a sentinel, not a transition. See pre-2018f tz/zic.c. - ++begin; - } + ++begin; + } std::int_fast64_t unix_time = ToUnixSeconds(tp); if (FromUnixSeconds(unix_time) != tp) { - if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) { - if (end == begin) return false; // Ignore future_spec_. + if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) { + if (end == begin) return false; // Ignore future_spec_. trans->from = (--end)->prev_civil_sec + 1; trans->to = end->civil_sec; - return true; - } - unix_time += 1; // ceils - } + return true; + } + unix_time += 1; // ceils + } const Transition target = {unix_time, 0, civil_second(), civil_second()}; - const Transition* tr = std::lower_bound(begin, end, target, - Transition::ByUnixTime()); + const Transition* tr = std::lower_bound(begin, end, target, + Transition::ByUnixTime()); for (; tr != begin; --tr) { // skip no-op transitions std::uint_fast8_t prev_type_index = (tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index; if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break; - } - // When tr == end we return the "last" transition, ignoring future_spec_. - if (tr == begin) return false; + } + // When tr == end we return the "last" transition, ignoring future_spec_. + if (tr == begin) return false; trans->from = (--tr)->prev_civil_sec + 1; trans->to = tr->civil_sec; - return true; -} - -} // namespace cctz + return true; +} + +} // namespace cctz diff --git a/contrib/libs/cctz/src/time_zone_info.h b/contrib/libs/cctz/src/time_zone_info.h index 431dc5b71b..009fe0b2a0 100644 --- a/contrib/libs/cctz/src/time_zone_info.h +++ b/contrib/libs/cctz/src/time_zone_info.h @@ -1,131 +1,131 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#ifndef CCTZ_TIME_ZONE_INFO_H_ -#define CCTZ_TIME_ZONE_INFO_H_ - -#include <atomic> -#include <cstddef> -#include <cstdint> -#include <string> -#include <vector> - +// +// 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. + +#ifndef CCTZ_TIME_ZONE_INFO_H_ +#define CCTZ_TIME_ZONE_INFO_H_ + +#include <atomic> +#include <cstddef> +#include <cstdint> +#include <string> +#include <vector> + #include "cctz/civil_time.h" #include "cctz/time_zone.h" #include "cctz/zone_info_source.h" -#include "time_zone_if.h" -#include "tzfile.h" - -namespace cctz { - -// A transition to a new UTC offset. -struct Transition { - std::int_least64_t unix_time; // the instant of this transition - std::uint_least8_t type_index; // index of the transition type - civil_second civil_sec; // local civil time of transition - civil_second prev_civil_sec; // local civil time one second earlier - - struct ByUnixTime { - inline bool operator()(const Transition& lhs, const Transition& rhs) const { - return lhs.unix_time < rhs.unix_time; - } - }; - struct ByCivilTime { - inline bool operator()(const Transition& lhs, const Transition& rhs) const { - return lhs.civil_sec < rhs.civil_sec; - } - }; -}; - -// The characteristics of a particular transition. -struct TransitionType { - std::int_least32_t utc_offset; // the new prevailing UTC offset - civil_second civil_max; // max convertible civil time for offset - civil_second civil_min; // min convertible civil time for offset - bool is_dst; // did we move into daylight-saving time - std::uint_least8_t abbr_index; // index of the new abbreviation -}; - -// A time zone backed by the IANA Time Zone Database (zoneinfo). -class TimeZoneInfo : public TimeZoneIf { - public: - TimeZoneInfo() = default; - TimeZoneInfo(const TimeZoneInfo&) = delete; - TimeZoneInfo& operator=(const TimeZoneInfo&) = delete; - - // Loads the zoneinfo for the given name, returning true if successful. - bool Load(const std::string& name); - - // TimeZoneIf implementations. - time_zone::absolute_lookup BreakTime( +#include "time_zone_if.h" +#include "tzfile.h" + +namespace cctz { + +// A transition to a new UTC offset. +struct Transition { + std::int_least64_t unix_time; // the instant of this transition + std::uint_least8_t type_index; // index of the transition type + civil_second civil_sec; // local civil time of transition + civil_second prev_civil_sec; // local civil time one second earlier + + struct ByUnixTime { + inline bool operator()(const Transition& lhs, const Transition& rhs) const { + return lhs.unix_time < rhs.unix_time; + } + }; + struct ByCivilTime { + inline bool operator()(const Transition& lhs, const Transition& rhs) const { + return lhs.civil_sec < rhs.civil_sec; + } + }; +}; + +// The characteristics of a particular transition. +struct TransitionType { + std::int_least32_t utc_offset; // the new prevailing UTC offset + civil_second civil_max; // max convertible civil time for offset + civil_second civil_min; // min convertible civil time for offset + bool is_dst; // did we move into daylight-saving time + std::uint_least8_t abbr_index; // index of the new abbreviation +}; + +// A time zone backed by the IANA Time Zone Database (zoneinfo). +class TimeZoneInfo : public TimeZoneIf { + public: + TimeZoneInfo() = default; + TimeZoneInfo(const TimeZoneInfo&) = delete; + TimeZoneInfo& operator=(const TimeZoneInfo&) = delete; + + // Loads the zoneinfo for the given name, returning true if successful. + bool Load(const std::string& name); + + // TimeZoneIf implementations. + time_zone::absolute_lookup BreakTime( const time_point<seconds>& tp) const override; - time_zone::civil_lookup MakeTime( - const civil_second& cs) const override; + time_zone::civil_lookup MakeTime( + const civil_second& cs) const override; bool NextTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const override; bool PrevTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const override; std::string Version() const override; - std::string Description() const override; - - private: - struct Header { // counts of: - std::size_t timecnt; // transition times - std::size_t typecnt; // transition types - std::size_t charcnt; // zone abbreviation characters - std::size_t leapcnt; // leap seconds (we expect none) - std::size_t ttisstdcnt; // UTC/local indicators (unused) + std::string Description() const override; + + private: + struct Header { // counts of: + std::size_t timecnt; // transition times + std::size_t typecnt; // transition types + std::size_t charcnt; // zone abbreviation characters + std::size_t leapcnt; // leap seconds (we expect none) + std::size_t ttisstdcnt; // UTC/local indicators (unused) std::size_t ttisutcnt; // standard/wall indicators (unused) - - bool Build(const tzhead& tzh); - std::size_t DataLength(std::size_t time_len) const; - }; - + + bool Build(const tzhead& tzh); + std::size_t DataLength(std::size_t time_len) const; + }; + bool GetTransitionType(std::int_fast32_t utc_offset, bool is_dst, const std::string& abbr, std::uint_least8_t* index); - bool EquivTransitions(std::uint_fast8_t tt1_index, - std::uint_fast8_t tt2_index) const; + bool EquivTransitions(std::uint_fast8_t tt1_index, + std::uint_fast8_t tt2_index) const; bool ExtendTransitions(); - + bool ResetToBuiltinUTC(const seconds& offset); bool Load(ZoneInfoSource* zip); - - // Helpers for BreakTime() and MakeTime(). - time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time, - const TransitionType& tt) const; - time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time, - const Transition& tr) const; - time_zone::civil_lookup TimeLocal(const civil_second& cs, + + // Helpers for BreakTime() and MakeTime(). + time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time, + const TransitionType& tt) const; + time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time, + const Transition& tr) const; + time_zone::civil_lookup TimeLocal(const civil_second& cs, year_t c4_shift) const; - - std::vector<Transition> transitions_; // ordered by unix_time and civil_sec - std::vector<TransitionType> transition_types_; // distinct transition types - std::uint_fast8_t default_transition_type_; // for before first transition - std::string abbreviations_; // all the NUL-terminated abbreviations - + + std::vector<Transition> transitions_; // ordered by unix_time and civil_sec + std::vector<TransitionType> transition_types_; // distinct transition types + std::uint_fast8_t default_transition_type_; // for before first transition + std::string abbreviations_; // all the NUL-terminated abbreviations + std::string version_; // the tzdata version if available - std::string future_spec_; // for after the last zic transition - bool extended_; // future_spec_ was used to generate transitions + std::string future_spec_; // for after the last zic transition + bool extended_; // future_spec_ was used to generate transitions year_t last_year_; // the final year of the generated transitions - - // We remember the transitions found during the last BreakTime() and - // MakeTime() calls. If the next request is for the same transition we - // will avoid re-searching. - mutable std::atomic<std::size_t> local_time_hint_ = {}; // BreakTime() hint - mutable std::atomic<std::size_t> time_local_hint_ = {}; // MakeTime() hint -}; - -} // namespace cctz - -#endif // CCTZ_TIME_ZONE_INFO_H_ + + // We remember the transitions found during the last BreakTime() and + // MakeTime() calls. If the next request is for the same transition we + // will avoid re-searching. + mutable std::atomic<std::size_t> local_time_hint_ = {}; // BreakTime() hint + mutable std::atomic<std::size_t> time_local_hint_ = {}; // MakeTime() hint +}; + +} // namespace cctz + +#endif // CCTZ_TIME_ZONE_INFO_H_ diff --git a/contrib/libs/cctz/src/time_zone_libc.cc b/contrib/libs/cctz/src/time_zone_libc.cc index f185b957b6..142fd792df 100644 --- a/contrib/libs/cctz/src/time_zone_libc.cc +++ b/contrib/libs/cctz/src/time_zone_libc.cc @@ -1,73 +1,73 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#if defined(_WIN32) || defined(_WIN64) +// +// 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. + +#if defined(_WIN32) || defined(_WIN64) #define _CRT_SECURE_NO_WARNINGS 1 -#endif - -#include "time_zone_libc.h" - -#include <chrono> -#include <ctime> +#endif + +#include "time_zone_libc.h" + +#include <chrono> +#include <ctime> #include <limits> -#include <utility> - +#include <utility> + #include "cctz/civil_time.h" #include "cctz/time_zone.h" - + #if defined(_AIX) extern "C" { extern long altzone; } #endif -namespace cctz { - -namespace { - -#if defined(_WIN32) || defined(_WIN64) -// Uses the globals: '_timezone', '_dstbias' and '_tzname'. +namespace cctz { + +namespace { + +#if defined(_WIN32) || defined(_WIN64) +// Uses the globals: '_timezone', '_dstbias' and '_tzname'. auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) { - const bool is_dst = tm.tm_isdst > 0; + const bool is_dst = tm.tm_isdst > 0; return _timezone + (is_dst ? _dstbias : 0); -} +} auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) { const bool is_dst = tm.tm_isdst > 0; return _tzname[is_dst]; } #elif defined(__sun) || defined(_AIX) -// Uses the globals: 'timezone', 'altzone' and 'tzname'. +// Uses the globals: 'timezone', 'altzone' and 'tzname'. auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) { - const bool is_dst = tm.tm_isdst > 0; + const bool is_dst = tm.tm_isdst > 0; return is_dst ? altzone : timezone; -} +} auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) { const bool is_dst = tm.tm_isdst > 0; return tzname[is_dst]; } #elif defined(__native_client__) || defined(__myriad2__) || \ defined(__EMSCRIPTEN__) -// Uses the globals: 'timezone' and 'tzname'. +// Uses the globals: 'timezone' and 'tzname'. auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) { - const bool is_dst = tm.tm_isdst > 0; + const bool is_dst = tm.tm_isdst > 0; return _timezone + (is_dst ? 60 * 60 : 0); -} +} auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) { const bool is_dst = tm.tm_isdst > 0; return tzname[is_dst]; } -#else +#else // Adapt to different spellings of the struct std::tm extension fields. #if defined(tm_gmtoff) auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) { @@ -78,14 +78,14 @@ auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) { return tm.__tm_gmtoff; } #else -template <typename T> +template <typename T> auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) { return tm.tm_gmtoff; -} -template <typename T> +} +template <typename T> auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) { return tm.__tm_gmtoff; -} +} #endif // tm_gmtoff #if defined(tm_zone) auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { @@ -105,8 +105,8 @@ auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) { return tm.__tm_zone; } #endif // tm_zone -#endif - +#endif + inline std::tm* gm_time(const std::time_t *timep, std::tm *result) { #if defined(_WIN32) || defined(_WIN64) return gmtime_s(result, timep) ? nullptr : result; @@ -179,14 +179,14 @@ std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) { return hi; } -} // namespace - -TimeZoneLibC::TimeZoneLibC(const std::string& name) - : local_(name == "localtime") {} - -time_zone::absolute_lookup TimeZoneLibC::BreakTime( +} // namespace + +TimeZoneLibC::TimeZoneLibC(const std::string& name) + : local_(name == "localtime") {} + +time_zone::absolute_lookup TimeZoneLibC::BreakTime( const time_point<seconds>& tp) const { - time_zone::absolute_lookup al; + time_zone::absolute_lookup al; al.offset = 0; al.is_dst = false; al.abbr = "-00"; @@ -204,14 +204,14 @@ time_zone::absolute_lookup TimeZoneLibC::BreakTime( } const std::time_t t = static_cast<std::time_t>(s); - std::tm tm; + std::tm tm; std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm); // If std::tm cannot hold the result we saturate the output. if (tmp == nullptr) { al.cs = (s < 0) ? civil_second::min() : civil_second::max(); return al; - } + } const year_t year = tmp->tm_year + year_t{1900}; al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, @@ -219,10 +219,10 @@ time_zone::absolute_lookup TimeZoneLibC::BreakTime( al.offset = static_cast<int>(tm_gmtoff(*tmp)); al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz al.is_dst = tmp->tm_isdst > 0; - return al; -} - -time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { + return al; +} + +time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { if (!local_) { // If time_point<seconds> cannot hold the result we saturate. static const civil_second min_tp_cs = @@ -243,13 +243,13 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { const time_point<seconds> tp = time_point<seconds>::min(); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } - } else { + } else { if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) { const time_point<seconds> tp = time_point<seconds>::max(); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; } - } - + } + // We probe with "is_dst" values of 0 and 1 to try to distinguish unique // civil seconds from skipped or repeated ones. This is not always possible // however, as the "dst" flag does not change over some offset transitions. @@ -288,18 +288,18 @@ time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { ? time_point<seconds>::min() : time_point<seconds>::max(); return {time_zone::civil_lookup::UNIQUE, tp, tp, tp}; -} - +} + bool TimeZoneLibC::NextTransition(const time_point<seconds>&, time_zone::civil_transition*) const { - return false; -} - + return false; +} + bool TimeZoneLibC::PrevTransition(const time_point<seconds>&, time_zone::civil_transition*) const { - return false; -} - + return false; +} + std::string TimeZoneLibC::Version() const { return std::string(); // unknown } @@ -308,4 +308,4 @@ std::string TimeZoneLibC::Description() const { return local_ ? "localtime" : "UTC"; } -} // namespace cctz +} // namespace cctz diff --git a/contrib/libs/cctz/src/time_zone_libc.h b/contrib/libs/cctz/src/time_zone_libc.h index 4cfe6dae31..5b39e37ab0 100644 --- a/contrib/libs/cctz/src/time_zone_libc.h +++ b/contrib/libs/cctz/src/time_zone_libc.h @@ -1,49 +1,49 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#ifndef CCTZ_TIME_ZONE_LIBC_H_ -#define CCTZ_TIME_ZONE_LIBC_H_ - -#include <string> - -#include "time_zone_if.h" - -namespace cctz { - -// A time zone backed by gmtime_r(3), localtime_r(3), and mktime(3), -// and which therefore only supports UTC and the local time zone. -// TODO: Add support for fixed offsets from UTC. -class TimeZoneLibC : public TimeZoneIf { - public: - explicit TimeZoneLibC(const std::string& name); - - // TimeZoneIf implementations. - time_zone::absolute_lookup BreakTime( +// +// 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. + +#ifndef CCTZ_TIME_ZONE_LIBC_H_ +#define CCTZ_TIME_ZONE_LIBC_H_ + +#include <string> + +#include "time_zone_if.h" + +namespace cctz { + +// A time zone backed by gmtime_r(3), localtime_r(3), and mktime(3), +// and which therefore only supports UTC and the local time zone. +// TODO: Add support for fixed offsets from UTC. +class TimeZoneLibC : public TimeZoneIf { + public: + explicit TimeZoneLibC(const std::string& name); + + // TimeZoneIf implementations. + time_zone::absolute_lookup BreakTime( const time_point<seconds>& tp) const override; - time_zone::civil_lookup MakeTime( - const civil_second& cs) const override; + time_zone::civil_lookup MakeTime( + const civil_second& cs) const override; bool NextTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const override; bool PrevTransition(const time_point<seconds>& tp, time_zone::civil_transition* trans) const override; std::string Version() const override; - std::string Description() const override; - - private: - const bool local_; // localtime or UTC -}; - -} // namespace cctz - -#endif // CCTZ_TIME_ZONE_LIBC_H_ + std::string Description() const override; + + private: + const bool local_; // localtime or UTC +}; + +} // namespace cctz + +#endif // CCTZ_TIME_ZONE_LIBC_H_ diff --git a/contrib/libs/cctz/src/time_zone_lookup.cc b/contrib/libs/cctz/src/time_zone_lookup.cc index 92eb2ae763..fd656dcb46 100644 --- a/contrib/libs/cctz/src/time_zone_lookup.cc +++ b/contrib/libs/cctz/src/time_zone_lookup.cc @@ -1,19 +1,19 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - +// +// 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. + #include "cctz/time_zone.h" - + #if defined(__ANDROID__) #include <sys/system_properties.h> #if defined(__ANDROID_API__) && __ANDROID_API__ >= 21 @@ -26,15 +26,15 @@ #include <vector> #endif -#include <cstdlib> -#include <cstring> -#include <string> - -#include "time_zone_fixed.h" -#include "time_zone_impl.h" - -namespace cctz { - +#include <cstdlib> +#include <cstring> +#include <string> + +#include "time_zone_fixed.h" +#include "time_zone_impl.h" + +namespace cctz { + #if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21 namespace { // Android 'L' removes __system_property_get() from the NDK, however @@ -64,24 +64,24 @@ int __system_property_get(const char* name, char* value) { } // namespace #endif -std::string time_zone::name() const { +std::string time_zone::name() const { return effective_impl().Name(); -} - -time_zone::absolute_lookup time_zone::lookup( +} + +time_zone::absolute_lookup time_zone::lookup( const time_point<seconds>& tp) const { return effective_impl().BreakTime(tp); -} - -time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const { +} + +time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const { return effective_impl().MakeTime(cs); -} - +} + bool time_zone::next_transition(const time_point<seconds>& tp, civil_transition* trans) const { return effective_impl().NextTransition(tp, trans); -} - +} + bool time_zone::prev_transition(const time_point<seconds>& tp, civil_transition* trans) const { return effective_impl().PrevTransition(tp, trans); @@ -104,22 +104,22 @@ const time_zone::Impl& time_zone::effective_impl() const { return *impl_; } -bool load_time_zone(const std::string& name, time_zone* tz) { - return time_zone::Impl::LoadTimeZone(name, tz); -} - -time_zone utc_time_zone() { - return time_zone::Impl::UTC(); // avoid name lookup -} - +bool load_time_zone(const std::string& name, time_zone* tz) { + return time_zone::Impl::LoadTimeZone(name, tz); +} + +time_zone utc_time_zone() { + return time_zone::Impl::UTC(); // avoid name lookup +} + time_zone fixed_time_zone(const seconds& offset) { - time_zone tz; - load_time_zone(FixedOffsetToName(offset), &tz); - return tz; -} - -time_zone local_time_zone() { - const char* zone = ":localtime"; + time_zone tz; + load_time_zone(FixedOffsetToName(offset), &tz); + return tz; +} + +time_zone local_time_zone() { + const char* zone = ":localtime"; #if defined(__ANDROID__) char sysprop[PROP_VALUE_MAX]; if (__system_property_get("persist.sys.timezone", sysprop) > 0) { @@ -139,45 +139,45 @@ time_zone local_time_zone() { } CFRelease(tz_default); #endif - - // Allow ${TZ} to override to default zone. - char* tz_env = nullptr; -#if defined(_MSC_VER) - _dupenv_s(&tz_env, nullptr, "TZ"); -#else - tz_env = std::getenv("TZ"); -#endif - if (tz_env) zone = tz_env; - - // We only support the "[:]<zone-name>" form. - if (*zone == ':') ++zone; - - // Map "localtime" to a system-specific name, but - // allow ${LOCALTIME} to override the default name. - char* localtime_env = nullptr; - if (strcmp(zone, "localtime") == 0) { -#if defined(_MSC_VER) - // System-specific default is just "localtime". - _dupenv_s(&localtime_env, nullptr, "LOCALTIME"); -#else - zone = "/etc/localtime"; // System-specific default. - localtime_env = std::getenv("LOCALTIME"); -#endif - if (localtime_env) zone = localtime_env; - } - - const std::string name = zone; -#if defined(_MSC_VER) - free(localtime_env); - free(tz_env); -#endif - - time_zone tz; - load_time_zone(name, &tz); // Falls back to UTC. + + // Allow ${TZ} to override to default zone. + char* tz_env = nullptr; +#if defined(_MSC_VER) + _dupenv_s(&tz_env, nullptr, "TZ"); +#else + tz_env = std::getenv("TZ"); +#endif + if (tz_env) zone = tz_env; + + // We only support the "[:]<zone-name>" form. + if (*zone == ':') ++zone; + + // Map "localtime" to a system-specific name, but + // allow ${LOCALTIME} to override the default name. + char* localtime_env = nullptr; + if (strcmp(zone, "localtime") == 0) { +#if defined(_MSC_VER) + // System-specific default is just "localtime". + _dupenv_s(&localtime_env, nullptr, "LOCALTIME"); +#else + zone = "/etc/localtime"; // System-specific default. + localtime_env = std::getenv("LOCALTIME"); +#endif + if (localtime_env) zone = localtime_env; + } + + const std::string name = zone; +#if defined(_MSC_VER) + free(localtime_env); + free(tz_env); +#endif + + time_zone tz; + load_time_zone(name, &tz); // Falls back to UTC. // TODO: Follow the RFC3339 "Unknown Local Offset Convention" and // arrange for %z to generate "-0000" when we don't know the local // offset because the load_time_zone() failed and we're using UTC. - return tz; -} - -} // namespace cctz + return tz; +} + +} // namespace cctz diff --git a/contrib/libs/cctz/src/time_zone_posix.cc b/contrib/libs/cctz/src/time_zone_posix.cc index 847db17cbc..9a4a38ace4 100644 --- a/contrib/libs/cctz/src/time_zone_posix.cc +++ b/contrib/libs/cctz/src/time_zone_posix.cc @@ -1,151 +1,151 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -#include "time_zone_posix.h" - -#include <cstddef> -#include <cstring> -#include <limits> -#include <string> - -namespace cctz { - -namespace { - -const char kDigits[] = "0123456789"; - -const char* ParseInt(const char* p, int min, int max, int* vp) { - int value = 0; - const char* op = p; - const int kMaxInt = std::numeric_limits<int>::max(); - for (; const char* dp = strchr(kDigits, *p); ++p) { - int d = static_cast<int>(dp - kDigits); - if (d >= 10) break; // '\0' - if (value > kMaxInt / 10) return nullptr; - value *= 10; - if (value > kMaxInt - d) return nullptr; - value += d; - } - if (p == op || value < min || value > max) return nullptr; - *vp = value; - return p; -} - -// abbr = <.*?> | [^-+,\d]{3,} -const char* ParseAbbr(const char* p, std::string* abbr) { - const char* op = p; - if (*p == '<') { // special zoneinfo <...> form - while (*++p != '>') { - if (*p == '\0') return nullptr; - } - abbr->assign(op + 1, static_cast<std::size_t>(p - op) - 1); - return ++p; - } - while (*p != '\0') { - if (strchr("-+,", *p)) break; - if (strchr(kDigits, *p)) break; - ++p; - } - if (p - op < 3) return nullptr; - abbr->assign(op, static_cast<std::size_t>(p - op)); - return p; -} - -// offset = [+|-]hh[:mm[:ss]] (aggregated into single seconds value) -const char* ParseOffset(const char* p, int min_hour, int max_hour, int sign, - std::int_fast32_t* offset) { - if (p == nullptr) return nullptr; - if (*p == '+' || *p == '-') { - if (*p++ == '-') sign = -sign; - } - int hours = 0; - int minutes = 0; - int seconds = 0; - - p = ParseInt(p, min_hour, max_hour, &hours); - if (p == nullptr) return nullptr; - if (*p == ':') { - p = ParseInt(p + 1, 0, 59, &minutes); - if (p == nullptr) return nullptr; - if (*p == ':') { - p = ParseInt(p + 1, 0, 59, &seconds); - if (p == nullptr) return nullptr; - } - } - *offset = sign * ((((hours * 60) + minutes) * 60) + seconds); - return p; -} - -// datetime = ( Jn | n | Mm.w.d ) [ / offset ] -const char* ParseDateTime(const char* p, PosixTransition* res) { - if (p != nullptr && *p == ',') { - if (*++p == 'M') { - int month = 0; - if ((p = ParseInt(p + 1, 1, 12, &month)) != nullptr && *p == '.') { - int week = 0; - if ((p = ParseInt(p + 1, 1, 5, &week)) != nullptr && *p == '.') { - int weekday = 0; - if ((p = ParseInt(p + 1, 0, 6, &weekday)) != nullptr) { - res->date.fmt = PosixTransition::M; +// +// 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. + +#include "time_zone_posix.h" + +#include <cstddef> +#include <cstring> +#include <limits> +#include <string> + +namespace cctz { + +namespace { + +const char kDigits[] = "0123456789"; + +const char* ParseInt(const char* p, int min, int max, int* vp) { + int value = 0; + const char* op = p; + const int kMaxInt = std::numeric_limits<int>::max(); + for (; const char* dp = strchr(kDigits, *p); ++p) { + int d = static_cast<int>(dp - kDigits); + if (d >= 10) break; // '\0' + if (value > kMaxInt / 10) return nullptr; + value *= 10; + if (value > kMaxInt - d) return nullptr; + value += d; + } + if (p == op || value < min || value > max) return nullptr; + *vp = value; + return p; +} + +// abbr = <.*?> | [^-+,\d]{3,} +const char* ParseAbbr(const char* p, std::string* abbr) { + const char* op = p; + if (*p == '<') { // special zoneinfo <...> form + while (*++p != '>') { + if (*p == '\0') return nullptr; + } + abbr->assign(op + 1, static_cast<std::size_t>(p - op) - 1); + return ++p; + } + while (*p != '\0') { + if (strchr("-+,", *p)) break; + if (strchr(kDigits, *p)) break; + ++p; + } + if (p - op < 3) return nullptr; + abbr->assign(op, static_cast<std::size_t>(p - op)); + return p; +} + +// offset = [+|-]hh[:mm[:ss]] (aggregated into single seconds value) +const char* ParseOffset(const char* p, int min_hour, int max_hour, int sign, + std::int_fast32_t* offset) { + if (p == nullptr) return nullptr; + if (*p == '+' || *p == '-') { + if (*p++ == '-') sign = -sign; + } + int hours = 0; + int minutes = 0; + int seconds = 0; + + p = ParseInt(p, min_hour, max_hour, &hours); + if (p == nullptr) return nullptr; + if (*p == ':') { + p = ParseInt(p + 1, 0, 59, &minutes); + if (p == nullptr) return nullptr; + if (*p == ':') { + p = ParseInt(p + 1, 0, 59, &seconds); + if (p == nullptr) return nullptr; + } + } + *offset = sign * ((((hours * 60) + minutes) * 60) + seconds); + return p; +} + +// datetime = ( Jn | n | Mm.w.d ) [ / offset ] +const char* ParseDateTime(const char* p, PosixTransition* res) { + if (p != nullptr && *p == ',') { + if (*++p == 'M') { + int month = 0; + if ((p = ParseInt(p + 1, 1, 12, &month)) != nullptr && *p == '.') { + int week = 0; + if ((p = ParseInt(p + 1, 1, 5, &week)) != nullptr && *p == '.') { + int weekday = 0; + if ((p = ParseInt(p + 1, 0, 6, &weekday)) != nullptr) { + res->date.fmt = PosixTransition::M; res->date.m.month = static_cast<std::int_fast8_t>(month); res->date.m.week = static_cast<std::int_fast8_t>(week); res->date.m.weekday = static_cast<std::int_fast8_t>(weekday); - } - } - } - } else if (*p == 'J') { - int day = 0; - if ((p = ParseInt(p + 1, 1, 365, &day)) != nullptr) { - res->date.fmt = PosixTransition::J; + } + } + } + } else if (*p == 'J') { + int day = 0; + if ((p = ParseInt(p + 1, 1, 365, &day)) != nullptr) { + res->date.fmt = PosixTransition::J; res->date.j.day = static_cast<std::int_fast16_t>(day); - } - } else { - int day = 0; - if ((p = ParseInt(p, 0, 365, &day)) != nullptr) { - res->date.fmt = PosixTransition::N; + } + } else { + int day = 0; + if ((p = ParseInt(p, 0, 365, &day)) != nullptr) { + res->date.fmt = PosixTransition::N; res->date.n.day = static_cast<std::int_fast16_t>(day); - } - } - } - if (p != nullptr) { - res->time.offset = 2 * 60 * 60; // default offset is 02:00:00 - if (*p == '/') p = ParseOffset(p + 1, -167, 167, 1, &res->time.offset); - } - return p; -} - -} // namespace - -// spec = std offset [ dst [ offset ] , datetime , datetime ] -bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) { - const char* p = spec.c_str(); - if (*p == ':') return false; - - p = ParseAbbr(p, &res->std_abbr); - p = ParseOffset(p, 0, 24, -1, &res->std_offset); - if (p == nullptr) return false; - if (*p == '\0') return true; - - p = ParseAbbr(p, &res->dst_abbr); - if (p == nullptr) return false; - res->dst_offset = res->std_offset + (60 * 60); // default - if (*p != ',') p = ParseOffset(p, 0, 24, -1, &res->dst_offset); - - p = ParseDateTime(p, &res->dst_start); - p = ParseDateTime(p, &res->dst_end); - - return p != nullptr && *p == '\0'; -} - -} // namespace cctz + } + } + } + if (p != nullptr) { + res->time.offset = 2 * 60 * 60; // default offset is 02:00:00 + if (*p == '/') p = ParseOffset(p + 1, -167, 167, 1, &res->time.offset); + } + return p; +} + +} // namespace + +// spec = std offset [ dst [ offset ] , datetime , datetime ] +bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) { + const char* p = spec.c_str(); + if (*p == ':') return false; + + p = ParseAbbr(p, &res->std_abbr); + p = ParseOffset(p, 0, 24, -1, &res->std_offset); + if (p == nullptr) return false; + if (*p == '\0') return true; + + p = ParseAbbr(p, &res->dst_abbr); + if (p == nullptr) return false; + res->dst_offset = res->std_offset + (60 * 60); // default + if (*p != ',') p = ParseOffset(p, 0, 24, -1, &res->dst_offset); + + p = ParseDateTime(p, &res->dst_start); + p = ParseDateTime(p, &res->dst_end); + + return p != nullptr && *p == '\0'; +} + +} // namespace cctz diff --git a/contrib/libs/cctz/src/time_zone_posix.h b/contrib/libs/cctz/src/time_zone_posix.h index aea93efdff..96e66b9f34 100644 --- a/contrib/libs/cctz/src/time_zone_posix.h +++ b/contrib/libs/cctz/src/time_zone_posix.h @@ -1,71 +1,71 @@ -// 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 -// +// 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 +// // https://www.apache.org/licenses/LICENSE-2.0 -// -// 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. - -// Parsing of a POSIX zone spec as described in the TZ part of section 8.3 in -// http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html. -// -// The current POSIX spec for America/Los_Angeles is "PST8PDT,M3.2.0,M11.1.0", -// which would be broken down as ... -// -// PosixTimeZone { -// std_abbr = "PST" -// std_offset = -28800 -// dst_abbr = "PDT" -// dst_offset = -25200 -// dst_start = PosixTransition { -// date { -// m { -// month = 3 -// week = 2 -// weekday = 0 -// } -// } -// time { -// offset = 7200 -// } -// } -// dst_end = PosixTransition { -// date { -// m { -// month = 11 -// week = 1 -// weekday = 0 -// } -// } -// time { -// offset = 7200 -// } -// } -// } - -#ifndef CCTZ_TIME_ZONE_POSIX_H_ -#define CCTZ_TIME_ZONE_POSIX_H_ - -#include <cstdint> -#include <string> - -namespace cctz { - -// The date/time of the transition. The date is specified as either: -// (J) the Nth day of the year (1 <= N <= 365), excluding leap days, or -// (N) the Nth day of the year (0 <= N <= 365), including leap days, or -// (M) the Nth weekday of a month (e.g., the 2nd Sunday in March). -// The time, specified as a day offset, identifies the particular moment -// of the transition, and may be negative or >= 24h, and in which case -// it would take us to another day, and perhaps week, or even month. -struct PosixTransition { - enum DateFormat { J, N, M }; +// +// 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. + +// Parsing of a POSIX zone spec as described in the TZ part of section 8.3 in +// http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html. +// +// The current POSIX spec for America/Los_Angeles is "PST8PDT,M3.2.0,M11.1.0", +// which would be broken down as ... +// +// PosixTimeZone { +// std_abbr = "PST" +// std_offset = -28800 +// dst_abbr = "PDT" +// dst_offset = -25200 +// dst_start = PosixTransition { +// date { +// m { +// month = 3 +// week = 2 +// weekday = 0 +// } +// } +// time { +// offset = 7200 +// } +// } +// dst_end = PosixTransition { +// date { +// m { +// month = 11 +// week = 1 +// weekday = 0 +// } +// } +// time { +// offset = 7200 +// } +// } +// } + +#ifndef CCTZ_TIME_ZONE_POSIX_H_ +#define CCTZ_TIME_ZONE_POSIX_H_ + +#include <cstdint> +#include <string> + +namespace cctz { + +// The date/time of the transition. The date is specified as either: +// (J) the Nth day of the year (1 <= N <= 365), excluding leap days, or +// (N) the Nth day of the year (0 <= N <= 365), including leap days, or +// (M) the Nth weekday of a month (e.g., the 2nd Sunday in March). +// The time, specified as a day offset, identifies the particular moment +// of the transition, and may be negative or >= 24h, and in which case +// it would take us to another day, and perhaps week, or even month. +struct PosixTransition { + enum DateFormat { J, N, M }; struct Date { struct NonLeapDay { @@ -80,45 +80,45 @@ struct PosixTransition { std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat }; - DateFormat fmt; + DateFormat fmt; - union { + union { NonLeapDay j; Day n; MonthWeekWeekday m; - }; + }; }; struct Time { - std::int_fast32_t offset; // seconds before/after 00:00:00 + std::int_fast32_t offset; // seconds before/after 00:00:00 }; Date date; Time time; -}; - -// The entirety of a POSIX-string specified time-zone rule. The standard -// abbreviation and offset are always given. If the time zone includes -// daylight saving, then the daylight abbrevation is non-empty and the -// remaining fields are also valid. Note that the start/end transitions -// are not ordered---in the southern hemisphere the transition to end -// daylight time occurs first in any particular year. -struct PosixTimeZone { - std::string std_abbr; - std::int_fast32_t std_offset; - - std::string dst_abbr; - std::int_fast32_t dst_offset; - PosixTransition dst_start; - PosixTransition dst_end; -}; - -// Breaks down a POSIX time-zone specification into its constituent pieces, -// filling in any missing values (DST offset, or start/end transition times) -// with the standard-defined defaults. Returns false if the specification -// could not be parsed (although some fields of *res may have been altered). -bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res); - -} // namespace cctz - -#endif // CCTZ_TIME_ZONE_POSIX_H_ +}; + +// The entirety of a POSIX-string specified time-zone rule. The standard +// abbreviation and offset are always given. If the time zone includes +// daylight saving, then the daylight abbrevation is non-empty and the +// remaining fields are also valid. Note that the start/end transitions +// are not ordered---in the southern hemisphere the transition to end +// daylight time occurs first in any particular year. +struct PosixTimeZone { + std::string std_abbr; + std::int_fast32_t std_offset; + + std::string dst_abbr; + std::int_fast32_t dst_offset; + PosixTransition dst_start; + PosixTransition dst_end; +}; + +// Breaks down a POSIX time-zone specification into its constituent pieces, +// filling in any missing values (DST offset, or start/end transition times) +// with the standard-defined defaults. Returns false if the specification +// could not be parsed (although some fields of *res may have been altered). +bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res); + +} // namespace cctz + +#endif // CCTZ_TIME_ZONE_POSIX_H_ diff --git a/contrib/libs/cctz/src/tzfile.h b/contrib/libs/cctz/src/tzfile.h index ee91104443..b86ce475c8 100644 --- a/contrib/libs/cctz/src/tzfile.h +++ b/contrib/libs/cctz/src/tzfile.h @@ -1,9 +1,9 @@ /* Layout and location of TZif files. */ -#ifndef TZFILE_H - -#define TZFILE_H +#ifndef TZFILE_H +#define TZFILE_H + /* ** This file is in the public domain, so clarified as of ** 1996-06-05 by Arthur David Olson. @@ -30,7 +30,7 @@ #endif /* !defined TZDEFAULT */ #ifndef TZDEFRULES -#define TZDEFRULES "posixrules" +#define TZDEFRULES "posixrules" #endif /* !defined TZDEFRULES */ @@ -40,38 +40,38 @@ ** Each file begins with. . . */ -#define TZ_MAGIC "TZif" +#define TZ_MAGIC "TZif" struct tzhead { - char tzh_magic[4]; /* TZ_MAGIC */ - char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ - char tzh_reserved[15]; /* reserved; must be zero */ + char tzh_magic[4]; /* TZ_MAGIC */ + char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */ + char tzh_reserved[15]; /* reserved; must be zero */ char tzh_ttisutcnt[4]; /* coded number of trans. time flags */ - char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ - char tzh_leapcnt[4]; /* coded number of leap seconds */ - char tzh_timecnt[4]; /* coded number of transition times */ - char tzh_typecnt[4]; /* coded number of local time types */ - char tzh_charcnt[4]; /* coded number of abbr. chars */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_leapcnt[4]; /* coded number of leap seconds */ + char tzh_timecnt[4]; /* coded number of transition times */ + char tzh_typecnt[4]; /* coded number of local time types */ + char tzh_charcnt[4]; /* coded number of abbr. chars */ }; /* ** . . .followed by. . . ** -** tzh_timecnt (char [4])s coded transition times a la time(2) -** tzh_timecnt (unsigned char)s types of local time starting at above -** tzh_typecnt repetitions of -** one (char [4]) coded UT offset in seconds -** one (unsigned char) used to set tm_isdst -** one (unsigned char) that's an abbreviation list index -** tzh_charcnt (char)s '\0'-terminated zone abbreviations -** tzh_leapcnt repetitions of -** one (char [4]) coded leap second transition times -** one (char [4]) total correction after above -** tzh_ttisstdcnt (char)s indexed by type; if 1, transition -** time is standard time, if 0, +** tzh_timecnt (char [4])s coded transition times a la time(2) +** tzh_timecnt (unsigned char)s types of local time starting at above +** tzh_typecnt repetitions of +** one (char [4]) coded UT offset in seconds +** one (unsigned char) used to set tm_isdst +** one (unsigned char) that's an abbreviation list index +** tzh_charcnt (char)s '\0'-terminated zone abbreviations +** tzh_leapcnt repetitions of +** one (char [4]) coded leap second transition times +** one (char [4]) total correction after above +** tzh_ttisstdcnt (char)s indexed by type; if 1, transition +** time is standard time, if 0, ** transition time is local (wall clock) ** time; if absent, transition times are -** assumed to be local time +** assumed to be local time ** tzh_ttisutcnt (char)s indexed by type; if 1, transition ** time is UT, if 0, transition time is ** local time; if absent, transition @@ -103,21 +103,21 @@ struct tzhead { */ #ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 2000 +#define TZ_MAX_TIMES 2000 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES /* This must be at least 17 for Europe/Samara and Europe/Vilnius. */ -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ +#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ #endif /* !defined TZ_MAX_TYPES */ #ifndef TZ_MAX_CHARS -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ - /* (limited by what unsigned chars can hold) */ +#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ + /* (limited by what unsigned chars can hold) */ #endif /* !defined TZ_MAX_CHARS */ #ifndef TZ_MAX_LEAPS -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ +#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ #endif /* !defined TZ_MAX_LEAPS */ -#endif /* !defined TZFILE_H */ +#endif /* !defined TZFILE_H */ |