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 | |
parent | dc5feacd15e76abe98d23fe0d4ea3c02cb8de60f (diff) | |
download | ydb-af66956edf116b93d5a07894ccb61dd4447d0b34.tar.gz |
Restoring authorship annotation for <petrk@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib')
24 files changed, 3667 insertions, 3667 deletions
diff --git a/contrib/libs/cctz/include/cctz/civil_time.h b/contrib/libs/cctz/include/cctz/civil_time.h index 57bd86cc06..0b339e8248 100644 --- a/contrib/libs/cctz/include/cctz/civil_time.h +++ b/contrib/libs/cctz/include/cctz/civil_time.h @@ -1,325 +1,325 @@ -// 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_CIVIL_TIME_H_ -#define CCTZ_CIVIL_TIME_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. + +#ifndef CCTZ_CIVIL_TIME_H_ +#define CCTZ_CIVIL_TIME_H_ + #include "cctz/civil_time_detail.h" - -namespace cctz { - -// The term "civil time" refers to the legally recognized human-scale time -// that is represented by the six fields YYYY-MM-DD hh:mm:ss. Modern-day civil -// time follows the Gregorian Calendar and is a time-zone-independent concept. -// A "date" is perhaps the most common example of a civil time (represented in -// this library as cctz::civil_day). This library provides six classes and a -// handful of functions that help with rounding, iterating, and arithmetic on -// civil times while avoiding complications like daylight-saving time (DST). -// -// The following six classes form the core of this civil-time library: -// -// * civil_second -// * civil_minute -// * civil_hour -// * civil_day -// * civil_month -// * civil_year -// -// Each class is a simple value type with the same interface for construction -// and the same six accessors for each of the civil fields (year, month, day, -// hour, minute, and second, aka YMDHMS). These classes differ only in their -// alignment, which is indicated by the type name and specifies the field on -// which arithmetic operates. -// -// Each class can be constructed by passing up to six optional integer -// arguments representing the YMDHMS fields (in that order) to the -// constructor. Omitted fields are assigned their minimum valid value. Hours, -// minutes, and seconds will be set to 0, month and day will be set to 1, and -// since there is no minimum valid year, it will be set to 1970. So, a -// default-constructed civil-time object will have YMDHMS fields representing -// "1970-01-01 00:00:00". Fields that are out-of-range are normalized (e.g., -// October 32 -> November 1) so that all civil-time objects represent valid -// values. -// -// Each civil-time class is aligned to the civil-time field indicated in the -// class's name after normalization. Alignment is performed by setting all the -// inferior fields to their minimum valid value (as described above). The -// following are examples of how each of the six types would align the fields -// representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the -// string format used here is not important; it's just a shorthand way of -// showing the six YMDHMS fields.) -// -// civil_second 2015-11-22 12:34:56 -// civil_minute 2015-11-22 12:34:00 -// civil_hour 2015-11-22 12:00:00 -// civil_day 2015-11-22 00:00:00 -// civil_month 2015-11-01 00:00:00 -// civil_year 2015-01-01 00:00:00 -// -// Each civil-time type performs arithmetic on the field to which it is -// aligned. This means that adding 1 to a civil_day increments the day field -// (normalizing as necessary), and subtracting 7 from a civil_month operates -// on the month field (normalizing as necessary). All arithmetic produces a -// valid civil time. Difference requires two similarly aligned civil-time -// objects and returns the scalar answer in units of the objects' alignment. -// For example, the difference between two civil_hour objects will give an -// answer in units of civil hours. -// -// In addition to the six civil-time types just described, there are -// a handful of helper functions and algorithms for performing common -// calculations. These are described below. -// -// Note: In C++14 and later, this library is usable in a constexpr context. -// -// CONSTRUCTION: -// -// Each of the civil-time types can be constructed in two ways: by directly -// passing to the constructor up to six (optional) integers representing the -// YMDHMS fields, or by copying the YMDHMS fields from a differently aligned -// civil-time type. -// -// civil_day default_value; // 1970-01-01 00:00:00 -// -// civil_day a(2015, 2, 3); // 2015-02-03 00:00:00 -// civil_day b(2015, 2, 3, 4, 5, 6); // 2015-02-03 00:00:00 -// civil_day c(2015); // 2015-01-01 00:00:00 -// -// civil_second ss(2015, 2, 3, 4, 5, 6); // 2015-02-03 04:05:06 -// civil_minute mm(ss); // 2015-02-03 04:05:00 -// civil_hour hh(mm); // 2015-02-03 04:00:00 -// civil_day d(hh); // 2015-02-03 00:00:00 -// civil_month m(d); // 2015-02-01 00:00:00 -// civil_year y(m); // 2015-01-01 00:00:00 -// -// m = civil_month(y); // 2015-01-01 00:00:00 -// d = civil_day(m); // 2015-01-01 00:00:00 -// hh = civil_hour(d); // 2015-01-01 00:00:00 -// mm = civil_minute(hh); // 2015-01-01 00:00:00 -// ss = civil_second(mm); // 2015-01-01 00:00:00 -// -// ALIGNMENT CONVERSION: -// -// The alignment of a civil-time object cannot change, but the object may be -// used to construct a new object with a different alignment. This is referred -// to as "realigning". When realigning to a type with the same or more -// precision (e.g., civil_day -> civil_second), the conversion may be -// performed implicitly since no information is lost. However, if information -// could be discarded (e.g., civil_second -> civil_day), the conversion must -// be explicit at the call site. -// -// void fun(const civil_day& day); -// -// civil_second cs; -// fun(cs); // Won't compile because data may be discarded -// fun(civil_day(cs)); // OK: explicit conversion -// -// civil_day cd; -// fun(cd); // OK: no conversion needed -// -// civil_month cm; -// fun(cm); // OK: implicit conversion to civil_day -// -// NORMALIZATION: -// -// Integer arguments passed to the constructor may be out-of-range, in which -// case they are normalized to produce a valid civil-time object. This enables -// natural arithmetic on constructor arguments without worrying about the -// field's range. Normalization guarantees that there are no invalid -// civil-time objects. -// -// civil_day d(2016, 10, 32); // Out-of-range day; normalized to 2016-11-01 -// -// Note: If normalization is undesired, you can signal an error by comparing -// the constructor arguments to the normalized values returned by the YMDHMS -// properties. -// -// PROPERTIES: -// -// All civil-time types have accessors for all six of the civil-time fields: -// year, month, day, hour, minute, and second. Recall that fields inferior to + +namespace cctz { + +// The term "civil time" refers to the legally recognized human-scale time +// that is represented by the six fields YYYY-MM-DD hh:mm:ss. Modern-day civil +// time follows the Gregorian Calendar and is a time-zone-independent concept. +// A "date" is perhaps the most common example of a civil time (represented in +// this library as cctz::civil_day). This library provides six classes and a +// handful of functions that help with rounding, iterating, and arithmetic on +// civil times while avoiding complications like daylight-saving time (DST). +// +// The following six classes form the core of this civil-time library: +// +// * civil_second +// * civil_minute +// * civil_hour +// * civil_day +// * civil_month +// * civil_year +// +// Each class is a simple value type with the same interface for construction +// and the same six accessors for each of the civil fields (year, month, day, +// hour, minute, and second, aka YMDHMS). These classes differ only in their +// alignment, which is indicated by the type name and specifies the field on +// which arithmetic operates. +// +// Each class can be constructed by passing up to six optional integer +// arguments representing the YMDHMS fields (in that order) to the +// constructor. Omitted fields are assigned their minimum valid value. Hours, +// minutes, and seconds will be set to 0, month and day will be set to 1, and +// since there is no minimum valid year, it will be set to 1970. So, a +// default-constructed civil-time object will have YMDHMS fields representing +// "1970-01-01 00:00:00". Fields that are out-of-range are normalized (e.g., +// October 32 -> November 1) so that all civil-time objects represent valid +// values. +// +// Each civil-time class is aligned to the civil-time field indicated in the +// class's name after normalization. Alignment is performed by setting all the +// inferior fields to their minimum valid value (as described above). The +// following are examples of how each of the six types would align the fields +// representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the +// string format used here is not important; it's just a shorthand way of +// showing the six YMDHMS fields.) +// +// civil_second 2015-11-22 12:34:56 +// civil_minute 2015-11-22 12:34:00 +// civil_hour 2015-11-22 12:00:00 +// civil_day 2015-11-22 00:00:00 +// civil_month 2015-11-01 00:00:00 +// civil_year 2015-01-01 00:00:00 +// +// Each civil-time type performs arithmetic on the field to which it is +// aligned. This means that adding 1 to a civil_day increments the day field +// (normalizing as necessary), and subtracting 7 from a civil_month operates +// on the month field (normalizing as necessary). All arithmetic produces a +// valid civil time. Difference requires two similarly aligned civil-time +// objects and returns the scalar answer in units of the objects' alignment. +// For example, the difference between two civil_hour objects will give an +// answer in units of civil hours. +// +// In addition to the six civil-time types just described, there are +// a handful of helper functions and algorithms for performing common +// calculations. These are described below. +// +// Note: In C++14 and later, this library is usable in a constexpr context. +// +// CONSTRUCTION: +// +// Each of the civil-time types can be constructed in two ways: by directly +// passing to the constructor up to six (optional) integers representing the +// YMDHMS fields, or by copying the YMDHMS fields from a differently aligned +// civil-time type. +// +// civil_day default_value; // 1970-01-01 00:00:00 +// +// civil_day a(2015, 2, 3); // 2015-02-03 00:00:00 +// civil_day b(2015, 2, 3, 4, 5, 6); // 2015-02-03 00:00:00 +// civil_day c(2015); // 2015-01-01 00:00:00 +// +// civil_second ss(2015, 2, 3, 4, 5, 6); // 2015-02-03 04:05:06 +// civil_minute mm(ss); // 2015-02-03 04:05:00 +// civil_hour hh(mm); // 2015-02-03 04:00:00 +// civil_day d(hh); // 2015-02-03 00:00:00 +// civil_month m(d); // 2015-02-01 00:00:00 +// civil_year y(m); // 2015-01-01 00:00:00 +// +// m = civil_month(y); // 2015-01-01 00:00:00 +// d = civil_day(m); // 2015-01-01 00:00:00 +// hh = civil_hour(d); // 2015-01-01 00:00:00 +// mm = civil_minute(hh); // 2015-01-01 00:00:00 +// ss = civil_second(mm); // 2015-01-01 00:00:00 +// +// ALIGNMENT CONVERSION: +// +// The alignment of a civil-time object cannot change, but the object may be +// used to construct a new object with a different alignment. This is referred +// to as "realigning". When realigning to a type with the same or more +// precision (e.g., civil_day -> civil_second), the conversion may be +// performed implicitly since no information is lost. However, if information +// could be discarded (e.g., civil_second -> civil_day), the conversion must +// be explicit at the call site. +// +// void fun(const civil_day& day); +// +// civil_second cs; +// fun(cs); // Won't compile because data may be discarded +// fun(civil_day(cs)); // OK: explicit conversion +// +// civil_day cd; +// fun(cd); // OK: no conversion needed +// +// civil_month cm; +// fun(cm); // OK: implicit conversion to civil_day +// +// NORMALIZATION: +// +// Integer arguments passed to the constructor may be out-of-range, in which +// case they are normalized to produce a valid civil-time object. This enables +// natural arithmetic on constructor arguments without worrying about the +// field's range. Normalization guarantees that there are no invalid +// civil-time objects. +// +// civil_day d(2016, 10, 32); // Out-of-range day; normalized to 2016-11-01 +// +// Note: If normalization is undesired, you can signal an error by comparing +// the constructor arguments to the normalized values returned by the YMDHMS +// properties. +// +// PROPERTIES: +// +// All civil-time types have accessors for all six of the civil-time fields: +// year, month, day, hour, minute, and second. Recall that fields inferior to // the type's alignment will be set to their minimum valid value. -// -// civil_day d(2015, 6, 28); -// // d.year() == 2015 -// // d.month() == 6 -// // d.day() == 28 -// // d.hour() == 0 -// // d.minute() == 0 -// // d.second() == 0 -// -// COMPARISON: -// -// Comparison always considers all six YMDHMS fields, regardless of the type's -// alignment. Comparison between differently aligned civil-time types is -// allowed. -// -// civil_day feb_3(2015, 2, 3); // 2015-02-03 00:00:00 -// civil_day mar_4(2015, 3, 4); // 2015-03-04 00:00:00 -// // feb_3 < mar_4 -// // civil_year(feb_3) == civil_year(mar_4) -// -// civil_second feb_3_noon(2015, 2, 3, 12, 0, 0); // 2015-02-03 12:00:00 -// // feb_3 < feb_3_noon -// // feb_3 == civil_day(feb_3_noon) -// -// // Iterates all the days of February 2015. -// for (civil_day d(2015, 2, 1); d < civil_month(2015, 3); ++d) { -// // ... -// } -// -// STREAMING: -// -// Each civil-time type may be sent to an output stream using operator<<(). -// The output format follows the pattern "YYYY-MM-DDThh:mm:ss" where fields -// inferior to the type's alignment are omitted. -// -// civil_second cs(2015, 2, 3, 4, 5, 6); -// std::cout << cs << "\n"; // Outputs: 2015-02-03T04:05:06 -// -// civil_day cd(cs); -// std::cout << cd << "\n"; // Outputs: 2015-02-03 -// -// civil_year cy(cs); -// std::cout << cy << "\n"; // Outputs: 2015 -// -// ARITHMETIC: -// -// Civil-time types support natural arithmetic operators such as addition, -// subtraction, and difference. Arithmetic operates on the civil-time field -// indicated in the type's name. Difference requires arguments with the same -// alignment and returns the answer in units of the alignment. -// -// civil_day a(2015, 2, 3); -// ++a; // 2015-02-04 00:00:00 -// --a; // 2015-02-03 00:00:00 -// civil_day b = a + 1; // 2015-02-04 00:00:00 -// civil_day c = 1 + b; // 2015-02-05 00:00:00 -// int n = c - a; // n = 2 (civil days) -// int m = c - civil_month(c); // Won't compile: different types. -// -// EXAMPLE: Adding a month to January 31. -// -// One of the classic questions that arises when considering a civil-time -// library (or a date library or a date/time library) is this: "What happens -// when you add a month to January 31?" This is an interesting question -// because there could be a number of possible answers: -// -// 1. March 3 (or 2 if a leap year). This may make sense if the operation -// wants the equivalent of February 31. -// 2. February 28 (or 29 if a leap year). This may make sense if the operation -// wants the last day of January to go to the last day of February. -// 3. Error. The caller may get some error, an exception, an invalid date -// object, or maybe false is returned. This may make sense because there is -// no single unambiguously correct answer to the question. -// -// Practically speaking, any answer that is not what the programmer intended -// is the wrong answer. -// -// This civil-time library avoids the problem by making it impossible to ask -// ambiguous questions. All civil-time objects are aligned to a particular -// civil-field boundary (such as aligned to a year, month, day, hour, minute, -// or second), and arithmetic operates on the field to which the object is -// aligned. This means that in order to "add a month" the object must first be -// aligned to a month boundary, which is equivalent to the first day of that -// month. -// -// Of course, there are ways to compute an answer the question at hand using -// this civil-time library, but they require the programmer to be explicit -// about the answer they expect. To illustrate, let's see how to compute all -// three of the above possible answers to the question of "Jan 31 plus 1 -// month": -// -// const civil_day d(2015, 1, 31); -// -// // Answer 1: -// // Add 1 to the month field in the constructor, and rely on normalization. -// const auto ans_normalized = civil_day(d.year(), d.month() + 1, d.day()); -// // ans_normalized == 2015-03-03 (aka Feb 31) -// -// // Answer 2: -// // Add 1 to month field, capping to the end of next month. -// const auto next_month = civil_month(d) + 1; -// const auto last_day_of_next_month = civil_day(next_month + 1) - 1; -// const auto ans_capped = std::min(ans_normalized, last_day_of_next_month); -// // ans_capped == 2015-02-28 -// -// // Answer 3: -// // Signal an error if the normalized answer is not in next month. -// if (civil_month(ans_normalized) != next_month) { -// // error, month overflow -// } -// -using civil_year = detail::civil_year; -using civil_month = detail::civil_month; -using civil_day = detail::civil_day; -using civil_hour = detail::civil_hour; -using civil_minute = detail::civil_minute; -using civil_second = detail::civil_second; - -// An enum class with members monday, tuesday, wednesday, thursday, friday, -// saturday, and sunday. These enum values may be sent to an output stream -// using operator<<(). The result is the full weekday name in English with a -// leading capital letter. -// -// weekday wd = weekday::thursday; -// std::cout << wd << "\n"; // Outputs: Thursday -// -using detail::weekday; - +// +// civil_day d(2015, 6, 28); +// // d.year() == 2015 +// // d.month() == 6 +// // d.day() == 28 +// // d.hour() == 0 +// // d.minute() == 0 +// // d.second() == 0 +// +// COMPARISON: +// +// Comparison always considers all six YMDHMS fields, regardless of the type's +// alignment. Comparison between differently aligned civil-time types is +// allowed. +// +// civil_day feb_3(2015, 2, 3); // 2015-02-03 00:00:00 +// civil_day mar_4(2015, 3, 4); // 2015-03-04 00:00:00 +// // feb_3 < mar_4 +// // civil_year(feb_3) == civil_year(mar_4) +// +// civil_second feb_3_noon(2015, 2, 3, 12, 0, 0); // 2015-02-03 12:00:00 +// // feb_3 < feb_3_noon +// // feb_3 == civil_day(feb_3_noon) +// +// // Iterates all the days of February 2015. +// for (civil_day d(2015, 2, 1); d < civil_month(2015, 3); ++d) { +// // ... +// } +// +// STREAMING: +// +// Each civil-time type may be sent to an output stream using operator<<(). +// The output format follows the pattern "YYYY-MM-DDThh:mm:ss" where fields +// inferior to the type's alignment are omitted. +// +// civil_second cs(2015, 2, 3, 4, 5, 6); +// std::cout << cs << "\n"; // Outputs: 2015-02-03T04:05:06 +// +// civil_day cd(cs); +// std::cout << cd << "\n"; // Outputs: 2015-02-03 +// +// civil_year cy(cs); +// std::cout << cy << "\n"; // Outputs: 2015 +// +// ARITHMETIC: +// +// Civil-time types support natural arithmetic operators such as addition, +// subtraction, and difference. Arithmetic operates on the civil-time field +// indicated in the type's name. Difference requires arguments with the same +// alignment and returns the answer in units of the alignment. +// +// civil_day a(2015, 2, 3); +// ++a; // 2015-02-04 00:00:00 +// --a; // 2015-02-03 00:00:00 +// civil_day b = a + 1; // 2015-02-04 00:00:00 +// civil_day c = 1 + b; // 2015-02-05 00:00:00 +// int n = c - a; // n = 2 (civil days) +// int m = c - civil_month(c); // Won't compile: different types. +// +// EXAMPLE: Adding a month to January 31. +// +// One of the classic questions that arises when considering a civil-time +// library (or a date library or a date/time library) is this: "What happens +// when you add a month to January 31?" This is an interesting question +// because there could be a number of possible answers: +// +// 1. March 3 (or 2 if a leap year). This may make sense if the operation +// wants the equivalent of February 31. +// 2. February 28 (or 29 if a leap year). This may make sense if the operation +// wants the last day of January to go to the last day of February. +// 3. Error. The caller may get some error, an exception, an invalid date +// object, or maybe false is returned. This may make sense because there is +// no single unambiguously correct answer to the question. +// +// Practically speaking, any answer that is not what the programmer intended +// is the wrong answer. +// +// This civil-time library avoids the problem by making it impossible to ask +// ambiguous questions. All civil-time objects are aligned to a particular +// civil-field boundary (such as aligned to a year, month, day, hour, minute, +// or second), and arithmetic operates on the field to which the object is +// aligned. This means that in order to "add a month" the object must first be +// aligned to a month boundary, which is equivalent to the first day of that +// month. +// +// Of course, there are ways to compute an answer the question at hand using +// this civil-time library, but they require the programmer to be explicit +// about the answer they expect. To illustrate, let's see how to compute all +// three of the above possible answers to the question of "Jan 31 plus 1 +// month": +// +// const civil_day d(2015, 1, 31); +// +// // Answer 1: +// // Add 1 to the month field in the constructor, and rely on normalization. +// const auto ans_normalized = civil_day(d.year(), d.month() + 1, d.day()); +// // ans_normalized == 2015-03-03 (aka Feb 31) +// +// // Answer 2: +// // Add 1 to month field, capping to the end of next month. +// const auto next_month = civil_month(d) + 1; +// const auto last_day_of_next_month = civil_day(next_month + 1) - 1; +// const auto ans_capped = std::min(ans_normalized, last_day_of_next_month); +// // ans_capped == 2015-02-28 +// +// // Answer 3: +// // Signal an error if the normalized answer is not in next month. +// if (civil_month(ans_normalized) != next_month) { +// // error, month overflow +// } +// +using civil_year = detail::civil_year; +using civil_month = detail::civil_month; +using civil_day = detail::civil_day; +using civil_hour = detail::civil_hour; +using civil_minute = detail::civil_minute; +using civil_second = detail::civil_second; + +// An enum class with members monday, tuesday, wednesday, thursday, friday, +// saturday, and sunday. These enum values may be sent to an output stream +// using operator<<(). The result is the full weekday name in English with a +// leading capital letter. +// +// weekday wd = weekday::thursday; +// std::cout << wd << "\n"; // Outputs: Thursday +// +using detail::weekday; + // Returns the weekday for the given civil-time value. -// -// civil_day a(2015, 8, 13); -// weekday wd = get_weekday(a); // wd == weekday::thursday -// -using detail::get_weekday; - -// Returns the civil_day that strictly follows or precedes the given -// civil_day, and that falls on the given weekday. -// -// For example, given: -// -// August 2015 -// Su Mo Tu We Th Fr Sa -// 1 -// 2 3 4 5 6 7 8 -// 9 10 11 12 13 14 15 -// 16 17 18 19 20 21 22 -// 23 24 25 26 27 28 29 -// 30 31 -// -// civil_day a(2015, 8, 13); // get_weekday(a) == weekday::thursday -// civil_day b = next_weekday(a, weekday::thursday); // b = 2015-08-20 -// civil_day c = prev_weekday(a, weekday::thursday); // c = 2015-08-06 -// -// civil_day d = ... -// // Gets the following Thursday if d is not already Thursday +// +// civil_day a(2015, 8, 13); +// weekday wd = get_weekday(a); // wd == weekday::thursday +// +using detail::get_weekday; + +// Returns the civil_day that strictly follows or precedes the given +// civil_day, and that falls on the given weekday. +// +// For example, given: +// +// August 2015 +// Su Mo Tu We Th Fr Sa +// 1 +// 2 3 4 5 6 7 8 +// 9 10 11 12 13 14 15 +// 16 17 18 19 20 21 22 +// 23 24 25 26 27 28 29 +// 30 31 +// +// civil_day a(2015, 8, 13); // get_weekday(a) == weekday::thursday +// civil_day b = next_weekday(a, weekday::thursday); // b = 2015-08-20 +// civil_day c = prev_weekday(a, weekday::thursday); // c = 2015-08-06 +// +// civil_day d = ... +// // Gets the following Thursday if d is not already Thursday // civil_day thurs1 = next_weekday(d - 1, weekday::thursday); -// // Gets the previous Thursday if d is not already Thursday +// // Gets the previous Thursday if d is not already Thursday // civil_day thurs2 = prev_weekday(d + 1, weekday::thursday); -// -using detail::next_weekday; -using detail::prev_weekday; - +// +using detail::next_weekday; +using detail::prev_weekday; + // Returns the day-of-year for the given civil-time value. -// -// civil_day a(2015, 1, 1); -// int yd_jan_1 = get_yearday(a); // yd_jan_1 = 1 -// civil_day b(2015, 12, 31); -// int yd_dec_31 = get_yearday(b); // yd_dec_31 = 365 -// -using detail::get_yearday; - -} // namespace cctz - -#endif // CCTZ_CIVIL_TIME_H_ +// +// civil_day a(2015, 1, 1); +// int yd_jan_1 = get_yearday(a); // yd_jan_1 = 1 +// civil_day b(2015, 12, 31); +// int yd_dec_31 = get_yearday(b); // yd_dec_31 = 365 +// +using detail::get_yearday; + +} // namespace cctz + +#endif // CCTZ_CIVIL_TIME_H_ diff --git a/contrib/libs/cctz/include/cctz/civil_time_detail.h b/contrib/libs/cctz/include/cctz/civil_time_detail.h index ea1a1dc276..22d958b3cf 100644 --- a/contrib/libs/cctz/include/cctz/civil_time_detail.h +++ b/contrib/libs/cctz/include/cctz/civil_time_detail.h @@ -1,528 +1,528 @@ -// 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_CIVIL_TIME_DETAIL_H_ -#define CCTZ_CIVIL_TIME_DETAIL_H_ - -#include <cstdint> -#include <limits> -#include <ostream> -#include <type_traits> - +// +// 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_CIVIL_TIME_DETAIL_H_ +#define CCTZ_CIVIL_TIME_DETAIL_H_ + +#include <cstdint> +#include <limits> +#include <ostream> +#include <type_traits> + // Disable constexpr support unless we are in C++14 mode. #if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910) -#define CONSTEXPR_D constexpr // data -#define CONSTEXPR_F constexpr // function -#define CONSTEXPR_M constexpr // member -#else -#define CONSTEXPR_D const -#define CONSTEXPR_F inline -#define CONSTEXPR_M -#endif - -namespace cctz { - -// Support years that at least span the range of 64-bit time_t values. -using year_t = std::int_fast64_t; - -// Type alias that indicates an argument is not normalized (e.g., the -// constructor parameters and operands/results of addition/subtraction). -using diff_t = std::int_fast64_t; - -namespace detail { - -// Type aliases that indicate normalized argument values. -using month_t = std::int_fast8_t; // [1:12] -using day_t = std::int_fast8_t; // [1:31] -using hour_t = std::int_fast8_t; // [0:23] -using minute_t = std::int_fast8_t; // [0:59] -using second_t = std::int_fast8_t; // [0:59] - -// Normalized civil-time fields: Y-M-D HH:MM:SS. -struct fields { - CONSTEXPR_M fields(year_t year, month_t month, day_t day, - hour_t hour, minute_t minute, second_t second) - : y(year), m(month), d(day), hh(hour), mm(minute), ss(second) {} - std::int_least64_t y; - std::int_least8_t m; - std::int_least8_t d; - std::int_least8_t hh; - std::int_least8_t mm; - std::int_least8_t ss; -}; - -struct second_tag {}; -struct minute_tag : second_tag {}; -struct hour_tag : minute_tag {}; -struct day_tag : hour_tag {}; -struct month_tag : day_tag {}; -struct year_tag : month_tag {}; - -//////////////////////////////////////////////////////////////////////// - -// Field normalization (without avoidable overflow). - -namespace impl { - -CONSTEXPR_F bool is_leap_year(year_t y) noexcept { - return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); -} -CONSTEXPR_F int year_index(year_t y, month_t m) noexcept { - return (static_cast<int>((y + (m > 2)) % 400) + 400) % 400; -} -CONSTEXPR_F int days_per_century(year_t y, month_t m) noexcept { - const int yi = year_index(y, m); - return 36524 + (yi == 0 || yi > 300); -} -CONSTEXPR_F int days_per_4years(year_t y, month_t m) noexcept { - const int yi = year_index(y, m); - return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96); -} -CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept { - return is_leap_year(y + (m > 2)) ? 366 : 365; -} -CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept { - CONSTEXPR_D int k_days_per_month[1 + 12] = { - -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // non leap year - }; - return k_days_per_month[m] + (m == 2 && is_leap_year(y)); -} - -CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, - hour_t hh, minute_t mm, second_t ss) noexcept { +#define CONSTEXPR_D constexpr // data +#define CONSTEXPR_F constexpr // function +#define CONSTEXPR_M constexpr // member +#else +#define CONSTEXPR_D const +#define CONSTEXPR_F inline +#define CONSTEXPR_M +#endif + +namespace cctz { + +// Support years that at least span the range of 64-bit time_t values. +using year_t = std::int_fast64_t; + +// Type alias that indicates an argument is not normalized (e.g., the +// constructor parameters and operands/results of addition/subtraction). +using diff_t = std::int_fast64_t; + +namespace detail { + +// Type aliases that indicate normalized argument values. +using month_t = std::int_fast8_t; // [1:12] +using day_t = std::int_fast8_t; // [1:31] +using hour_t = std::int_fast8_t; // [0:23] +using minute_t = std::int_fast8_t; // [0:59] +using second_t = std::int_fast8_t; // [0:59] + +// Normalized civil-time fields: Y-M-D HH:MM:SS. +struct fields { + CONSTEXPR_M fields(year_t year, month_t month, day_t day, + hour_t hour, minute_t minute, second_t second) + : y(year), m(month), d(day), hh(hour), mm(minute), ss(second) {} + std::int_least64_t y; + std::int_least8_t m; + std::int_least8_t d; + std::int_least8_t hh; + std::int_least8_t mm; + std::int_least8_t ss; +}; + +struct second_tag {}; +struct minute_tag : second_tag {}; +struct hour_tag : minute_tag {}; +struct day_tag : hour_tag {}; +struct month_tag : day_tag {}; +struct year_tag : month_tag {}; + +//////////////////////////////////////////////////////////////////////// + +// Field normalization (without avoidable overflow). + +namespace impl { + +CONSTEXPR_F bool is_leap_year(year_t y) noexcept { + return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); +} +CONSTEXPR_F int year_index(year_t y, month_t m) noexcept { + return (static_cast<int>((y + (m > 2)) % 400) + 400) % 400; +} +CONSTEXPR_F int days_per_century(year_t y, month_t m) noexcept { + const int yi = year_index(y, m); + return 36524 + (yi == 0 || yi > 300); +} +CONSTEXPR_F int days_per_4years(year_t y, month_t m) noexcept { + const int yi = year_index(y, m); + return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96); +} +CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept { + return is_leap_year(y + (m > 2)) ? 366 : 365; +} +CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept { + CONSTEXPR_D int k_days_per_month[1 + 12] = { + -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // non leap year + }; + return k_days_per_month[m] + (m == 2 && is_leap_year(y)); +} + +CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, + hour_t hh, minute_t mm, second_t ss) noexcept { year_t ey = y % 400; const year_t oey = ey; ey += (cd / 146097) * 400; - cd %= 146097; - if (cd < 0) { + cd %= 146097; + if (cd < 0) { ey -= 400; - cd += 146097; - } + cd += 146097; + } ey += (d / 146097) * 400; - d = d % 146097 + cd; - if (d > 0) { - if (d > 146097) { + d = d % 146097 + cd; + if (d > 0) { + if (d > 146097) { ey += 400; - d -= 146097; - } - } else { - if (d > -365) { - // We often hit the previous year when stepping a civil time backwards, - // so special case it to avoid counting up by 100/4/1-year chunks. + d -= 146097; + } + } else { + if (d > -365) { + // We often hit the previous year when stepping a civil time backwards, + // so special case it to avoid counting up by 100/4/1-year chunks. ey -= 1; d += days_per_year(ey, m); - } else { + } else { ey -= 400; - d += 146097; - } - } - if (d > 365) { + d += 146097; + } + } + if (d > 365) { for (;;) { int n = days_per_century(ey, m); if (d <= n) break; - d -= n; + d -= n; ey += 100; - } + } for (;;) { int n = days_per_4years(ey, m); if (d <= n) break; - d -= n; + d -= n; ey += 4; - } + } for (;;) { int n = days_per_year(ey, m); if (d <= n) break; - d -= n; + d -= n; ++ey; - } - } - if (d > 28) { + } + } + if (d > 28) { for (;;) { int n = days_per_month(ey, m); if (d <= n) break; - d -= n; - if (++m > 12) { + d -= n; + if (++m > 12) { ++ey; - m = 1; - } - } - } + m = 1; + } + } + } return fields(y + (ey - oey), m, static_cast<day_t>(d), hh, mm, ss); -} -CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, - hour_t hh, minute_t mm, second_t ss) noexcept { - if (m != 12) { - y += m / 12; - m %= 12; - if (m <= 0) { - y -= 1; - m += 12; - } - } - return n_day(y, static_cast<month_t>(m), d, cd, hh, mm, ss); -} -CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, - diff_t hh, minute_t mm, second_t ss) noexcept { - cd += hh / 24; - hh %= 24; - if (hh < 0) { - cd -= 1; - hh += 24; - } - return n_mon(y, m, d, cd, static_cast<hour_t>(hh), mm, ss); -} -CONSTEXPR_F fields n_min(year_t y, diff_t m, diff_t d, diff_t hh, diff_t ch, - diff_t mm, second_t ss) noexcept { - ch += mm / 60; - mm %= 60; - if (mm < 0) { - ch -= 1; - mm += 60; - } - return n_hour(y, m, d, hh / 24 + ch / 24, hh % 24 + ch % 24, - static_cast<minute_t>(mm), ss); -} -CONSTEXPR_F fields n_sec(year_t y, diff_t m, diff_t d, diff_t hh, diff_t mm, - diff_t ss) noexcept { - // Optimization for when (non-constexpr) fields are already normalized. - if (0 <= ss && ss < 60) { - const second_t nss = static_cast<second_t>(ss); - if (0 <= mm && mm < 60) { - const minute_t nmm = static_cast<minute_t>(mm); - if (0 <= hh && hh < 24) { - const hour_t nhh = static_cast<hour_t>(hh); - if (1 <= d && d <= 28 && 1 <= m && m <= 12) { - const day_t nd = static_cast<day_t>(d); - const month_t nm = static_cast<month_t>(m); - return fields(y, nm, nd, nhh, nmm, nss); - } - return n_mon(y, m, d, 0, nhh, nmm, nss); - } - return n_hour(y, m, d, hh / 24, hh % 24, nmm, nss); - } - return n_min(y, m, d, hh, mm / 60, mm % 60, nss); - } - diff_t cm = ss / 60; - ss %= 60; - if (ss < 0) { - cm -= 1; - ss += 60; - } - return n_min(y, m, d, hh, mm / 60 + cm / 60, mm % 60 + cm % 60, - static_cast<second_t>(ss)); -} - -} // namespace impl - -//////////////////////////////////////////////////////////////////////// - -// Increments the indicated (normalized) field by "n". -CONSTEXPR_F fields step(second_tag, fields f, diff_t n) noexcept { - return impl::n_sec(f.y, f.m, f.d, f.hh, f.mm + n / 60, f.ss + n % 60); -} -CONSTEXPR_F fields step(minute_tag, fields f, diff_t n) noexcept { - return impl::n_min(f.y, f.m, f.d, f.hh + n / 60, 0, f.mm + n % 60, f.ss); -} -CONSTEXPR_F fields step(hour_tag, fields f, diff_t n) noexcept { - return impl::n_hour(f.y, f.m, f.d + n / 24, 0, f.hh + n % 24, f.mm, f.ss); -} -CONSTEXPR_F fields step(day_tag, fields f, diff_t n) noexcept { - return impl::n_day(f.y, f.m, f.d, n, f.hh, f.mm, f.ss); -} -CONSTEXPR_F fields step(month_tag, fields f, diff_t n) noexcept { - return impl::n_mon(f.y + n / 12, f.m + n % 12, f.d, 0, f.hh, f.mm, f.ss); -} -CONSTEXPR_F fields step(year_tag, fields f, diff_t n) noexcept { - return fields(f.y + n, f.m, f.d, f.hh, f.mm, f.ss); -} - -//////////////////////////////////////////////////////////////////////// - -namespace impl { - -// Returns (v * f + a) but avoiding intermediate overflow when possible. -CONSTEXPR_F diff_t scale_add(diff_t v, diff_t f, diff_t a) noexcept { - return (v < 0) ? ((v + 1) * f + a) - f : ((v - 1) * f + a) + f; -} - -// Map a (normalized) Y/M/D to the number of days before/after 1970-01-01. -// Probably overflows for years outside [-292277022656:292277026595]. -CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept { - const diff_t eyear = (m <= 2) ? y - 1 : y; - const diff_t era = (eyear >= 0 ? eyear : eyear - 399) / 400; - const diff_t yoe = eyear - era * 400; - const diff_t doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; - const diff_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; - return era * 146097 + doe - 719468; -} - -// Returns the difference in days between two normalized Y-M-D tuples. -// ymd_ord() will encounter integer overflow given extreme year values, -// yet the difference between two such extreme values may actually be -// small, so we take a little care to avoid overflow when possible by -// exploiting the 146097-day cycle. -CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, - year_t y2, month_t m2, day_t d2) noexcept { - const diff_t a_c4_off = y1 % 400; - const diff_t b_c4_off = y2 % 400; - diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off); - diff_t delta = ymd_ord(a_c4_off, m1, d1) - ymd_ord(b_c4_off, m2, d2); - if (c4_diff > 0 && delta < 0) { - delta += 2 * 146097; - c4_diff -= 2 * 400; - } else if (c4_diff < 0 && delta > 0) { - delta -= 2 * 146097; - c4_diff += 2 * 400; - } - return (c4_diff / 400 * 146097) + delta; -} - -} // namespace impl - -// Returns the difference between fields structs using the indicated unit. -CONSTEXPR_F diff_t difference(year_tag, fields f1, fields f2) noexcept { - return f1.y - f2.y; -} -CONSTEXPR_F diff_t difference(month_tag, fields f1, fields f2) noexcept { - return impl::scale_add(difference(year_tag{}, f1, f2), 12, (f1.m - f2.m)); -} -CONSTEXPR_F diff_t difference(day_tag, fields f1, fields f2) noexcept { - return impl::day_difference(f1.y, f1.m, f1.d, f2.y, f2.m, f2.d); -} -CONSTEXPR_F diff_t difference(hour_tag, fields f1, fields f2) noexcept { - return impl::scale_add(difference(day_tag{}, f1, f2), 24, (f1.hh - f2.hh)); -} -CONSTEXPR_F diff_t difference(minute_tag, fields f1, fields f2) noexcept { - return impl::scale_add(difference(hour_tag{}, f1, f2), 60, (f1.mm - f2.mm)); -} -CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept { - return impl::scale_add(difference(minute_tag{}, f1, f2), 60, f1.ss - f2.ss); -} - -//////////////////////////////////////////////////////////////////////// - -// Aligns the (normalized) fields struct to the indicated field. -CONSTEXPR_F fields align(second_tag, fields f) noexcept { - return f; -} -CONSTEXPR_F fields align(minute_tag, fields f) noexcept { - return fields{f.y, f.m, f.d, f.hh, f.mm, 0}; -} -CONSTEXPR_F fields align(hour_tag, fields f) noexcept { - return fields{f.y, f.m, f.d, f.hh, 0, 0}; -} -CONSTEXPR_F fields align(day_tag, fields f) noexcept { - return fields{f.y, f.m, f.d, 0, 0, 0}; -} -CONSTEXPR_F fields align(month_tag, fields f) noexcept { - return fields{f.y, f.m, 1, 0, 0, 0}; -} -CONSTEXPR_F fields align(year_tag, fields f) noexcept { - return fields{f.y, 1, 1, 0, 0, 0}; -} - -//////////////////////////////////////////////////////////////////////// - -template <typename T> -class civil_time { - public: - explicit CONSTEXPR_M civil_time(year_t y, diff_t m = 1, diff_t d = 1, - diff_t hh = 0, diff_t mm = 0, - diff_t ss = 0) noexcept - : civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {} - - CONSTEXPR_M civil_time() noexcept : f_{1970, 1, 1, 0, 0, 0} {} - civil_time(const civil_time&) = default; - civil_time& operator=(const civil_time&) = default; - - // Conversion between civil times of different alignment. Conversion to - // a more precise alignment is allowed implicitly (e.g., day -> hour), - // but conversion where information is discarded must be explicit - // (e.g., second -> minute). - template <typename U, typename S> - using preserves_data = - typename std::enable_if<std::is_base_of<U, S>::value>::type; - template <typename U> - CONSTEXPR_M civil_time(const civil_time<U>& ct, - preserves_data<T, U>* = nullptr) noexcept - : civil_time(ct.f_) {} - template <typename U> - explicit CONSTEXPR_M civil_time(const civil_time<U>& ct, - preserves_data<U, T>* = nullptr) noexcept - : civil_time(ct.f_) {} - - // Factories for the maximum/minimum representable civil_time. +} +CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, + hour_t hh, minute_t mm, second_t ss) noexcept { + if (m != 12) { + y += m / 12; + m %= 12; + if (m <= 0) { + y -= 1; + m += 12; + } + } + return n_day(y, static_cast<month_t>(m), d, cd, hh, mm, ss); +} +CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, + diff_t hh, minute_t mm, second_t ss) noexcept { + cd += hh / 24; + hh %= 24; + if (hh < 0) { + cd -= 1; + hh += 24; + } + return n_mon(y, m, d, cd, static_cast<hour_t>(hh), mm, ss); +} +CONSTEXPR_F fields n_min(year_t y, diff_t m, diff_t d, diff_t hh, diff_t ch, + diff_t mm, second_t ss) noexcept { + ch += mm / 60; + mm %= 60; + if (mm < 0) { + ch -= 1; + mm += 60; + } + return n_hour(y, m, d, hh / 24 + ch / 24, hh % 24 + ch % 24, + static_cast<minute_t>(mm), ss); +} +CONSTEXPR_F fields n_sec(year_t y, diff_t m, diff_t d, diff_t hh, diff_t mm, + diff_t ss) noexcept { + // Optimization for when (non-constexpr) fields are already normalized. + if (0 <= ss && ss < 60) { + const second_t nss = static_cast<second_t>(ss); + if (0 <= mm && mm < 60) { + const minute_t nmm = static_cast<minute_t>(mm); + if (0 <= hh && hh < 24) { + const hour_t nhh = static_cast<hour_t>(hh); + if (1 <= d && d <= 28 && 1 <= m && m <= 12) { + const day_t nd = static_cast<day_t>(d); + const month_t nm = static_cast<month_t>(m); + return fields(y, nm, nd, nhh, nmm, nss); + } + return n_mon(y, m, d, 0, nhh, nmm, nss); + } + return n_hour(y, m, d, hh / 24, hh % 24, nmm, nss); + } + return n_min(y, m, d, hh, mm / 60, mm % 60, nss); + } + diff_t cm = ss / 60; + ss %= 60; + if (ss < 0) { + cm -= 1; + ss += 60; + } + return n_min(y, m, d, hh, mm / 60 + cm / 60, mm % 60 + cm % 60, + static_cast<second_t>(ss)); +} + +} // namespace impl + +//////////////////////////////////////////////////////////////////////// + +// Increments the indicated (normalized) field by "n". +CONSTEXPR_F fields step(second_tag, fields f, diff_t n) noexcept { + return impl::n_sec(f.y, f.m, f.d, f.hh, f.mm + n / 60, f.ss + n % 60); +} +CONSTEXPR_F fields step(minute_tag, fields f, diff_t n) noexcept { + return impl::n_min(f.y, f.m, f.d, f.hh + n / 60, 0, f.mm + n % 60, f.ss); +} +CONSTEXPR_F fields step(hour_tag, fields f, diff_t n) noexcept { + return impl::n_hour(f.y, f.m, f.d + n / 24, 0, f.hh + n % 24, f.mm, f.ss); +} +CONSTEXPR_F fields step(day_tag, fields f, diff_t n) noexcept { + return impl::n_day(f.y, f.m, f.d, n, f.hh, f.mm, f.ss); +} +CONSTEXPR_F fields step(month_tag, fields f, diff_t n) noexcept { + return impl::n_mon(f.y + n / 12, f.m + n % 12, f.d, 0, f.hh, f.mm, f.ss); +} +CONSTEXPR_F fields step(year_tag, fields f, diff_t n) noexcept { + return fields(f.y + n, f.m, f.d, f.hh, f.mm, f.ss); +} + +//////////////////////////////////////////////////////////////////////// + +namespace impl { + +// Returns (v * f + a) but avoiding intermediate overflow when possible. +CONSTEXPR_F diff_t scale_add(diff_t v, diff_t f, diff_t a) noexcept { + return (v < 0) ? ((v + 1) * f + a) - f : ((v - 1) * f + a) + f; +} + +// Map a (normalized) Y/M/D to the number of days before/after 1970-01-01. +// Probably overflows for years outside [-292277022656:292277026595]. +CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept { + const diff_t eyear = (m <= 2) ? y - 1 : y; + const diff_t era = (eyear >= 0 ? eyear : eyear - 399) / 400; + const diff_t yoe = eyear - era * 400; + const diff_t doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; + const diff_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; + return era * 146097 + doe - 719468; +} + +// Returns the difference in days between two normalized Y-M-D tuples. +// ymd_ord() will encounter integer overflow given extreme year values, +// yet the difference between two such extreme values may actually be +// small, so we take a little care to avoid overflow when possible by +// exploiting the 146097-day cycle. +CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, + year_t y2, month_t m2, day_t d2) noexcept { + const diff_t a_c4_off = y1 % 400; + const diff_t b_c4_off = y2 % 400; + diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off); + diff_t delta = ymd_ord(a_c4_off, m1, d1) - ymd_ord(b_c4_off, m2, d2); + if (c4_diff > 0 && delta < 0) { + delta += 2 * 146097; + c4_diff -= 2 * 400; + } else if (c4_diff < 0 && delta > 0) { + delta -= 2 * 146097; + c4_diff += 2 * 400; + } + return (c4_diff / 400 * 146097) + delta; +} + +} // namespace impl + +// Returns the difference between fields structs using the indicated unit. +CONSTEXPR_F diff_t difference(year_tag, fields f1, fields f2) noexcept { + return f1.y - f2.y; +} +CONSTEXPR_F diff_t difference(month_tag, fields f1, fields f2) noexcept { + return impl::scale_add(difference(year_tag{}, f1, f2), 12, (f1.m - f2.m)); +} +CONSTEXPR_F diff_t difference(day_tag, fields f1, fields f2) noexcept { + return impl::day_difference(f1.y, f1.m, f1.d, f2.y, f2.m, f2.d); +} +CONSTEXPR_F diff_t difference(hour_tag, fields f1, fields f2) noexcept { + return impl::scale_add(difference(day_tag{}, f1, f2), 24, (f1.hh - f2.hh)); +} +CONSTEXPR_F diff_t difference(minute_tag, fields f1, fields f2) noexcept { + return impl::scale_add(difference(hour_tag{}, f1, f2), 60, (f1.mm - f2.mm)); +} +CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept { + return impl::scale_add(difference(minute_tag{}, f1, f2), 60, f1.ss - f2.ss); +} + +//////////////////////////////////////////////////////////////////////// + +// Aligns the (normalized) fields struct to the indicated field. +CONSTEXPR_F fields align(second_tag, fields f) noexcept { + return f; +} +CONSTEXPR_F fields align(minute_tag, fields f) noexcept { + return fields{f.y, f.m, f.d, f.hh, f.mm, 0}; +} +CONSTEXPR_F fields align(hour_tag, fields f) noexcept { + return fields{f.y, f.m, f.d, f.hh, 0, 0}; +} +CONSTEXPR_F fields align(day_tag, fields f) noexcept { + return fields{f.y, f.m, f.d, 0, 0, 0}; +} +CONSTEXPR_F fields align(month_tag, fields f) noexcept { + return fields{f.y, f.m, 1, 0, 0, 0}; +} +CONSTEXPR_F fields align(year_tag, fields f) noexcept { + return fields{f.y, 1, 1, 0, 0, 0}; +} + +//////////////////////////////////////////////////////////////////////// + +template <typename T> +class civil_time { + public: + explicit CONSTEXPR_M civil_time(year_t y, diff_t m = 1, diff_t d = 1, + diff_t hh = 0, diff_t mm = 0, + diff_t ss = 0) noexcept + : civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {} + + CONSTEXPR_M civil_time() noexcept : f_{1970, 1, 1, 0, 0, 0} {} + civil_time(const civil_time&) = default; + civil_time& operator=(const civil_time&) = default; + + // Conversion between civil times of different alignment. Conversion to + // a more precise alignment is allowed implicitly (e.g., day -> hour), + // but conversion where information is discarded must be explicit + // (e.g., second -> minute). + template <typename U, typename S> + using preserves_data = + typename std::enable_if<std::is_base_of<U, S>::value>::type; + template <typename U> + CONSTEXPR_M civil_time(const civil_time<U>& ct, + preserves_data<T, U>* = nullptr) noexcept + : civil_time(ct.f_) {} + template <typename U> + explicit CONSTEXPR_M civil_time(const civil_time<U>& ct, + preserves_data<U, T>* = nullptr) noexcept + : civil_time(ct.f_) {} + + // Factories for the maximum/minimum representable civil_time. static CONSTEXPR_F civil_time (max)() { const auto max_year = (std::numeric_limits<std::int_least64_t>::max)(); - return civil_time(max_year, 12, 31, 23, 59, 59); - } + return civil_time(max_year, 12, 31, 23, 59, 59); + } static CONSTEXPR_F civil_time (min)() { const auto min_year = (std::numeric_limits<std::int_least64_t>::min)(); - return civil_time(min_year, 1, 1, 0, 0, 0); - } - - // Field accessors. Note: All but year() return an int. - CONSTEXPR_M year_t year() const noexcept { return f_.y; } - CONSTEXPR_M int month() const noexcept { return f_.m; } - CONSTEXPR_M int day() const noexcept { return f_.d; } - CONSTEXPR_M int hour() const noexcept { return f_.hh; } - CONSTEXPR_M int minute() const noexcept { return f_.mm; } - CONSTEXPR_M int second() const noexcept { return f_.ss; } - - // Assigning arithmetic. - CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept { + return civil_time(min_year, 1, 1, 0, 0, 0); + } + + // Field accessors. Note: All but year() return an int. + CONSTEXPR_M year_t year() const noexcept { return f_.y; } + CONSTEXPR_M int month() const noexcept { return f_.m; } + CONSTEXPR_M int day() const noexcept { return f_.d; } + CONSTEXPR_M int hour() const noexcept { return f_.hh; } + CONSTEXPR_M int minute() const noexcept { return f_.mm; } + CONSTEXPR_M int second() const noexcept { return f_.ss; } + + // Assigning arithmetic. + CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept { return *this = *this + n; - } - CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept { + } + CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept { return *this = *this - n; - } - CONSTEXPR_M civil_time& operator++() noexcept { - return *this += 1; - } - CONSTEXPR_M civil_time operator++(int) noexcept { - const civil_time a = *this; - ++*this; - return a; - } - CONSTEXPR_M civil_time& operator--() noexcept { - return *this -= 1; - } - CONSTEXPR_M civil_time operator--(int) noexcept { - const civil_time a = *this; - --*this; - return a; - } - - // Binary arithmetic operators. + } + CONSTEXPR_M civil_time& operator++() noexcept { + return *this += 1; + } + CONSTEXPR_M civil_time operator++(int) noexcept { + const civil_time a = *this; + ++*this; + return a; + } + CONSTEXPR_M civil_time& operator--() noexcept { + return *this -= 1; + } + CONSTEXPR_M civil_time operator--(int) noexcept { + const civil_time a = *this; + --*this; + return a; + } + + // Binary arithmetic operators. friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept { return civil_time(step(T{}, a.f_, n)); - } + } friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept { return a + n; - } + } friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept { return n != (std::numeric_limits<diff_t>::min)() ? civil_time(step(T{}, a.f_, -n)) : civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1)); - } + } friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept { - return difference(T{}, lhs.f_, rhs.f_); - } - - private: - // All instantiations of this template are allowed to call the following - // private constructor and access the private fields member. - template <typename U> - friend class civil_time; - - // The designated constructor that all others eventually call. - explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {} - - fields f_; -}; - -// Disallows difference between differently aligned types. -// auto n = civil_day(...) - civil_hour(...); // would be confusing. + return difference(T{}, lhs.f_, rhs.f_); + } + + private: + // All instantiations of this template are allowed to call the following + // private constructor and access the private fields member. + template <typename U> + friend class civil_time; + + // The designated constructor that all others eventually call. + explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {} + + fields f_; +}; + +// Disallows difference between differently aligned types. +// auto n = civil_day(...) - civil_hour(...); // would be confusing. template <typename T, typename U> CONSTEXPR_F diff_t operator-(civil_time<T>, civil_time<U>) = delete; - -using civil_year = civil_time<year_tag>; -using civil_month = civil_time<month_tag>; -using civil_day = civil_time<day_tag>; -using civil_hour = civil_time<hour_tag>; -using civil_minute = civil_time<minute_tag>; -using civil_second = civil_time<second_tag>; - -//////////////////////////////////////////////////////////////////////// - -// Relational operators that work with differently aligned objects. -// Always compares all six fields. -template <typename T1, typename T2> -CONSTEXPR_F bool operator<(const civil_time<T1>& lhs, - const civil_time<T2>& rhs) noexcept { - return (lhs.year() < rhs.year() || - (lhs.year() == rhs.year() && - (lhs.month() < rhs.month() || - (lhs.month() == rhs.month() && - (lhs.day() < rhs.day() || - (lhs.day() == rhs.day() && - (lhs.hour() < rhs.hour() || - (lhs.hour() == rhs.hour() && - (lhs.minute() < rhs.minute() || - (lhs.minute() == rhs.minute() && - (lhs.second() < rhs.second()))))))))))); -} -template <typename T1, typename T2> -CONSTEXPR_F bool operator<=(const civil_time<T1>& lhs, - const civil_time<T2>& rhs) noexcept { - return !(rhs < lhs); -} -template <typename T1, typename T2> -CONSTEXPR_F bool operator>=(const civil_time<T1>& lhs, - const civil_time<T2>& rhs) noexcept { - return !(lhs < rhs); -} -template <typename T1, typename T2> -CONSTEXPR_F bool operator>(const civil_time<T1>& lhs, - const civil_time<T2>& rhs) noexcept { - return rhs < lhs; -} -template <typename T1, typename T2> -CONSTEXPR_F bool operator==(const civil_time<T1>& lhs, - const civil_time<T2>& rhs) noexcept { - return lhs.year() == rhs.year() && lhs.month() == rhs.month() && - lhs.day() == rhs.day() && lhs.hour() == rhs.hour() && - lhs.minute() == rhs.minute() && lhs.second() == rhs.second(); -} -template <typename T1, typename T2> -CONSTEXPR_F bool operator!=(const civil_time<T1>& lhs, - const civil_time<T2>& rhs) noexcept { - return !(lhs == rhs); -} - -//////////////////////////////////////////////////////////////////////// - -enum class weekday { - monday, - tuesday, - wednesday, - thursday, - friday, - saturday, - sunday, -}; - + +using civil_year = civil_time<year_tag>; +using civil_month = civil_time<month_tag>; +using civil_day = civil_time<day_tag>; +using civil_hour = civil_time<hour_tag>; +using civil_minute = civil_time<minute_tag>; +using civil_second = civil_time<second_tag>; + +//////////////////////////////////////////////////////////////////////// + +// Relational operators that work with differently aligned objects. +// Always compares all six fields. +template <typename T1, typename T2> +CONSTEXPR_F bool operator<(const civil_time<T1>& lhs, + const civil_time<T2>& rhs) noexcept { + return (lhs.year() < rhs.year() || + (lhs.year() == rhs.year() && + (lhs.month() < rhs.month() || + (lhs.month() == rhs.month() && + (lhs.day() < rhs.day() || + (lhs.day() == rhs.day() && + (lhs.hour() < rhs.hour() || + (lhs.hour() == rhs.hour() && + (lhs.minute() < rhs.minute() || + (lhs.minute() == rhs.minute() && + (lhs.second() < rhs.second()))))))))))); +} +template <typename T1, typename T2> +CONSTEXPR_F bool operator<=(const civil_time<T1>& lhs, + const civil_time<T2>& rhs) noexcept { + return !(rhs < lhs); +} +template <typename T1, typename T2> +CONSTEXPR_F bool operator>=(const civil_time<T1>& lhs, + const civil_time<T2>& rhs) noexcept { + return !(lhs < rhs); +} +template <typename T1, typename T2> +CONSTEXPR_F bool operator>(const civil_time<T1>& lhs, + const civil_time<T2>& rhs) noexcept { + return rhs < lhs; +} +template <typename T1, typename T2> +CONSTEXPR_F bool operator==(const civil_time<T1>& lhs, + const civil_time<T2>& rhs) noexcept { + return lhs.year() == rhs.year() && lhs.month() == rhs.month() && + lhs.day() == rhs.day() && lhs.hour() == rhs.hour() && + lhs.minute() == rhs.minute() && lhs.second() == rhs.second(); +} +template <typename T1, typename T2> +CONSTEXPR_F bool operator!=(const civil_time<T1>& lhs, + const civil_time<T2>& rhs) noexcept { + return !(lhs == rhs); +} + +//////////////////////////////////////////////////////////////////////// + +enum class weekday { + monday, + tuesday, + wednesday, + thursday, + friday, + saturday, + sunday, +}; + CONSTEXPR_F weekday get_weekday(const civil_second& cs) noexcept { CONSTEXPR_D weekday k_weekday_by_mon_off[13] = { weekday::monday, weekday::tuesday, weekday::wednesday, weekday::thursday, weekday::friday, weekday::saturday, weekday::sunday, weekday::monday, weekday::tuesday, weekday::wednesday, weekday::thursday, weekday::friday, - weekday::saturday, - }; - CONSTEXPR_D int k_weekday_offsets[1 + 12] = { - -1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4, - }; + weekday::saturday, + }; + CONSTEXPR_D int k_weekday_offsets[1 + 12] = { + -1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4, + }; year_t wd = 2400 + (cs.year() % 400) - (cs.month() < 3); wd += wd / 4 - wd / 100 + wd / 400; wd += k_weekday_offsets[cs.month()] + cs.day(); return k_weekday_by_mon_off[wd % 7 + 6]; -} - -//////////////////////////////////////////////////////////////////////// - -CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept { +} + +//////////////////////////////////////////////////////////////////////// + +CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept { CONSTEXPR_D weekday k_weekdays_forw[14] = { weekday::monday, weekday::tuesday, weekday::wednesday, weekday::thursday, weekday::friday, weekday::saturday, @@ -540,9 +540,9 @@ CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept { } } } -} - -CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept { +} + +CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept { CONSTEXPR_D weekday k_weekdays_back[14] = { weekday::sunday, weekday::saturday, weekday::friday, weekday::thursday, weekday::wednesday, weekday::tuesday, @@ -560,31 +560,31 @@ CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept { } } } -} - +} + CONSTEXPR_F int get_yearday(const civil_second& cs) noexcept { - CONSTEXPR_D int k_month_offsets[1 + 12] = { - -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, - }; + CONSTEXPR_D int k_month_offsets[1 + 12] = { + -1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + }; const int feb29 = (cs.month() > 2 && impl::is_leap_year(cs.year())); return k_month_offsets[cs.month()] + feb29 + cs.day(); -} - -//////////////////////////////////////////////////////////////////////// - -std::ostream& operator<<(std::ostream& os, const civil_year& y); -std::ostream& operator<<(std::ostream& os, const civil_month& m); -std::ostream& operator<<(std::ostream& os, const civil_day& d); -std::ostream& operator<<(std::ostream& os, const civil_hour& h); -std::ostream& operator<<(std::ostream& os, const civil_minute& m); -std::ostream& operator<<(std::ostream& os, const civil_second& s); -std::ostream& operator<<(std::ostream& os, weekday wd); - -} // namespace detail -} // namespace cctz - -#undef CONSTEXPR_M -#undef CONSTEXPR_F -#undef CONSTEXPR_D - -#endif // CCTZ_CIVIL_TIME_DETAIL_H_ +} + +//////////////////////////////////////////////////////////////////////// + +std::ostream& operator<<(std::ostream& os, const civil_year& y); +std::ostream& operator<<(std::ostream& os, const civil_month& m); +std::ostream& operator<<(std::ostream& os, const civil_day& d); +std::ostream& operator<<(std::ostream& os, const civil_hour& h); +std::ostream& operator<<(std::ostream& os, const civil_minute& m); +std::ostream& operator<<(std::ostream& os, const civil_second& s); +std::ostream& operator<<(std::ostream& os, weekday wd); + +} // namespace detail +} // namespace cctz + +#undef CONSTEXPR_M +#undef CONSTEXPR_F +#undef CONSTEXPR_D + +#endif // CCTZ_CIVIL_TIME_DETAIL_H_ diff --git a/contrib/libs/cctz/include/cctz/time_zone.h b/contrib/libs/cctz/include/cctz/time_zone.h index 4fc1a2a24f..e497e6780f 100644 --- a/contrib/libs/cctz/include/cctz/time_zone.h +++ b/contrib/libs/cctz/include/cctz/time_zone.h @@ -1,162 +1,162 @@ -// 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. - -// A library for translating between absolute times (represented by -// std::chrono::time_points of the std::chrono::system_clock) and civil -// times (represented by cctz::civil_second) using the rules defined by -// a time zone (cctz::time_zone). - -#ifndef CCTZ_TIME_ZONE_H_ -#define CCTZ_TIME_ZONE_H_ - -#include <chrono> -#include <cstdint> -#include <string> -#include <utility> - +// +// 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. + +// A library for translating between absolute times (represented by +// std::chrono::time_points of the std::chrono::system_clock) and civil +// times (represented by cctz::civil_second) using the rules defined by +// a time zone (cctz::time_zone). + +#ifndef CCTZ_TIME_ZONE_H_ +#define CCTZ_TIME_ZONE_H_ + +#include <chrono> +#include <cstdint> +#include <string> +#include <utility> + #include "cctz/civil_time.h" - -namespace cctz { - -// Convenience aliases. Not intended as public API points. -template <typename D> -using time_point = std::chrono::time_point<std::chrono::system_clock, D>; + +namespace cctz { + +// Convenience aliases. Not intended as public API points. +template <typename D> +using time_point = std::chrono::time_point<std::chrono::system_clock, D>; using seconds = std::chrono::duration<std::int_fast64_t>; using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead. - -namespace detail { -template <typename D> + +namespace detail { +template <typename D> inline std::pair<time_point<seconds>, D> -split_seconds(const time_point<D>& tp) { +split_seconds(const time_point<D>& tp) { auto sec = std::chrono::time_point_cast<seconds>(tp); - auto sub = tp - sec; - if (sub.count() < 0) { + auto sub = tp - sec; + if (sub.count() < 0) { sec -= seconds(1); sub += seconds(1); - } - return {sec, std::chrono::duration_cast<D>(sub)}; -} + } + return {sec, std::chrono::duration_cast<D>(sub)}; +} inline std::pair<time_point<seconds>, seconds> split_seconds(const time_point<seconds>& tp) { return {tp, seconds::zero()}; -} -} // namespace detail - -// cctz::time_zone is an opaque, small, value-type class representing a -// geo-political region within which particular rules are used for mapping -// between absolute and civil times. Time zones are named using the TZ -// identifiers from the IANA Time Zone Database, such as "America/Los_Angeles" -// or "Australia/Sydney". Time zones are created from factory functions such -// as load_time_zone(). Note: strings like "PST" and "EDT" are not valid TZ -// identifiers. -// -// Example: -// cctz::time_zone utc = cctz::utc_time_zone(); -// cctz::time_zone pst = cctz::fixed_time_zone(std::chrono::hours(-8)); -// cctz::time_zone loc = cctz::local_time_zone(); -// cctz::time_zone lax; -// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } -// -// See also: -// - http://www.iana.org/time-zones +} +} // namespace detail + +// cctz::time_zone is an opaque, small, value-type class representing a +// geo-political region within which particular rules are used for mapping +// between absolute and civil times. Time zones are named using the TZ +// identifiers from the IANA Time Zone Database, such as "America/Los_Angeles" +// or "Australia/Sydney". Time zones are created from factory functions such +// as load_time_zone(). Note: strings like "PST" and "EDT" are not valid TZ +// identifiers. +// +// Example: +// cctz::time_zone utc = cctz::utc_time_zone(); +// cctz::time_zone pst = cctz::fixed_time_zone(std::chrono::hours(-8)); +// cctz::time_zone loc = cctz::local_time_zone(); +// cctz::time_zone lax; +// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } +// +// See also: +// - http://www.iana.org/time-zones // - https://en.wikipedia.org/wiki/Zoneinfo -class time_zone { - public: +class time_zone { + public: time_zone() : time_zone(nullptr) {} // Equivalent to UTC - time_zone(const time_zone&) = default; - time_zone& operator=(const time_zone&) = default; - - std::string name() const; - - // An absolute_lookup represents the civil time (cctz::civil_second) within - // this time_zone at the given absolute time (time_point). There are - // additionally a few other fields that may be useful when working with - // older APIs, such as std::tm. - // - // Example: - // const cctz::time_zone tz = ... - // const auto tp = std::chrono::system_clock::now(); - // const cctz::time_zone::absolute_lookup al = tz.lookup(tp); - struct absolute_lookup { - civil_second cs; - // Note: The following fields exist for backward compatibility with older - // APIs. Accessing these fields directly is a sign of imprudent logic in - // the calling code. Modern time-related code should only access this data - // indirectly by way of cctz::format(). - int offset; // civil seconds east of UTC - bool is_dst; // is offset non-standard? - const char* abbr; // time-zone abbreviation (e.g., "PST") - }; + time_zone(const time_zone&) = default; + time_zone& operator=(const time_zone&) = default; + + std::string name() const; + + // An absolute_lookup represents the civil time (cctz::civil_second) within + // this time_zone at the given absolute time (time_point). There are + // additionally a few other fields that may be useful when working with + // older APIs, such as std::tm. + // + // Example: + // const cctz::time_zone tz = ... + // const auto tp = std::chrono::system_clock::now(); + // const cctz::time_zone::absolute_lookup al = tz.lookup(tp); + struct absolute_lookup { + civil_second cs; + // Note: The following fields exist for backward compatibility with older + // APIs. Accessing these fields directly is a sign of imprudent logic in + // the calling code. Modern time-related code should only access this data + // indirectly by way of cctz::format(). + int offset; // civil seconds east of UTC + bool is_dst; // is offset non-standard? + const char* abbr; // time-zone abbreviation (e.g., "PST") + }; absolute_lookup lookup(const time_point<seconds>& tp) const; - template <typename D> - absolute_lookup lookup(const time_point<D>& tp) const { - return lookup(detail::split_seconds(tp).first); - } - - // A civil_lookup represents the absolute time(s) (time_point) that - // correspond to the given civil time (cctz::civil_second) within this - // time_zone. Usually the given civil time represents a unique instant - // in time, in which case the conversion is unambiguous. However, - // within this time zone, the given civil time may be skipped (e.g., - // during a positive UTC offset shift), or repeated (e.g., during a - // negative UTC offset shift). To account for these possibilities, - // civil_lookup is richer than just a single time_point. - // - // In all cases the civil_lookup::kind enum will indicate the nature - // of the given civil-time argument, and the pre, trans, and post - // members will give the absolute time answers using the pre-transition - // offset, the transition point itself, and the post-transition offset, + template <typename D> + absolute_lookup lookup(const time_point<D>& tp) const { + return lookup(detail::split_seconds(tp).first); + } + + // A civil_lookup represents the absolute time(s) (time_point) that + // correspond to the given civil time (cctz::civil_second) within this + // time_zone. Usually the given civil time represents a unique instant + // in time, in which case the conversion is unambiguous. However, + // within this time zone, the given civil time may be skipped (e.g., + // during a positive UTC offset shift), or repeated (e.g., during a + // negative UTC offset shift). To account for these possibilities, + // civil_lookup is richer than just a single time_point. + // + // In all cases the civil_lookup::kind enum will indicate the nature + // of the given civil-time argument, and the pre, trans, and post + // members will give the absolute time answers using the pre-transition + // offset, the transition point itself, and the post-transition offset, // respectively (all three times are equal if kind == UNIQUE). If any - // of these three absolute times is outside the representable range of a + // of these three absolute times is outside the representable range of a // time_point<seconds> the field is set to its maximum/minimum value. - // - // Example: - // cctz::time_zone lax; - // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } - // - // // A unique civil time. - // auto jan01 = lax.lookup(cctz::civil_second(2011, 1, 1, 0, 0, 0)); - // // jan01.kind == cctz::time_zone::civil_lookup::UNIQUE - // // jan01.pre is 2011/01/01 00:00:00 -0800 - // // jan01.trans is 2011/01/01 00:00:00 -0800 - // // jan01.post is 2011/01/01 00:00:00 -0800 - // - // // A Spring DST transition, when there is a gap in civil time. - // auto mar13 = lax.lookup(cctz::civil_second(2011, 3, 13, 2, 15, 0)); - // // mar13.kind == cctz::time_zone::civil_lookup::SKIPPED - // // mar13.pre is 2011/03/13 03:15:00 -0700 - // // mar13.trans is 2011/03/13 03:00:00 -0700 - // // mar13.post is 2011/03/13 01:15:00 -0800 - // - // // A Fall DST transition, when civil times are repeated. - // auto nov06 = lax.lookup(cctz::civil_second(2011, 11, 6, 1, 15, 0)); - // // nov06.kind == cctz::time_zone::civil_lookup::REPEATED - // // nov06.pre is 2011/11/06 01:15:00 -0700 - // // nov06.trans is 2011/11/06 01:00:00 -0800 - // // nov06.post is 2011/11/06 01:15:00 -0800 - struct civil_lookup { - enum civil_kind { - UNIQUE, // the civil time was singular (pre == trans == post) - SKIPPED, // the civil time did not exist (pre >= trans > post) - REPEATED, // the civil time was ambiguous (pre < trans <= post) - } kind; + // + // Example: + // cctz::time_zone lax; + // if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } + // + // // A unique civil time. + // auto jan01 = lax.lookup(cctz::civil_second(2011, 1, 1, 0, 0, 0)); + // // jan01.kind == cctz::time_zone::civil_lookup::UNIQUE + // // jan01.pre is 2011/01/01 00:00:00 -0800 + // // jan01.trans is 2011/01/01 00:00:00 -0800 + // // jan01.post is 2011/01/01 00:00:00 -0800 + // + // // A Spring DST transition, when there is a gap in civil time. + // auto mar13 = lax.lookup(cctz::civil_second(2011, 3, 13, 2, 15, 0)); + // // mar13.kind == cctz::time_zone::civil_lookup::SKIPPED + // // mar13.pre is 2011/03/13 03:15:00 -0700 + // // mar13.trans is 2011/03/13 03:00:00 -0700 + // // mar13.post is 2011/03/13 01:15:00 -0800 + // + // // A Fall DST transition, when civil times are repeated. + // auto nov06 = lax.lookup(cctz::civil_second(2011, 11, 6, 1, 15, 0)); + // // nov06.kind == cctz::time_zone::civil_lookup::REPEATED + // // nov06.pre is 2011/11/06 01:15:00 -0700 + // // nov06.trans is 2011/11/06 01:00:00 -0800 + // // nov06.post is 2011/11/06 01:15:00 -0800 + struct civil_lookup { + enum civil_kind { + UNIQUE, // the civil time was singular (pre == trans == post) + SKIPPED, // the civil time did not exist (pre >= trans > post) + REPEATED, // the civil time was ambiguous (pre < trans <= post) + } kind; time_point<seconds> pre; // uses the pre-transition offset time_point<seconds> trans; // instant of civil-offset change time_point<seconds> post; // uses the post-transition offset - }; - civil_lookup lookup(const civil_second& cs) const; - + }; + civil_lookup lookup(const civil_second& cs) const; + // Finds the time of the next/previous offset change in this time zone. // // By definition, next_transition(tp, &trans) returns false when tp has @@ -222,161 +222,161 @@ class time_zone { return !(lhs == rhs); } - class Impl; - - private: - explicit time_zone(const Impl* impl) : impl_(impl) {} + class Impl; + + private: + explicit time_zone(const Impl* impl) : impl_(impl) {} const Impl& effective_impl() const; // handles implicit UTC const Impl* impl_; -}; - -// Loads the named time zone. May perform I/O on the initial load. -// If the name is invalid, or some other kind of error occurs, returns -// false and "*tz" is set to the UTC time zone. -bool load_time_zone(const std::string& name, time_zone* tz); - -// Returns a time_zone representing UTC. Cannot fail. -time_zone utc_time_zone(); - -// Returns a time zone that is a fixed offset (seconds east) from UTC. -// Note: If the absolute value of the offset is greater than 24 hours -// you'll get UTC (i.e., zero offset) instead. +}; + +// Loads the named time zone. May perform I/O on the initial load. +// If the name is invalid, or some other kind of error occurs, returns +// false and "*tz" is set to the UTC time zone. +bool load_time_zone(const std::string& name, time_zone* tz); + +// Returns a time_zone representing UTC. Cannot fail. +time_zone utc_time_zone(); + +// Returns a time zone that is a fixed offset (seconds east) from UTC. +// Note: If the absolute value of the offset is greater than 24 hours +// you'll get UTC (i.e., zero offset) instead. time_zone fixed_time_zone(const seconds& offset); - -// Returns a time zone representing the local time zone. Falls back to UTC. + +// Returns a time zone representing the local time zone. Falls back to UTC. // Note: local_time_zone.name() may only be something like "localtime". -time_zone local_time_zone(); - -// Returns the civil time (cctz::civil_second) within the given time zone at -// the given absolute time (time_point). Since the additional fields provided -// by the time_zone::absolute_lookup struct should rarely be needed in modern -// code, this convert() function is simpler and should be preferred. -template <typename D> -inline civil_second convert(const time_point<D>& tp, const time_zone& tz) { - return tz.lookup(tp).cs; -} - -// Returns the absolute time (time_point) that corresponds to the given civil -// time within the given time zone. If the civil time is not unique (i.e., if -// it was either repeated or non-existent), then the returned time_point is -// the best estimate that preserves relative order. That is, this function -// guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz). +time_zone local_time_zone(); + +// Returns the civil time (cctz::civil_second) within the given time zone at +// the given absolute time (time_point). Since the additional fields provided +// by the time_zone::absolute_lookup struct should rarely be needed in modern +// code, this convert() function is simpler and should be preferred. +template <typename D> +inline civil_second convert(const time_point<D>& tp, const time_zone& tz) { + return tz.lookup(tp).cs; +} + +// Returns the absolute time (time_point) that corresponds to the given civil +// time within the given time zone. If the civil time is not unique (i.e., if +// it was either repeated or non-existent), then the returned time_point is +// the best estimate that preserves relative order. That is, this function +// guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz). inline time_point<seconds> convert(const civil_second& cs, const time_zone& tz) { - const time_zone::civil_lookup cl = tz.lookup(cs); - if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans; - return cl.pre; -} - -namespace detail { -using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>; + const time_zone::civil_lookup cl = tz.lookup(cs); + if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans; + return cl.pre; +} + +namespace detail { +using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>; std::string format(const std::string&, const time_point<seconds>&, - const femtoseconds&, const time_zone&); -bool parse(const std::string&, const std::string&, const time_zone&, + const femtoseconds&, const time_zone&); +bool parse(const std::string&, const std::string&, const time_zone&, time_point<seconds>*, femtoseconds*, std::string* err = nullptr); -} // namespace detail - -// Formats the given time_point in the given cctz::time_zone according to -// the provided format string. Uses strftime()-like formatting options, -// with the following extensions: -// +} // namespace detail + +// Formats the given time_point in the given cctz::time_zone according to +// the provided format string. Uses strftime()-like formatting options, +// with the following extensions: +// // - %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 '*') -// - %E#f - Fractional seconds with # digits of precision -// - %E*f - Fractional seconds with full 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 '*') +// - %E#f - Fractional seconds with # digits of precision +// - %E*f - Fractional seconds with full precision (a literal '*') +// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999) // - %ET - The RFC3339 "date-time" separator "T" -// +// // Note that %E0S behaves like %S, and %E0f produces no characters. In -// contrast %E*f always produces at least one digit, which may be '0'. -// -// Note that %Y produces as many characters as it takes to fully render the -// year. A year outside of [-999:9999] when formatted with %E4Y will produce -// more than four characters, just like %Y. -// +// contrast %E*f always produces at least one digit, which may be '0'. +// +// Note that %Y produces as many characters as it takes to fully render the +// year. A year outside of [-999:9999] when formatted with %E4Y will produce +// more than four characters, just like %Y. +// // Tip: Format strings should include the UTC offset (e.g., %z, %Ez, or %E*z) // so that the resulting string uniquely identifies an absolute time. -// -// Example: -// cctz::time_zone lax; -// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } -// auto tp = cctz::convert(cctz::civil_second(2013, 1, 2, 3, 4, 5), lax); -// std::string f = cctz::format("%H:%M:%S", tp, lax); // "03:04:05" -// f = cctz::format("%H:%M:%E3S", tp, lax); // "03:04:05.000" -template <typename D> -inline std::string format(const std::string& fmt, const time_point<D>& tp, - const time_zone& tz) { - const auto p = detail::split_seconds(tp); - const auto n = std::chrono::duration_cast<detail::femtoseconds>(p.second); - return detail::format(fmt, p.first, n, tz); -} - -// Parses an input string according to the provided format string and -// returns the corresponding time_point. Uses strftime()-like formatting -// options, with the same extensions as cctz::format(), but with the +// +// Example: +// cctz::time_zone lax; +// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... } +// auto tp = cctz::convert(cctz::civil_second(2013, 1, 2, 3, 4, 5), lax); +// std::string f = cctz::format("%H:%M:%S", tp, lax); // "03:04:05" +// f = cctz::format("%H:%M:%E3S", tp, lax); // "03:04:05.000" +template <typename D> +inline std::string format(const std::string& fmt, const time_point<D>& tp, + const time_zone& tz) { + const auto p = detail::split_seconds(tp); + const auto n = std::chrono::duration_cast<detail::femtoseconds>(p.second); + return detail::format(fmt, p.first, n, tz); +} + +// Parses an input string according to the provided format string and +// returns the corresponding time_point. Uses strftime()-like formatting +// options, with the same extensions as cctz::format(), but with the // exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez // and %E*z also accept the same inputs, which (along with %z) includes // 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'. -// -// %Y consumes as many numeric characters as it can, so the matching data -// should always be terminated with a non-numeric. %E4Y always consumes -// exactly four characters, including any sign. -// -// Unspecified fields are taken from the default date and time of ... -// -// "1970-01-01 00:00:00.0 +0000" -// -// For example, parsing a string of "15:45" (%H:%M) will return a time_point -// that represents "1970-01-01 15:45:00.0 +0000". -// -// Note that parse() returns time instants, so it makes most sense to parse +// +// %Y consumes as many numeric characters as it can, so the matching data +// should always be terminated with a non-numeric. %E4Y always consumes +// exactly four characters, including any sign. +// +// Unspecified fields are taken from the default date and time of ... +// +// "1970-01-01 00:00:00.0 +0000" +// +// For example, parsing a string of "15:45" (%H:%M) will return a time_point +// that represents "1970-01-01 15:45:00.0 +0000". +// +// Note that parse() returns time instants, so it makes most sense to parse // fully-specified date/time strings that include a UTC offset (%z, %Ez, or // %E*z). -// -// Note also that parse() only heeds the fields year, month, day, hour, -// minute, (fractional) second, and UTC offset. Other fields, like weekday (%a -// or %A), while parsed for syntactic validity, are ignored in the conversion. -// -// Date and time fields that are out-of-range will be treated as errors rather -// than normalizing them like cctz::civil_second() would do. For example, it -// is an error to parse the date "Oct 32, 2013" because 32 is out of range. -// -// A second of ":60" is normalized to ":00" of the following minute with -// fractional seconds discarded. The following table shows how the given -// seconds and subseconds will be parsed: -// -// "59.x" -> 59.x // exact -// "60.x" -> 00.0 // normalized -// "00.x" -> 00.x // exact -// -// Errors are indicated by returning false. -// -// Example: -// const cctz::time_zone tz = ... -// std::chrono::system_clock::time_point tp; -// if (cctz::parse("%Y-%m-%d", "2015-10-09", tz, &tp)) { -// ... -// } -template <typename D> -inline bool parse(const std::string& fmt, const std::string& input, - const time_zone& tz, time_point<D>* tpp) { +// +// Note also that parse() only heeds the fields year, month, day, hour, +// minute, (fractional) second, and UTC offset. Other fields, like weekday (%a +// or %A), while parsed for syntactic validity, are ignored in the conversion. +// +// Date and time fields that are out-of-range will be treated as errors rather +// than normalizing them like cctz::civil_second() would do. For example, it +// is an error to parse the date "Oct 32, 2013" because 32 is out of range. +// +// A second of ":60" is normalized to ":00" of the following minute with +// fractional seconds discarded. The following table shows how the given +// seconds and subseconds will be parsed: +// +// "59.x" -> 59.x // exact +// "60.x" -> 00.0 // normalized +// "00.x" -> 00.x // exact +// +// Errors are indicated by returning false. +// +// Example: +// const cctz::time_zone tz = ... +// std::chrono::system_clock::time_point tp; +// if (cctz::parse("%Y-%m-%d", "2015-10-09", tz, &tp)) { +// ... +// } +template <typename D> +inline bool parse(const std::string& fmt, const std::string& input, + const time_zone& tz, time_point<D>* tpp) { time_point<seconds> sec; - detail::femtoseconds fs; - const bool b = detail::parse(fmt, input, tz, &sec, &fs); - if (b) { - // TODO: Return false if unrepresentable as a time_point<D>. - *tpp = std::chrono::time_point_cast<D>(sec); - *tpp += std::chrono::duration_cast<D>(fs); - } - return b; -} - + detail::femtoseconds fs; + const bool b = detail::parse(fmt, input, tz, &sec, &fs); + if (b) { + // TODO: Return false if unrepresentable as a time_point<D>. + *tpp = std::chrono::time_point_cast<D>(sec); + *tpp += std::chrono::duration_cast<D>(fs); + } + return b; +} + // Access to convert functions which are placed in time_zone_if.h time_point<seconds> UnixSecondsToTimePoint(std::int_fast64_t); std::int_fast64_t TimePointToUnixSeconds(const time_point<seconds>& tp); -} // namespace cctz - -#endif // CCTZ_TIME_ZONE_H_ +} // namespace cctz + +#endif // CCTZ_TIME_ZONE_H_ diff --git a/contrib/libs/cctz/include/cctz/zone_info_source.h b/contrib/libs/cctz/include/cctz/zone_info_source.h index 08e91b3c1f..ccdb73f2fa 100644 --- a/contrib/libs/cctz/include/cctz/zone_info_source.h +++ b/contrib/libs/cctz/include/cctz/zone_info_source.h @@ -1,88 +1,88 @@ -// 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_ZONE_INFO_SOURCE_H_ -#define CCTZ_ZONE_INFO_SOURCE_H_ - -#include <cstddef> -#include <functional> -#include <memory> -#include <string> - -namespace cctz { - -// A stdio-like interface for providing zoneinfo data for a particular zone. -class ZoneInfoSource { - public: +// +// 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_ZONE_INFO_SOURCE_H_ +#define CCTZ_ZONE_INFO_SOURCE_H_ + +#include <cstddef> +#include <functional> +#include <memory> +#include <string> + +namespace cctz { + +// A stdio-like interface for providing zoneinfo data for a particular zone. +class ZoneInfoSource { + public: virtual ~ZoneInfoSource(); - - virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread() - virtual int Skip(std::size_t offset) = 0; // like fseek() + + virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread() + virtual int Skip(std::size_t offset) = 0; // like fseek() // Until the zoneinfo data supports versioning information, we provide // a way for a ZoneInfoSource to indicate it out-of-band. The default // implementation returns an empty string. virtual std::string Version() const; -}; - -} // namespace cctz - -namespace cctz_extension { - -// A function-pointer type for a factory that returns a ZoneInfoSource -// given the name of a time zone and a fallback factory. Returns null -// when the data for the named zone cannot be found. -using ZoneInfoSourceFactory = - std::unique_ptr<cctz::ZoneInfoSource> (*)( - const std::string&, - const std::function<std::unique_ptr<cctz::ZoneInfoSource>( - const std::string&)>&); - -// The user can control the mapping of zone names to zoneinfo data by -// providing a definition for cctz_extension::zone_info_source_factory. -// For example, given functions my_factory() and my_other_factory() that -// can return a ZoneInfoSource for a named zone, we could inject them into -// cctz::load_time_zone() with: -// -// namespace cctz_extension { -// namespace { -// std::unique_ptr<cctz::ZoneInfoSource> CustomFactory( -// const std::string& name, -// const std::function<std::unique_ptr<cctz::ZoneInfoSource>( -// const std::string& name)>& fallback_factory) { -// if (auto zip = my_factory(name)) return zip; -// if (auto zip = fallback_factory(name)) return zip; -// if (auto zip = my_other_factory(name)) return zip; -// return nullptr; -// } -// } // namespace -// ZoneInfoSourceFactory zone_info_source_factory = CustomFactory; -// } // namespace cctz_extension -// -// This might be used, say, to use zoneinfo data embedded in the program, -// or read from a (possibly compressed) file archive, or both. -// -// cctz_extension::zone_info_source_factory() will be called: -// (1) from the same thread as the cctz::load_time_zone() call, -// (2) only once for any zone name, and -// (3) serially (i.e., no concurrent execution). -// -// The fallback factory obtains zoneinfo data by reading files in ${TZDIR}, -// and it is used automatically when no zone_info_source_factory definition -// is linked into the program. -extern ZoneInfoSourceFactory zone_info_source_factory; - -} // namespace cctz_extension - -#endif // CCTZ_ZONE_INFO_SOURCE_H_ +}; + +} // namespace cctz + +namespace cctz_extension { + +// A function-pointer type for a factory that returns a ZoneInfoSource +// given the name of a time zone and a fallback factory. Returns null +// when the data for the named zone cannot be found. +using ZoneInfoSourceFactory = + std::unique_ptr<cctz::ZoneInfoSource> (*)( + const std::string&, + const std::function<std::unique_ptr<cctz::ZoneInfoSource>( + const std::string&)>&); + +// The user can control the mapping of zone names to zoneinfo data by +// providing a definition for cctz_extension::zone_info_source_factory. +// For example, given functions my_factory() and my_other_factory() that +// can return a ZoneInfoSource for a named zone, we could inject them into +// cctz::load_time_zone() with: +// +// namespace cctz_extension { +// namespace { +// std::unique_ptr<cctz::ZoneInfoSource> CustomFactory( +// const std::string& name, +// const std::function<std::unique_ptr<cctz::ZoneInfoSource>( +// const std::string& name)>& fallback_factory) { +// if (auto zip = my_factory(name)) return zip; +// if (auto zip = fallback_factory(name)) return zip; +// if (auto zip = my_other_factory(name)) return zip; +// return nullptr; +// } +// } // namespace +// ZoneInfoSourceFactory zone_info_source_factory = CustomFactory; +// } // namespace cctz_extension +// +// This might be used, say, to use zoneinfo data embedded in the program, +// or read from a (possibly compressed) file archive, or both. +// +// cctz_extension::zone_info_source_factory() will be called: +// (1) from the same thread as the cctz::load_time_zone() call, +// (2) only once for any zone name, and +// (3) serially (i.e., no concurrent execution). +// +// The fallback factory obtains zoneinfo data by reading files in ${TZDIR}, +// and it is used automatically when no zone_info_source_factory definition +// is linked into the program. +extern ZoneInfoSourceFactory zone_info_source_factory; + +} // namespace cctz_extension + +#endif // CCTZ_ZONE_INFO_SOURCE_H_ 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 */ diff --git a/contrib/libs/cctz/tzdata/factory.cpp b/contrib/libs/cctz/tzdata/factory.cpp index 5a4b4a27b6..d7e1acc8b9 100644 --- a/contrib/libs/cctz/tzdata/factory.cpp +++ b/contrib/libs/cctz/tzdata/factory.cpp @@ -1,60 +1,60 @@ #include <contrib/libs/cctz/include/cctz/zone_info_source.h> - + #include <library/cpp/resource/resource.h> - -#include <util/generic/string.h> -#include <util/stream/str.h> + +#include <util/generic/string.h> +#include <util/stream/str.h> #include <util/string/builder.h> - -namespace cctz_extension { - namespace { - - class TZoneInfoSource: public cctz::ZoneInfoSource { - private: - TZoneInfoSource(TString&& data) - : TimeZoneData_(data) - , Stream_(TimeZoneData_) - { - } - - public: - static std::unique_ptr<cctz::ZoneInfoSource> LoadZone(const std::string& zoneName) { + +namespace cctz_extension { + namespace { + + class TZoneInfoSource: public cctz::ZoneInfoSource { + private: + TZoneInfoSource(TString&& data) + : TimeZoneData_(data) + , Stream_(TimeZoneData_) + { + } + + public: + static std::unique_ptr<cctz::ZoneInfoSource> LoadZone(const std::string& zoneName) { TString resourceName = TStringBuilder() << "/cctz/tzdata/"sv << zoneName; - TString tzData; + TString tzData; if (!NResource::FindExact(resourceName, &tzData)) { - return nullptr; - } - return std::unique_ptr<cctz::ZoneInfoSource>(new TZoneInfoSource(std::move(tzData))); - } - - size_t Read(void* buf, size_t size) override { - return Stream_.Read(buf, size); - } - - int Skip(size_t offset) override { - Stream_.Skip(offset); - return 0; - } - - private: - const TString TimeZoneData_; - TStringInput Stream_; - }; - - std::unique_ptr<cctz::ZoneInfoSource> CustomFactory( - const std::string& name, - const std::function<std::unique_ptr<cctz::ZoneInfoSource>(const std::string& name)>& /*fallback*/ - ) - { - std::unique_ptr<cctz::ZoneInfoSource> zis = TZoneInfoSource::LoadZone(name); - if (zis) { - return zis; - } - return nullptr; - } - - } // namespace - - ZoneInfoSourceFactory zone_info_source_factory = CustomFactory; - -} // namespace cctz_extension + return nullptr; + } + return std::unique_ptr<cctz::ZoneInfoSource>(new TZoneInfoSource(std::move(tzData))); + } + + size_t Read(void* buf, size_t size) override { + return Stream_.Read(buf, size); + } + + int Skip(size_t offset) override { + Stream_.Skip(offset); + return 0; + } + + private: + const TString TimeZoneData_; + TStringInput Stream_; + }; + + std::unique_ptr<cctz::ZoneInfoSource> CustomFactory( + const std::string& name, + const std::function<std::unique_ptr<cctz::ZoneInfoSource>(const std::string& name)>& /*fallback*/ + ) + { + std::unique_ptr<cctz::ZoneInfoSource> zis = TZoneInfoSource::LoadZone(name); + if (zis) { + return zis; + } + return nullptr; + } + + } // namespace + + ZoneInfoSourceFactory zone_info_source_factory = CustomFactory; + +} // namespace cctz_extension diff --git a/contrib/libs/cctz/tzdata/update_tzdata.py b/contrib/libs/cctz/tzdata/update_tzdata.py index 9c3cd07352..2f741ce1b8 100755 --- a/contrib/libs/cctz/tzdata/update_tzdata.py +++ b/contrib/libs/cctz/tzdata/update_tzdata.py @@ -50,7 +50,7 @@ def create_cmakelists(zoneinfo_dir): cmd = 'add' if os.path.basename(fn) in all_hashes else 'remove' subprocess.check_call(['svn', cmd, '--force', fn]) - with open('ya.make.resources', 'w') as f: + with open('ya.make.resources', 'w') as f: print >>f, yamake_template.format(resources) def get_latest_iana_version(): @@ -61,11 +61,11 @@ def get_latest_iana_version(): return version_match.group(1) def get_current_version(): - try: - with open('VERSION') as f: - return f.read() - except: - return 0 + try: + with open('VERSION') as f: + return f.read() + except: + return 0 def prepare_tzdata(version): temp_dir = tempfile.mkdtemp() @@ -86,7 +86,7 @@ def prepare_tzdata(version): print 'Converting tzdata to binary format' subprocess.check_call(['make', '-s', '-C', temp_dir, 'TOPDIR={}'.format(temp_dir), 'install']) - print 'Preparing ya.make.resources' + print 'Preparing ya.make.resources' zoneinfo_dir = os.path.join(temp_dir, 'usr', 'share', 'zoneinfo') create_cmakelists(zoneinfo_dir) finally: diff --git a/contrib/libs/cctz/tzdata/ya.make b/contrib/libs/cctz/tzdata/ya.make index 3b9ff5cc67..1173cf0627 100644 --- a/contrib/libs/cctz/tzdata/ya.make +++ b/contrib/libs/cctz/tzdata/ya.make @@ -13,10 +13,10 @@ PEERDIR( contrib/libs/cctz ) -INCLUDE(ya.make.resources) +INCLUDE(ya.make.resources) -SRCS( - GLOBAL factory.cpp +SRCS( + GLOBAL factory.cpp ) END() diff --git a/contrib/libs/cctz/tzdata/ya.make.resources b/contrib/libs/cctz/tzdata/ya.make.resources index 5c7575f3bd..0e38f3df1d 100644 --- a/contrib/libs/cctz/tzdata/ya.make.resources +++ b/contrib/libs/cctz/tzdata/ya.make.resources @@ -1,4 +1,4 @@ -RESOURCE( +RESOURCE( generated/58543f30ac34b6510b552b9b3e82b772 /cctz/tzdata/posixrules generated/796a57137d718e4fa3db8ef611f18e61 /cctz/tzdata/Africa/Abidjan generated/33cffb9ac13285f945f3c4ef6ce13585 /cctz/tzdata/Africa/Accra @@ -594,4 +594,4 @@ RESOURCE( generated/39b47bf37a27f7bcd5d3f7c51343c7fc /cctz/tzdata/W-SU generated/0124cd65b22dfd92129cb0a43719c717 /cctz/tzdata/WET generated/51d8a0e68892ebf0854a1b4250ffb26b /cctz/tzdata/Zulu -) +) |