- fd5f3d7077270ffc5ea74cdb9e18bbae3b9b46aa Fix typo optional -> variant by Abseil Team <absl-team@google.com>
- 9136c06dfa8dbfdde0a427ad3509e34763d607a6 Fix string_view_test and str_cat_test build under MSVC de... by Derek Mauro <dmauro@google.com> - a463820f9441888f4368aa87328599e3209f9b07 Removes constexpr optional<T>::operator->(). This was don... by Abseil Team <absl-team@google.com> - 3bf78a7f126daafff329f7815d507422f1ca378d Remove dependencies on external CCTZ project. by Shaindel Schwartz <shaindel@google.com> - a4ae574a11b1ddf6e88459af3d638cf79aea7ecd Internal change by Jon Cohen <cohenjon@google.com> GitOrigin-RevId: fd5f3d7077270ffc5ea74cdb9e18bbae3b9b46aa Change-Id: I6ab8ab99863716fe9b2745a12ef285f7a6da6d1epull/117/head
parent
94ce52d46c
commit
af7882601a
638 changed files with 9262 additions and 58 deletions
@ -0,0 +1,105 @@ |
||||
# 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 |
||||
# |
||||
# http://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. |
||||
|
||||
licenses(["notice"]) # Apache License |
||||
|
||||
### libraries |
||||
|
||||
cc_library( |
||||
name = "includes", |
||||
textual_hdrs = [ |
||||
"include/cctz/civil_time.h", |
||||
"include/cctz/civil_time_detail.h", |
||||
"include/cctz/time_zone.h", |
||||
], |
||||
visibility = ["//absl/time:__pkg__"], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "civil_time", |
||||
srcs = ["src/civil_time_detail.cc"], |
||||
hdrs = [ |
||||
"include/cctz/civil_time.h", |
||||
], |
||||
textual_hdrs = ["include/cctz/civil_time_detail.h"], |
||||
visibility = ["//visibility:public"], |
||||
) |
||||
|
||||
cc_library( |
||||
name = "time_zone", |
||||
srcs = [ |
||||
"src/time_zone_fixed.cc", |
||||
"src/time_zone_fixed.h", |
||||
"src/time_zone_format.cc", |
||||
"src/time_zone_if.cc", |
||||
"src/time_zone_if.h", |
||||
"src/time_zone_impl.cc", |
||||
"src/time_zone_impl.h", |
||||
"src/time_zone_info.cc", |
||||
"src/time_zone_info.h", |
||||
"src/time_zone_libc.cc", |
||||
"src/time_zone_libc.h", |
||||
"src/time_zone_lookup.cc", |
||||
"src/time_zone_posix.cc", |
||||
"src/time_zone_posix.h", |
||||
"src/tzfile.h", |
||||
"src/zone_info_source.cc", |
||||
], |
||||
hdrs = [ |
||||
"include/cctz/time_zone.h", |
||||
"include/cctz/zone_info_source.h", |
||||
], |
||||
visibility = ["//visibility:public"], |
||||
deps = [":civil_time"], |
||||
) |
||||
|
||||
### tests |
||||
|
||||
cc_test( |
||||
name = "civil_time_test", |
||||
size = "small", |
||||
srcs = ["src/civil_time_test.cc"], |
||||
deps = [ |
||||
":civil_time", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "time_zone_format_test", |
||||
size = "small", |
||||
srcs = ["src/time_zone_format_test.cc"], |
||||
deps = [ |
||||
":civil_time", |
||||
":time_zone", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
cc_test( |
||||
name = "time_zone_lookup_test", |
||||
size = "small", |
||||
srcs = ["src/time_zone_lookup_test.cc"], |
||||
deps = [ |
||||
":civil_time", |
||||
":time_zone", |
||||
"@com_google_googletest//:gtest_main", |
||||
], |
||||
) |
||||
|
||||
### benchmarks |
||||
|
||||
### examples |
||||
|
||||
### binaries |
@ -0,0 +1,329 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_ |
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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
|
||||
// std::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 aligment 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; |
||||
|
||||
// Returns the weekday for the given civil_day.
|
||||
//
|
||||
// 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 = prev_weekday(d, weekday::thursday) + 7;
|
||||
// // Gets the previous Thursday if d is not already Thursday
|
||||
// civil_day thurs2 = next_weekday(d, weekday::thursday) - 7;
|
||||
//
|
||||
using detail::next_weekday; |
||||
using detail::prev_weekday; |
||||
|
||||
// Returns the day-of-year for the given civil_day.
|
||||
//
|
||||
// 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
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
|
@ -0,0 +1,564 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_ |
||||
|
||||
#include <cstdint> |
||||
#include <limits> |
||||
#include <ostream> |
||||
#include <type_traits> |
||||
|
||||
// Disable constexpr support unless we are using clang in C++14 mode.
|
||||
#if __clang__ && __cpp_constexpr >= 201304 |
||||
#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 absl { |
||||
namespace time_internal { |
||||
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 { |
||||
y += (cd / 146097) * 400; |
||||
cd %= 146097; |
||||
if (cd < 0) { |
||||
y -= 400; |
||||
cd += 146097; |
||||
} |
||||
y += (d / 146097) * 400; |
||||
d = d % 146097 + cd; |
||||
if (d > 0) { |
||||
if (d > 146097) { |
||||
y += 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.
|
||||
y -= 1; |
||||
d += days_per_year(y, m); |
||||
} else { |
||||
y -= 400; |
||||
d += 146097; |
||||
} |
||||
} |
||||
if (d > 365) { |
||||
for (int n = days_per_century(y, m); d > n; n = days_per_century(y, m)) { |
||||
d -= n; |
||||
y += 100; |
||||
} |
||||
for (int n = days_per_4years(y, m); d > n; n = days_per_4years(y, m)) { |
||||
d -= n; |
||||
y += 4; |
||||
} |
||||
for (int n = days_per_year(y, m); d > n; n = days_per_year(y, m)) { |
||||
d -= n; |
||||
++y; |
||||
} |
||||
} |
||||
if (d > 28) { |
||||
for (int n = days_per_month(y, m); d > n; n = days_per_month(y, m)) { |
||||
d -= n; |
||||
if (++m > 12) { |
||||
++y; |
||||
m = 1; |
||||
} |
||||
} |
||||
} |
||||
return fields(y, 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.
|
||||
static 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); |
||||
} |
||||
static 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 { |
||||
f_ = step(T{}, f_, n); |
||||
return *this; |
||||
} |
||||
CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept { |
||||
if (n != std::numeric_limits<diff_t>::min()) { |
||||
f_ = step(T{}, f_, -n); |
||||
} else { |
||||
f_ = step(T{}, step(T{}, f_, -(n + 1)), 1); |
||||
} |
||||
return *this; |
||||
} |
||||
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.
|
||||
inline friend CONSTEXPR_M civil_time operator+(civil_time a, |
||||
diff_t n) noexcept { |
||||
return a += n; |
||||
} |
||||
inline friend CONSTEXPR_M civil_time operator+(diff_t n, |
||||
civil_time a) noexcept { |
||||
return a += n; |
||||
} |
||||
inline friend CONSTEXPR_M civil_time operator-(civil_time a, |
||||
diff_t n) noexcept { |
||||
return a -= n; |
||||
} |
||||
inline friend CONSTEXPR_M diff_t operator-(const civil_time& lhs, |
||||
const 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.
|
||||
template <typename Tag1, typename Tag2> |
||||
CONSTEXPR_F diff_t operator-(civil_time<Tag1>, civil_time<Tag2>) = 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, |
||||
}; |
||||
|
||||
CONSTEXPR_F weekday get_weekday(const civil_day& cd) noexcept { |
||||
CONSTEXPR_D weekday k_weekday_by_sun_off[7] = { |
||||
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, |
||||
}; |
||||
year_t wd = cd.year() - (cd.month() < 3); |
||||
if (wd >= 0) { |
||||
wd += wd / 4 - wd / 100 + wd / 400; |
||||
} else { |
||||
wd += (wd - 3) / 4 - (wd - 99) / 100 + (wd - 399) / 400; |
||||
} |
||||
wd += k_weekday_offsets[cd.month()] + cd.day(); |
||||
return k_weekday_by_sun_off[(wd % 7 + 7) % 7]; |
||||
} |
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept { |
||||
do { cd += 1; } while (get_weekday(cd) != wd); |
||||
return cd; |
||||
} |
||||
|
||||
CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept { |
||||
do { cd -= 1; } while (get_weekday(cd) != wd); |
||||
return cd; |
||||
} |
||||
|
||||
CONSTEXPR_F int get_yearday(const civil_day& cd) noexcept { |
||||
CONSTEXPR_D int k_month_offsets[1 + 12] = { |
||||
-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, |
||||
}; |
||||
const int feb29 = (cd.month() > 2 && impl::is_leap_year(cd.year())); |
||||
return k_month_offsets[cd.month()] + feb29 + cd.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
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#undef CONSTEXPR_M |
||||
#undef CONSTEXPR_F |
||||
#undef CONSTEXPR_D |
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
|
@ -0,0 +1,316 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_ |
||||
|
||||
#include <chrono> |
||||
#include <cstdint> |
||||
#include <string> |
||||
#include <utility> |
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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 sys_seconds = std::chrono::duration<std::int_fast64_t>; |
||||
|
||||
namespace detail { |
||||
template <typename D> |
||||
inline std::pair<time_point<sys_seconds>, D> |
||||
split_seconds(const time_point<D>& tp) { |
||||
auto sec = std::chrono::time_point_cast<sys_seconds>(tp); |
||||
auto sub = tp - sec; |
||||
if (sub.count() < 0) { |
||||
sec -= sys_seconds(1); |
||||
sub += sys_seconds(1); |
||||
} |
||||
return {sec, std::chrono::duration_cast<D>(sub)}; |
||||
} |
||||
inline std::pair<time_point<sys_seconds>, sys_seconds> |
||||
split_seconds(const time_point<sys_seconds>& tp) { |
||||
return {tp, sys_seconds(0)}; |
||||
} |
||||
} // 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
|
||||
// - http://en.wikipedia.org/wiki/Zoneinfo
|
||||
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")
|
||||
}; |
||||
absolute_lookup lookup(const time_point<sys_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,
|
||||
// respectively (all three times are equal if kind == UNIQUE). If any
|
||||
// of these three absolute times is outside the representable range of a
|
||||
// time_point<sys_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; |
||||
time_point<sys_seconds> pre; // uses the pre-transition offset
|
||||
time_point<sys_seconds> trans; // instant of civil-offset change
|
||||
time_point<sys_seconds> post; // uses the post-transition offset
|
||||
}; |
||||
civil_lookup lookup(const civil_second& cs) const; |
||||
|
||||
class Impl; |
||||
|
||||
private: |
||||
explicit time_zone(const Impl* impl) : impl_(impl) {} |
||||
const Impl* impl_; |
||||
}; |
||||
|
||||
// Relational operators.
|
||||
bool operator==(time_zone lhs, time_zone rhs); |
||||
inline bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); } |
||||
|
||||
// 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 sys_seconds& offset); |
||||
|
||||
// Returns a time zone representing the local time zone. Falls back to UTC.
|
||||
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<sys_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>; |
||||
std::string format(const std::string&, const time_point<sys_seconds>&, |
||||
const femtoseconds&, const time_zone&); |
||||
bool parse(const std::string&, const std::string&, const time_zone&, |
||||
time_point<sys_seconds>*, femtoseconds*, std::string* err = nullptr); |
||||
} // namespace detail
|
||||
|
||||
// Formats the given time_point in the given cctz::time_zone according to
|
||||
// the provided format std::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)
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// Tip: Format strings should include the UTC offset (e.g., %z, %Ez, or %E*z)
|
||||
// so that the resulting std::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 std::string according to the provided format std::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.
|
||||
//
|
||||
// %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 std::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) { |
||||
time_point<sys_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; |
||||
} |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
|
@ -0,0 +1,91 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_ |
||||
|
||||
#include <cstddef> |
||||
#include <functional> |
||||
#include <memory> |
||||
#include <string> |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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()
|
||||
}; |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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<absl::time_internal::cctz::ZoneInfoSource> (*)( |
||||
const std::string&, |
||||
const std::function<std::unique_ptr<absl::time_internal::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
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
|
@ -0,0 +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
|
||||
//
|
||||
// http://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 "absl/time/internal/cctz/include/cctz/civil_time_detail.h" |
||||
|
||||
#include <iomanip> |
||||
#include <ostream> |
||||
#include <sstream> |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,133 @@ |
||||
// 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
|
||||
//
|
||||
// http://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> |
||||
#include <chrono> |
||||
#include <cstdio> |
||||
#include <cstring> |
||||
#include <string> |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
namespace cctz { |
||||
|
||||
namespace { |
||||
|
||||
// The prefix used for the internal names of fixed-offset zones.
|
||||
const char kFixedOffsetPrefix[] = "Fixed/"; |
||||
|
||||
int Parse02d(const char* p) { |
||||
static const char kDigits[] = "0123456789"; |
||||
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, sys_seconds* offset) { |
||||
if (name.compare(0, std::string::npos, "UTC", 3) == 0) { |
||||
*offset = sys_seconds::zero(); |
||||
return true; |
||||
} |
||||
|
||||
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1; |
||||
const char* const ep = kFixedOffsetPrefix + prefix_len; |
||||
if (name.size() != prefix_len + 12) // "<prefix>UTC+99:99:99"
|
||||
return false; |
||||
if (!std::equal(kFixedOffsetPrefix, ep, name.begin())) |
||||
return false; |
||||
const char* np = name.data() + prefix_len; |
||||
if (*np++ != 'U' || *np++ != 'T' || *np++ != 'C') |
||||
return false; |
||||
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 = sys_seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
|
||||
return true; |
||||
} |
||||
|
||||
std::string FixedOffsetToName(const sys_seconds& offset) { |
||||
if (offset == sys_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"; |
||||
} |
||||
int seconds = static_cast<int>(offset.count()); |
||||
const char sign = (seconds < 0 ? '-' : '+'); |
||||
int minutes = seconds / 60; |
||||
seconds %= 60; |
||||
if (sign == '-') { |
||||
if (seconds > 0) { |
||||
seconds -= 60; |
||||
minutes += 1; |
||||
} |
||||
seconds = -seconds; |
||||
minutes = -minutes; |
||||
} |
||||
int hours = minutes / 60; |
||||
minutes %= 60; |
||||
char buf[sizeof(kFixedOffsetPrefix) + sizeof("UTC-24:00:00")]; |
||||
snprintf(buf, sizeof(buf), "%sUTC%c%02d:%02d:%02d", |
||||
kFixedOffsetPrefix, sign, hours, minutes, seconds); |
||||
return buf; |
||||
} |
||||
|
||||
std::string FixedOffsetToAbbr(const sys_seconds& offset) { |
||||
std::string abbr = FixedOffsetToName(offset); |
||||
const std::size_t prefix_len = sizeof(kFixedOffsetPrefix) - 1; |
||||
const char* const ep = kFixedOffsetPrefix + prefix_len; |
||||
if (abbr.size() >= prefix_len) { |
||||
if (std::equal(kFixedOffsetPrefix, ep, abbr.begin())) { |
||||
abbr.erase(0, prefix_len); |
||||
if (abbr.size() == 12) { // UTC+99:99:99
|
||||
abbr.erase(9, 1); // UTC+99:9999
|
||||
abbr.erase(6, 1); // UTC+999999
|
||||
if (abbr[8] == '0' && abbr[9] == '0') { // UTC+999900
|
||||
abbr.erase(8, 2); // UTC+9999
|
||||
if (abbr[6] == '0' && abbr[7] == '0') { // UTC+9900
|
||||
abbr.erase(6, 2); // UTC+99
|
||||
if (abbr[4] == '0') { // UTC+09
|
||||
abbr.erase(4, 1); // UTC+9
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return abbr; |
||||
} |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
@ -0,0 +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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_ |
||||
|
||||
#include <string> |
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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, sys_seconds* offset); |
||||
std::string FixedOffsetToName(const sys_seconds& offset); |
||||
std::string FixedOffsetToAbbr(const sys_seconds& offset); |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
|
@ -0,0 +1,848 @@ |
||||
// 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
|
||||
//
|
||||
// http://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) |
||||
# if !defined(_MSC_VER) |
||||
# define HAS_STRPTIME 1 // assume everyone has strptime() except windows
|
||||
# endif |
||||
#endif |
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.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 "absl/time/internal/cctz/include/cctz/civil_time.h" |
||||
#include "time_zone_if.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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 |
||||
|
||||
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); |
||||
} |
||||
|
||||
switch (get_weekday(civil_day(al.cs))) { |
||||
case weekday::sunday: |
||||
tm.tm_wday = 0; |
||||
break; |
||||
case weekday::monday: |
||||
tm.tm_wday = 1; |
||||
break; |
||||
case weekday::tuesday: |
||||
tm.tm_wday = 2; |
||||
break; |
||||
case weekday::wednesday: |
||||
tm.tm_wday = 3; |
||||
break; |
||||
case weekday::thursday: |
||||
tm.tm_wday = 4; |
||||
break; |
||||
case weekday::friday: |
||||
tm.tm_wday = 5; |
||||
break; |
||||
case weekday::saturday: |
||||
tm.tm_wday = 6; |
||||
break; |
||||
} |
||||
tm.tm_yday = get_yearday(civil_day(al.cs)) - 1; |
||||
tm.tm_isdst = al.is_dst ? 1 : 0; |
||||
return tm; |
||||
} |
||||
|
||||
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) { |
||||
char sign = '+'; |
||||
if (offset < 0) { |
||||
offset = -offset; // bounded by 24h so no overflow
|
||||
sign = '-'; |
||||
} |
||||
char sep = mode[0]; |
||||
if (sep != '\0' && mode[1] == '*') { |
||||
ep = Format02d(ep, offset % 60); |
||||
*--ep = sep; |
||||
} |
||||
int minutes = offset / 60; |
||||
ep = Format02d(ep, minutes % 60); |
||||
if (sep != '\0') *--ep = sep; |
||||
ep = Format02d(ep, minutes / 60); |
||||
*--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 std::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)
|
||||
//
|
||||
// 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<sys_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.
|
||||
if (strchr("YmdeHMSzZs%", *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; |
||||
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; |
||||
case '%': |
||||
result.push_back('%'); |
||||
break; |
||||
} |
||||
pending = ++cur; |
||||
continue; |
||||
} |
||||
|
||||
// Loop if there is no E modifier.
|
||||
if (*cur != 'E' || ++cur == end) continue; |
||||
|
||||
// Format our extensions.
|
||||
if (*cur == 'z') { |
||||
// 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; |
||||
} else if (*cur == '*' && cur + 1 != end && *(cur + 1) == 'z') { |
||||
// Formats %E*z.
|
||||
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 += 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 { |
||||
|
||||
const char* ParseOffset(const char* dp, const char* mode, int* offset) { |
||||
if (dp != nullptr) { |
||||
const char first = *dp++; |
||||
if (first == '+' || first == '-') { |
||||
char sep = mode[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); |
||||
if (bp != nullptr && bp - ap == 2) { |
||||
dp = bp; |
||||
if (sep != '\0' && *bp == sep) ++bp; |
||||
const char* cp = ParseInt(bp, 2, 0, 59, &seconds); |
||||
if (cp != nullptr && cp - bp == 2) dp = cp; |
||||
} |
||||
*offset = ((hours * 60 + minutes) * 60) + seconds; |
||||
if (first == '-') *offset = -*offset; |
||||
} else { |
||||
dp = nullptr; |
||||
} |
||||
} else if (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 std::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; |
||||
} |
||||
|
||||
} // 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.
|
||||
//
|
||||
// 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<sys_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; |
||||
|
||||
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; |
||||
continue; |
||||
case 'd': |
||||
case 'e': |
||||
data = ParseInt(data, 2, 1, 31, &tm.tm_mday); |
||||
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': |
||||
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; |
||||
case '%': |
||||
data = (*data == '%' ? data + 1 : nullptr); |
||||
continue; |
||||
case 'E': |
||||
if (*fmt == 'z' || (*fmt == '*' && *(fmt + 1) == 'z')) { |
||||
data = ParseOffset(data, ":", &offset); |
||||
if (data != nullptr) saw_offset = true; |
||||
fmt += (*fmt == 'z') ? 1 : 2; |
||||
continue; |
||||
} |
||||
if (*fmt == '*' && *(fmt + 1) == 'S') { |
||||
data = ParseInt(data, 2, 0, 60, &tm.tm_sec); |
||||
if (data != nullptr && *data == '.') { |
||||
data = ParseSubSeconds(data + 1, &subseconds); |
||||
} |
||||
fmt += 2; |
||||
continue; |
||||
} |
||||
if (*fmt == '*' && *(fmt + 1) == 'f') { |
||||
if (data != nullptr && std::isdigit(*data)) { |
||||
data = ParseSubSeconds(data, &subseconds); |
||||
} |
||||
fmt += 2; |
||||
continue; |
||||
} |
||||
if (*fmt == '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
|
||||
// was AM or PM so that we can adjust tm_hour before ConvertDateTime().
|
||||
// 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 std::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; |
||||
} |
||||
|
||||
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<sys_seconds>::max()) { |
||||
const auto al = ptz.lookup(time_point<sys_seconds>::max()); |
||||
if (cs > al.cs) { |
||||
if (err != nullptr) *err = "Out-of-range field"; |
||||
return false; |
||||
} |
||||
} |
||||
if (tp == time_point<sys_seconds>::min()) { |
||||
const auto al = ptz.lookup(time_point<sys_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
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,41 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 absl { |
||||
namespace time_internal { |
||||
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() {} |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
@ -0,0 +1,70 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_ |
||||
|
||||
#include <chrono> |
||||
#include <cstdint> |
||||
#include <memory> |
||||
#include <string> |
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h" |
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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( |
||||
const time_point<sys_seconds>& tp) const = 0; |
||||
virtual time_zone::civil_lookup MakeTime( |
||||
const civil_second& cs) const = 0; |
||||
|
||||
virtual std::string Description() const = 0; |
||||
virtual bool NextTransition(time_point<sys_seconds>* tp) const = 0; |
||||
virtual bool PrevTransition(time_point<sys_seconds>* tp) const = 0; |
||||
|
||||
protected: |
||||
TimeZoneIf() {} |
||||
}; |
||||
|
||||
// Convert between time_point<sys_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.
|
||||
inline std::int_fast64_t ToUnixSeconds(const time_point<sys_seconds>& tp) { |
||||
return (tp - std::chrono::time_point_cast<sys_seconds>( |
||||
std::chrono::system_clock::from_time_t(0))) |
||||
.count(); |
||||
} |
||||
inline time_point<sys_seconds> FromUnixSeconds(std::int_fast64_t t) { |
||||
return std::chrono::time_point_cast<sys_seconds>( |
||||
std::chrono::system_clock::from_time_t(0)) + |
||||
sys_seconds(t); |
||||
} |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
|
@ -0,0 +1,117 @@ |
||||
// 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
|
||||
//
|
||||
// http://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" |
||||
|
||||
#include <mutex> |
||||
#include <string> |
||||
#include <unordered_map> |
||||
#include <utility> |
||||
|
||||
#include "time_zone_fixed.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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 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) { |
||||
const time_zone::Impl* const utc_impl = UTCImpl(); |
||||
|
||||
// First check for UTC (which is never a key in time_zone_map).
|
||||
auto offset = sys_seconds::zero(); |
||||
if (FixedOffsetFromName(name, &offset) && offset == sys_seconds::zero()) { |
||||
*tz = time_zone(utc_impl); |
||||
return true; |
||||
} |
||||
|
||||
// Then check, under a shared lock, whether the time zone has already
|
||||
// been loaded. This is the common path. TODO: Move to shared_mutex.
|
||||
{ |
||||
std::lock_guard<std::mutex> lock(time_zone_mutex); |
||||
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; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Now check again, under an exclusive lock.
|
||||
std::lock_guard<std::mutex> lock(time_zone_mutex); |
||||
if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName; |
||||
const Impl*& impl = (*time_zone_map)[name]; |
||||
if (impl == nullptr) { |
||||
// The first thread in loads the new time zone.
|
||||
Impl* new_impl = new Impl(name); |
||||
new_impl->zone_ = TimeZoneIf::Load(new_impl->name_); |
||||
if (new_impl->zone_ == nullptr) { |
||||
delete new_impl; // free the nascent Impl
|
||||
impl = utc_impl; // and fallback to UTC
|
||||
} else { |
||||
impl = new_impl; // install new time zone
|
||||
} |
||||
} |
||||
*tz = time_zone(impl); |
||||
return impl != utc_impl; |
||||
} |
||||
|
||||
const time_zone::Impl& time_zone::Impl::get(const time_zone& tz) { |
||||
if (tz.impl_ == nullptr) { |
||||
// Dereferencing an implicit-UTC time_zone is expected to be
|
||||
// rare, so we don't mind paying a small synchronization cost.
|
||||
return *UTCImpl(); |
||||
} |
||||
return *tz.impl_; |
||||
} |
||||
|
||||
void time_zone::Impl::ClearTimeZoneMapTestOnly() { |
||||
std::lock_guard<std::mutex> lock(time_zone_mutex); |
||||
if (time_zone_map != nullptr) { |
||||
// Existing time_zone::Impl* entries are in the wild, so we simply
|
||||
// leak them. Future requests will result in reloading the data.
|
||||
time_zone_map->clear(); |
||||
} |
||||
} |
||||
|
||||
time_zone::Impl::Impl(const std::string& name) : name_(name) {} |
||||
|
||||
const time_zone::Impl* time_zone::Impl::UTCImpl() { |
||||
static Impl* utc_impl = [] { |
||||
Impl* impl = new Impl("UTC"); |
||||
impl->zone_ = TimeZoneIf::Load(impl->name_); // never fails
|
||||
return impl; |
||||
}(); |
||||
return utc_impl; |
||||
} |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
@ -0,0 +1,97 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_ |
||||
|
||||
#include <memory> |
||||
#include <string> |
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h" |
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h" |
||||
#include "time_zone_if.h" |
||||
#include "time_zone_info.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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); |
||||
|
||||
// Dereferences the time_zone to obtain its Impl.
|
||||
static const time_zone::Impl& get(const 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 { return name_; } |
||||
|
||||
// Breaks a time_point down to civil-time components in this time zone.
|
||||
time_zone::absolute_lookup BreakTime( |
||||
const time_point<sys_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); |
||||
} |
||||
|
||||
// Returns an implementation-specific description of this time zone.
|
||||
std::string Description() const { return zone_->Description(); } |
||||
|
||||
// Finds the time of the next/previous offset change in this time zone.
|
||||
//
|
||||
// By definition, NextTransition(&tp) returns false when tp has its
|
||||
// maximum value, and PrevTransition(&tp) returns false when tp has its
|
||||
// mimimum value. If the zone has no transitions, the result will also
|
||||
// be false no matter what the argument.
|
||||
//
|
||||
// Otherwise, when tp has its mimimum value, NextTransition(&tp) returns
|
||||
// true and sets tp to the first recorded transition. Chains of calls
|
||||
// to NextTransition()/PrevTransition() will eventually return false,
|
||||
// but it is unspecified exactly when NextTransition(&tp) jumps to false,
|
||||
// or what time is set by PrevTransition(&tp) for a very distant tp.
|
||||
bool NextTransition(time_point<sys_seconds>* tp) const { |
||||
return zone_->NextTransition(tp); |
||||
} |
||||
bool PrevTransition(time_point<sys_seconds>* tp) const { |
||||
return zone_->PrevTransition(tp); |
||||
} |
||||
|
||||
private: |
||||
explicit Impl(const std::string& name); |
||||
static const Impl* UTCImpl(); |
||||
|
||||
const std::string name_; |
||||
std::unique_ptr<TimeZoneIf> zone_; |
||||
}; |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
|
@ -0,0 +1,956 @@ |
||||
// 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
|
||||
//
|
||||
// http://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
|
||||
// http://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 <iostream> |
||||
#include <memory> |
||||
#include <sstream> |
||||
#include <string> |
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h" |
||||
#include "time_zone_fixed.h" |
||||
#include "time_zone_posix.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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, |
||||
}; |
||||
|
||||
// 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<sys_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; |
||||
} |
||||
|
||||
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.
|
||||
bool TimeZoneInfo::ResetToBuiltinUTC(const sys_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; |
||||
|
||||
// We temporarily add some redundant, contemporary (2012 through 2021)
|
||||
// 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), // BIG_BANG
|
||||
1325376000LL, // 2012-01-01T00:00:00+00:00
|
||||
1356998400LL, // 2013-01-01T00:00:00+00:00
|
||||
1388534400LL, // 2014-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
|
||||
2147483647LL, // 2^31 - 1
|
||||
}) { |
||||
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'); // add NUL
|
||||
future_spec_.clear(); // never needed for a fixed-offset zone
|
||||
extended_ = false; |
||||
|
||||
tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs; |
||||
tt.civil_min = LocalTime(sys_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); |
||||
if ((v = Decode32(tzh.tzh_ttisgmtcnt)) < 0) return false; |
||||
ttisgmtcnt = 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
|
||||
len += 1 * ttisgmtcnt; // standard/wall indicators
|
||||
return len; |
||||
} |
||||
|
||||
// Check that the TransitionType has the expected offset/is_dst/abbreviation.
|
||||
void TimeZoneInfo::CheckTransition(const std::string& name, |
||||
const TransitionType& tt, |
||||
std::int_fast32_t offset, bool is_dst, |
||||
const std::string& abbr) const { |
||||
if (tt.utc_offset != offset || tt.is_dst != is_dst || |
||||
&abbreviations_[tt.abbr_index] != abbr) { |
||||
std::clog << name << ": Transition" |
||||
<< " offset=" << tt.utc_offset << "/" |
||||
<< (tt.is_dst ? "DST" : "STD") |
||||
<< "/abbr=" << &abbreviations_[tt.abbr_index] |
||||
<< " does not match POSIX spec '" << future_spec_ << "'\n"; |
||||
} |
||||
} |
||||
|
||||
// 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.is_dst != tt2.is_dst) return false; |
||||
if (tt1.utc_offset != tt2.utc_offset) return false; |
||||
if (tt1.abbr_index != tt2.abbr_index) return false; |
||||
return true; |
||||
} |
||||
|
||||
// Use the POSIX-TZ-environment-variable-style std::string to handle times
|
||||
// in years after the last transition stored in the zoneinfo data.
|
||||
void TimeZoneInfo::ExtendTransitions(const std::string& name, |
||||
const Header& hdr) { |
||||
extended_ = false; |
||||
bool extending = !future_spec_.empty(); |
||||
|
||||
PosixTimeZone posix; |
||||
if (extending && !ParsePosixSpec(future_spec_, &posix)) { |
||||
std::clog << name << ": Failed to parse '" << future_spec_ << "'\n"; |
||||
extending = false; |
||||
} |
||||
|
||||
if (extending && posix.dst_abbr.empty()) { // std only
|
||||
// The future specification should match the last/default transition,
|
||||
// and that means that handling the future will fall out naturally.
|
||||
std::uint_fast8_t index = default_transition_type_; |
||||
if (hdr.timecnt != 0) index = transitions_[hdr.timecnt - 1].type_index; |
||||
const TransitionType& tt(transition_types_[index]); |
||||
CheckTransition(name, tt, posix.std_offset, false, posix.std_abbr); |
||||
extending = false; |
||||
} |
||||
|
||||
if (extending && hdr.timecnt < 2) { |
||||
std::clog << name << ": Too few transitions for POSIX spec\n"; |
||||
extending = false; |
||||
} |
||||
|
||||
if (!extending) { |
||||
// Ensure that there is always a transition in the second half of the
|
||||
// time line (the BIG_BANG transition is in the first half) so that the
|
||||
// signed difference between a civil_second and the civil_second of its
|
||||
// previous transition is always representable, without overflow.
|
||||
const Transition& last(transitions_.back()); |
||||
if (last.unix_time < 0) { |
||||
const std::uint_fast8_t type_index = last.type_index; |
||||
Transition& tr(*transitions_.emplace(transitions_.end())); |
||||
tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00
|
||||
tr.type_index = type_index; |
||||
} |
||||
return; // last transition wins
|
||||
} |
||||
|
||||
// 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.
|
||||
// zic(8) should probably do this so that we don't have to.
|
||||
// TODO: Reduce the extension by the number of compatible
|
||||
// transitions already in place.
|
||||
transitions_.reserve(hdr.timecnt + 400 * 2 + 1); |
||||
transitions_.resize(hdr.timecnt + 400 * 2); |
||||
extended_ = true; |
||||
|
||||
// The future specification should match the last two transitions,
|
||||
// and those transitions should have different is_dst flags. Note
|
||||
// that nothing says the UTC offset used by the is_dst transition
|
||||
// must be greater than that used by the !is_dst transition. (See
|
||||
// Europe/Dublin, for example.)
|
||||
const Transition* tr0 = &transitions_[hdr.timecnt - 1]; |
||||
const Transition* tr1 = &transitions_[hdr.timecnt - 2]; |
||||
const TransitionType* tt0 = &transition_types_[tr0->type_index]; |
||||
const TransitionType* tt1 = &transition_types_[tr1->type_index]; |
||||
const TransitionType& dst(tt0->is_dst ? *tt0 : *tt1); |
||||
const TransitionType& std(tt0->is_dst ? *tt1 : *tt0); |
||||
CheckTransition(name, dst, posix.dst_offset, true, posix.dst_abbr); |
||||
CheckTransition(name, std, posix.std_offset, false, posix.std_abbr); |
||||
|
||||
// Add the transitions to tr1 and back to tr0 for each extra year.
|
||||
last_year_ = LocalTime(tr0->unix_time, *tt0).cs.year(); |
||||
bool leap_year = IsLeap(last_year_); |
||||
const civil_day jan1(last_year_, 1, 1); |
||||
std::int_fast64_t jan1_time = civil_second(jan1) - civil_second(); |
||||
int jan1_weekday = (static_cast<int>(get_weekday(jan1)) + 1) % 7; |
||||
Transition* tr = &transitions_[hdr.timecnt]; // next trans to fill
|
||||
if (LocalTime(tr1->unix_time, *tt1).cs.year() != last_year_) { |
||||
// Add a single extra transition to align to a calendar year.
|
||||
transitions_.resize(transitions_.size() + 1); |
||||
assert(tr == &transitions_[hdr.timecnt]); // no reallocation
|
||||
const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start); |
||||
std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1); |
||||
tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset; |
||||
tr++->type_index = tr1->type_index; |
||||
tr0 = &transitions_[hdr.timecnt]; |
||||
tr1 = &transitions_[hdr.timecnt - 1]; |
||||
tt0 = &transition_types_[tr0->type_index]; |
||||
tt1 = &transition_types_[tr1->type_index]; |
||||
} |
||||
const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start); |
||||
const PosixTransition& pt0(tt0->is_dst ? posix.dst_start : posix.dst_end); |
||||
for (const year_t limit = last_year_ + 400; last_year_ < limit;) { |
||||
last_year_ += 1; // an additional year of generated transitions
|
||||
jan1_time += kSecsPerYear[leap_year]; |
||||
jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7; |
||||
leap_year = !leap_year && IsLeap(last_year_); |
||||
std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1); |
||||
tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset; |
||||
tr++->type_index = tr1->type_index; |
||||
std::int_fast64_t tr0_offset = TransOffset(leap_year, jan1_weekday, pt0); |
||||
tr->unix_time = jan1_time + tr0_offset - tt1->utc_offset; |
||||
tr++->type_index = tr0->type_index; |
||||
} |
||||
assert(tr == &transitions_[0] + transitions_.size()); |
||||
} |
||||
|
||||
bool TimeZoneInfo::Load(const std::string& name, 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; |
||||
if (hdr.ttisgmtcnt != 0 && hdr.ttisgmtcnt != 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.
|
||||
transitions_.reserve(hdr.timecnt + 2); // We might add a couple.
|
||||
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_.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_.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.ttisgmtcnt; // 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.
|
||||
auto get_char = [](ZoneInfoSource* zip) -> int { |
||||
unsigned char ch; // all non-EOF results are positive
|
||||
return (zip->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.
|
||||
|
||||
// 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 in ExtendTransitions()) so that
|
||||
// the signed difference between a civil_second and the civil_second of
|
||||
// its previous transition is always representable, without overflow.
|
||||
// A contemporary zic will usually have already done this for us.
|
||||
if (transitions_.empty() || transitions_.front().unix_time >= 0) { |
||||
Transition& tr(*transitions_.emplace(transitions_.begin())); |
||||
tr.unix_time = -(1LL << 59); // see tz/zic.c "BIG_BANG"
|
||||
tr.type_index = default_transition_type_; |
||||
hdr.timecnt += 1; |
||||
} |
||||
|
||||
// Extend the transitions using the future specification.
|
||||
ExtendTransitions(name, hdr); |
||||
|
||||
// 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
|
||||
// time_point<sys_seconds> for each of the zone's transition types.
|
||||
for (auto& tt : transition_types_) { |
||||
tt.civil_max = LocalTime(sys_seconds::max().count(), tt).cs; |
||||
tt.civil_min = LocalTime(sys_seconds::min().count(), tt).cs; |
||||
} |
||||
|
||||
transitions_.shrink_to_fit(); |
||||
return true; |
||||
} |
||||
|
||||
namespace { |
||||
|
||||
// fopen(3) adaptor.
|
||||
inline FILE* FOpen(const char* path, const char* mode) { |
||||
#if defined(_MSC_VER) |
||||
FILE* fp; |
||||
if (fopen_s(&fp, path, mode) != 0) fp = nullptr; |
||||
return fp; |
||||
#else |
||||
return fopen(path, mode); // TODO: Enable the close-on-exec flag.
|
||||
#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 { |
||||
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 { |
||||
offset = std::min(offset, len_); |
||||
int rc = fseek(fp_.get(), static_cast<long>(offset), SEEK_CUR); |
||||
if (rc == 0) len_ -= offset; |
||||
return rc; |
||||
} |
||||
|
||||
protected: |
||||
explicit FileZoneInfoSource( |
||||
FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max()) |
||||
: fp_(fp, fclose), len_(len) {} |
||||
|
||||
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.
|
||||
if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5)); |
||||
|
||||
// Map the time-zone name to a path name.
|
||||
std::string path; |
||||
if (name.empty() || name[0] != '/') { |
||||
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 += name; |
||||
|
||||
// Open the zoneinfo file.
|
||||
FILE* fp = FOpen(path.c_str(), "rb"); |
||||
if (fp == nullptr) return nullptr; |
||||
std::size_t length = 0; |
||||
if (fseek(fp, 0, SEEK_END) == 0) { |
||||
long pos = ftell(fp); |
||||
if (pos >= 0) { |
||||
length = static_cast<std::size_t>(pos); |
||||
} |
||||
rewind(fp); |
||||
} |
||||
return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length)); |
||||
} |
||||
|
||||
#if defined(__ANDROID__) |
||||
class AndroidZoneInfoSource : public FileZoneInfoSource { |
||||
public: |
||||
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name); |
||||
|
||||
private: |
||||
explicit AndroidZoneInfoSource(FILE* fp, std::size_t len) |
||||
: FileZoneInfoSource(fp, len) {} |
||||
}; |
||||
|
||||
std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open( |
||||
const std::string& name) { |
||||
// Use of the "file:" prefix is intended for testing purposes only.
|
||||
if (name.compare(0, 5, "file:") == 0) return Open(name.substr(5)); |
||||
|
||||
// See Android's libc/tzcode/bionic.cpp for additional information.
|
||||
for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata", |
||||
"/system/usr/share/zoneinfo/tzdata"}) { |
||||
std::unique_ptr<FILE, int (*)(FILE*)> fp(FOpen(tzdata, "rb"), fclose); |
||||
if (fp.get() == nullptr) continue; |
||||
|
||||
char hbuf[24]; // covers header.zonetab_offset too
|
||||
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue; |
||||
if (strncmp(hbuf, "tzdata", 6) != 0) continue; |
||||
const std::int_fast32_t index_offset = Decode32(hbuf + 12); |
||||
const std::int_fast32_t data_offset = Decode32(hbuf + 16); |
||||
if (index_offset < 0 || data_offset < index_offset) continue; |
||||
if (fseek(fp.get(), static_cast<long>(index_offset), SEEK_SET) != 0) |
||||
continue; |
||||
|
||||
char ebuf[52]; // covers entry.unused too
|
||||
const std::size_t index_size = |
||||
static_cast<std::size_t>(data_offset - index_offset); |
||||
const std::size_t zonecnt = index_size / sizeof(ebuf); |
||||
if (zonecnt * sizeof(ebuf) != index_size) continue; |
||||
for (std::size_t i = 0; i != zonecnt; ++i) { |
||||
if (fread(ebuf, 1, sizeof(ebuf), fp.get()) != sizeof(ebuf)) break; |
||||
const std::int_fast32_t start = data_offset + Decode32(ebuf + 40); |
||||
const std::int_fast32_t length = Decode32(ebuf + 44); |
||||
if (start < 0 || length < 0) break; |
||||
ebuf[40] = '\0'; // ensure zone name is NUL terminated
|
||||
if (strcmp(name.c_str(), ebuf) == 0) { |
||||
if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break; |
||||
return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource( |
||||
fp.release(), static_cast<std::size_t>(length))); |
||||
} |
||||
} |
||||
} |
||||
return nullptr; |
||||
} |
||||
#endif |
||||
|
||||
} // 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 = sys_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( |
||||
name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> { |
||||
if (auto zip = FileZoneInfoSource::Open(name)) return zip; |
||||
#if defined(__ANDROID__) |
||||
if (auto zip = AndroidZoneInfoSource::Open(name)) return zip; |
||||
#endif |
||||
return nullptr; |
||||
}); |
||||
return zip != nullptr && Load(name, 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, |
||||
year_t c4_shift) const { |
||||
assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_); |
||||
time_zone::civil_lookup cl = MakeTime(cs); |
||||
if (c4_shift > sys_seconds::max().count() / kSecsPer400Years) { |
||||
cl.pre = cl.trans = cl.post = time_point<sys_seconds>::max(); |
||||
} else { |
||||
const auto offset = sys_seconds(c4_shift * kSecsPer400Years); |
||||
const auto limit = time_point<sys_seconds>::max() - offset; |
||||
for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) { |
||||
if (*tp > limit) { |
||||
*tp = time_point<sys_seconds>::max(); |
||||
} else { |
||||
*tp += offset; |
||||
} |
||||
} |
||||
} |
||||
return cl; |
||||
} |
||||
|
||||
time_zone::absolute_lookup TimeZoneInfo::BreakTime( |
||||
const time_point<sys_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; |
||||
const year_t shift = diff / kSecsPer400Years + 1; |
||||
const auto d = sys_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_]); |
||||
if (cs < tt.civil_min) return MakeUnique(time_point<sys_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_) { |
||||
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]); |
||||
if (cs > tt.civil_max) return MakeUnique(time_point<sys_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)); |
||||
} |
||||
|
||||
std::string TimeZoneInfo::Description() const { |
||||
std::ostringstream oss; |
||||
// TODO: It would nice if the zoneinfo data included the zone name.
|
||||
// TODO: It would nice if the zoneinfo data included the tzdb version.
|
||||
oss << "#trans=" << transitions_.size(); |
||||
oss << " #types=" << transition_types_.size(); |
||||
oss << " spec='" << future_spec_ << "'"; |
||||
return oss.str(); |
||||
} |
||||
|
||||
bool TimeZoneInfo::NextTransition(time_point<sys_seconds>* tp) const { |
||||
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 recent zoneinfo data as it is
|
||||
// really a sentinel, not a transition. See tz/zic.c.
|
||||
++begin; |
||||
} |
||||
std::int_fast64_t unix_time = ToUnixSeconds(*tp); |
||||
const Transition target = { unix_time }; |
||||
const Transition* tr = std::upper_bound(begin, end, target, |
||||
Transition::ByUnixTime()); |
||||
if (tr != begin) { // skip no-op transitions
|
||||
for (; tr != end; ++tr) { |
||||
if (!EquivTransitions(tr[-1].type_index, tr[0].type_index)) break; |
||||
} |
||||
} |
||||
// When tr == end we return false, ignoring future_spec_.
|
||||
if (tr == end) return false; |
||||
*tp = FromUnixSeconds(tr->unix_time); |
||||
return true; |
||||
} |
||||
|
||||
bool TimeZoneInfo::PrevTransition(time_point<sys_seconds>* tp) const { |
||||
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 recent zoneinfo data as it is
|
||||
// really a sentinel, not a transition. See tz/zic.c.
|
||||
++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_.
|
||||
*tp = FromUnixSeconds((--end)->unix_time); |
||||
return true; |
||||
} |
||||
unix_time += 1; // ceils
|
||||
} |
||||
const Transition target = { unix_time }; |
||||
const Transition* tr = std::lower_bound(begin, end, target, |
||||
Transition::ByUnixTime()); |
||||
if (tr != begin) { // skip no-op transitions
|
||||
for (; tr - 1 != begin; --tr) { |
||||
if (!EquivTransitions(tr[-2].type_index, tr[-1].type_index)) break; |
||||
} |
||||
} |
||||
// When tr == end we return the "last" transition, ignoring future_spec_.
|
||||
if (tr == begin) return false; |
||||
*tp = FromUnixSeconds((--tr)->unix_time); |
||||
return true; |
||||
} |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
@ -0,0 +1,132 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_ |
||||
|
||||
#include <atomic> |
||||
#include <cstddef> |
||||
#include <cstdint> |
||||
#include <string> |
||||
#include <vector> |
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h" |
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h" |
||||
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h" |
||||
#include "time_zone_if.h" |
||||
#include "tzfile.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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<sys_seconds>& tp) const override; |
||||
time_zone::civil_lookup MakeTime( |
||||
const civil_second& cs) const override; |
||||
std::string Description() const override; |
||||
bool NextTransition(time_point<sys_seconds>* tp) const override; |
||||
bool PrevTransition(time_point<sys_seconds>* tp) 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 ttisgmtcnt; // standard/wall indicators (unused)
|
||||
|
||||
bool Build(const tzhead& tzh); |
||||
std::size_t DataLength(std::size_t time_len) const; |
||||
}; |
||||
|
||||
void CheckTransition(const std::string& name, const TransitionType& tt, |
||||
std::int_fast32_t offset, bool is_dst, |
||||
const std::string& abbr) const; |
||||
bool EquivTransitions(std::uint_fast8_t tt1_index, |
||||
std::uint_fast8_t tt2_index) const; |
||||
void ExtendTransitions(const std::string& name, const Header& hdr); |
||||
|
||||
bool ResetToBuiltinUTC(const sys_seconds& offset); |
||||
bool Load(const std::string& name, 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, |
||||
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::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
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
|
@ -0,0 +1,156 @@ |
||||
// 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
|
||||
//
|
||||
// http://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) |
||||
#define _CRT_SECURE_NO_WARNINGS 1 |
||||
#endif |
||||
|
||||
#include "time_zone_libc.h" |
||||
|
||||
#include <chrono> |
||||
#include <ctime> |
||||
#include <tuple> |
||||
#include <utility> |
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h" |
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
namespace cctz { |
||||
|
||||
namespace { |
||||
|
||||
// .first is seconds east of UTC; .second is the time-zone abbreviation.
|
||||
using OffsetAbbr = std::pair<int, const char*>; |
||||
|
||||
// Defines a function that can be called as follows:
|
||||
//
|
||||
// std::tm tm = ...;
|
||||
// OffsetAbbr off_abbr = get_offset_abbr(tm);
|
||||
//
|
||||
#if defined(_WIN32) || defined(_WIN64) |
||||
// Uses the globals: '_timezone', '_dstbias' and '_tzname'.
|
||||
OffsetAbbr get_offset_abbr(const std::tm& tm) { |
||||
const bool is_dst = tm.tm_isdst > 0; |
||||
const int off = _timezone + (is_dst ? _dstbias : 0); |
||||
const char* abbr = _tzname[is_dst]; |
||||
return {off, abbr}; |
||||
} |
||||
#elif defined(__sun) |
||||
// Uses the globals: 'timezone', 'altzone' and 'tzname'.
|
||||
OffsetAbbr get_offset_abbr(const std::tm& tm) { |
||||
const bool is_dst = tm.tm_isdst > 0; |
||||
const int off = is_dst ? altzone : timezone; |
||||
const char* abbr = tzname[is_dst]; |
||||
return {off, abbr}; |
||||
} |
||||
#elif defined(__native_client__) || defined(__myriad2__) || \ |
||||
defined(__EMSCRIPTEN__) |
||||
// Uses the globals: 'timezone' and 'tzname'.
|
||||
OffsetAbbr get_offset_abbr(const std::tm& tm) { |
||||
const bool is_dst = tm.tm_isdst > 0; |
||||
const int off = _timezone + (is_dst ? 60 * 60 : 0); |
||||
const char* abbr = tzname[is_dst]; |
||||
return {off, abbr}; |
||||
} |
||||
#else |
||||
//
|
||||
// Returns an OffsetAbbr using std::tm fields with various spellings.
|
||||
//
|
||||
#if !defined(tm_gmtoff) && !defined(tm_zone) |
||||
template <typename T> |
||||
OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::tm_gmtoff) = nullptr, |
||||
decltype(&T::tm_zone) = nullptr) { |
||||
return {tm.tm_gmtoff, tm.tm_zone}; |
||||
} |
||||
#endif // !defined(tm_gmtoff) && !defined(tm_zone)
|
||||
#if !defined(__tm_gmtoff) && !defined(__tm_zone) |
||||
template <typename T> |
||||
OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::__tm_gmtoff) = nullptr, |
||||
decltype(&T::__tm_zone) = nullptr) { |
||||
return {tm.__tm_gmtoff, tm.__tm_zone}; |
||||
} |
||||
#endif // !defined(__tm_gmtoff) && !defined(__tm_zone)
|
||||
#endif |
||||
|
||||
} // namespace
|
||||
|
||||
TimeZoneLibC::TimeZoneLibC(const std::string& name) |
||||
: local_(name == "localtime") {} |
||||
|
||||
time_zone::absolute_lookup TimeZoneLibC::BreakTime( |
||||
const time_point<sys_seconds>& tp) const { |
||||
time_zone::absolute_lookup al; |
||||
std::time_t t = ToUnixSeconds(tp); |
||||
std::tm tm; |
||||
if (local_) { |
||||
#if defined(_WIN32) || defined(_WIN64) |
||||
localtime_s(&tm, &t); |
||||
#else |
||||
localtime_r(&t, &tm); |
||||
#endif |
||||
std::tie(al.offset, al.abbr) = get_offset_abbr(tm); |
||||
} else { |
||||
#if defined(_WIN32) || defined(_WIN64) |
||||
gmtime_s(&tm, &t); |
||||
#else |
||||
gmtime_r(&t, &tm); |
||||
#endif |
||||
al.offset = 0; |
||||
al.abbr = "UTC"; |
||||
} |
||||
al.cs = civil_second(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, |
||||
tm.tm_hour, tm.tm_min, tm.tm_sec); |
||||
al.is_dst = tm.tm_isdst > 0; |
||||
return al; |
||||
} |
||||
|
||||
time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const { |
||||
time_zone::civil_lookup cl; |
||||
std::time_t t; |
||||
if (local_) { |
||||
// Does not handle SKIPPED/AMBIGUOUS or huge years.
|
||||
std::tm tm; |
||||
tm.tm_year = static_cast<int>(cs.year() - 1900); |
||||
tm.tm_mon = cs.month() - 1; |
||||
tm.tm_mday = cs.day(); |
||||
tm.tm_hour = cs.hour(); |
||||
tm.tm_min = cs.minute(); |
||||
tm.tm_sec = cs.second(); |
||||
tm.tm_isdst = -1; |
||||
t = std::mktime(&tm); |
||||
} else { |
||||
t = cs - civil_second(); |
||||
} |
||||
cl.kind = time_zone::civil_lookup::UNIQUE; |
||||
cl.pre = cl.trans = cl.post = FromUnixSeconds(t); |
||||
return cl; |
||||
} |
||||
|
||||
std::string TimeZoneLibC::Description() const { |
||||
return local_ ? "localtime" : "UTC"; |
||||
} |
||||
|
||||
bool TimeZoneLibC::NextTransition(time_point<sys_seconds>* tp) const { |
||||
return false; |
||||
} |
||||
|
||||
bool TimeZoneLibC::PrevTransition(time_point<sys_seconds>* tp) const { |
||||
return false; |
||||
} |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
@ -0,0 +1,50 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_ |
||||
|
||||
#include <string> |
||||
|
||||
#include "time_zone_if.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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<sys_seconds>& tp) const override; |
||||
time_zone::civil_lookup MakeTime( |
||||
const civil_second& cs) const override; |
||||
std::string Description() const override; |
||||
bool NextTransition(time_point<sys_seconds>* tp) const override; |
||||
bool PrevTransition(time_point<sys_seconds>* tp) const override; |
||||
|
||||
private: |
||||
const bool local_; // localtime or UTC
|
||||
}; |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
|
@ -0,0 +1,142 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 "absl/time/internal/cctz/include/cctz/time_zone.h" |
||||
|
||||
#if defined(__ANDROID__) |
||||
#include <sys/system_properties.h> |
||||
#if __ANDROID_API__ >= 21 |
||||
#include <dlfcn.h> |
||||
#endif |
||||
#endif |
||||
#include <cstdlib> |
||||
#include <cstring> |
||||
#include <string> |
||||
|
||||
#include "time_zone_fixed.h" |
||||
#include "time_zone_impl.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
namespace cctz { |
||||
|
||||
#if defined(__ANDROID__) && __ANDROID_API__ >= 21 |
||||
namespace { |
||||
// Android 'L' removes __system_property_get() from the NDK, however
|
||||
// it is still a hidden symbol in libc so we use dlsym() to access it.
|
||||
// See Chromium's base/sys_info_android.cc for a similar example.
|
||||
|
||||
using property_get_func = int (*)(const char*, char*); |
||||
|
||||
property_get_func LoadSystemPropertyGet() { |
||||
int flag = RTLD_LAZY | RTLD_GLOBAL; |
||||
#if defined(RTLD_NOLOAD) |
||||
flag |= RTLD_NOLOAD; // libc.so should already be resident
|
||||
#endif |
||||
if (void* handle = dlopen("libc.so", flag)) { |
||||
void* sym = dlsym(handle, "__system_property_get"); |
||||
dlclose(handle); |
||||
return reinterpret_cast<property_get_func>(sym); |
||||
} |
||||
return nullptr; |
||||
} |
||||
|
||||
int __system_property_get(const char* name, char* value) { |
||||
static property_get_func system_property_get = LoadSystemPropertyGet(); |
||||
return system_property_get ? system_property_get(name, value) : -1; |
||||
} |
||||
|
||||
} // namespace
|
||||
#endif |
||||
|
||||
std::string time_zone::name() const { |
||||
return time_zone::Impl::get(*this).name(); |
||||
} |
||||
|
||||
time_zone::absolute_lookup time_zone::lookup( |
||||
const time_point<sys_seconds>& tp) const { |
||||
return time_zone::Impl::get(*this).BreakTime(tp); |
||||
} |
||||
|
||||
time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const { |
||||
return time_zone::Impl::get(*this).MakeTime(cs); |
||||
} |
||||
|
||||
bool operator==(time_zone lhs, time_zone rhs) { |
||||
return &time_zone::Impl::get(lhs) == &time_zone::Impl::get(rhs); |
||||
} |
||||
|
||||
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 sys_seconds& offset) { |
||||
time_zone tz; |
||||
load_time_zone(FixedOffsetToName(offset), &tz); |
||||
return tz; |
||||
} |
||||
|
||||
time_zone local_time_zone() { |
||||
const char* zone = ":localtime"; |
||||
|
||||
// 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 defined(__ANDROID__) |
||||
char sysprop[PROP_VALUE_MAX]; |
||||
if (tz_env == nullptr) |
||||
if (__system_property_get("persist.sys.timezone", sysprop) > 0) |
||||
tz_env = sysprop; |
||||
#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.
|
||||
return tz; |
||||
} |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,155 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 absl { |
||||
namespace time_internal { |
||||
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<int_fast8_t>(month); |
||||
res->date.m.week = static_cast<int_fast8_t>(week); |
||||
res->date.m.weekday = static_cast<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; |
||||
res->date.j.day = static_cast<int_fast16_t>(day); |
||||
} |
||||
} else { |
||||
int day = 0; |
||||
if ((p = ParseInt(p, 0, 365, &day)) != nullptr) { |
||||
res->date.fmt = PosixTransition::N; |
||||
res->date.j.day = static_cast<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
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
@ -0,0 +1,118 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_ |
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_ |
||||
|
||||
#include <cstdint> |
||||
#include <string> |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
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 { |
||||
DateFormat fmt; |
||||
union { |
||||
struct { |
||||
std::int_fast16_t day; // day of non-leap year [1:365]
|
||||
} j; |
||||
struct { |
||||
std::int_fast16_t day; // day of year [0:365]
|
||||
} n; |
||||
struct { |
||||
std::int_fast8_t month; // month of year [1:12]
|
||||
std::int_fast8_t week; // week of month [1:5] (5==last)
|
||||
std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat
|
||||
} m; |
||||
}; |
||||
} date; |
||||
struct { |
||||
std::int_fast32_t offset; // seconds before/after 00:00:00
|
||||
} time; |
||||
}; |
||||
|
||||
// The entirety of a POSIX-std::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
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
|
@ -0,0 +1,117 @@ |
||||
#ifndef TZFILE_H |
||||
|
||||
#define TZFILE_H |
||||
|
||||
/*
|
||||
** This file is in the public domain, so clarified as of |
||||
** 1996-06-05 by Arthur David Olson. |
||||
*/ |
||||
|
||||
/*
|
||||
** This header is for use ONLY with the time conversion code. |
||||
** There is no guarantee that it will remain unchanged, |
||||
** or that it will remain at all. |
||||
** Do NOT copy it to any system include directory. |
||||
** Thank you! |
||||
*/ |
||||
|
||||
/*
|
||||
** Information about time zone files. |
||||
*/ |
||||
|
||||
#ifndef TZDIR |
||||
#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */ |
||||
#endif /* !defined TZDIR */ |
||||
|
||||
#ifndef TZDEFAULT |
||||
#define TZDEFAULT "/etc/localtime" |
||||
#endif /* !defined TZDEFAULT */ |
||||
|
||||
#ifndef TZDEFRULES |
||||
#define TZDEFRULES "posixrules" |
||||
#endif /* !defined TZDEFRULES */ |
||||
|
||||
/*
|
||||
** Each file begins with. . . |
||||
*/ |
||||
|
||||
#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_ttisgmtcnt[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 */ |
||||
}; |
||||
|
||||
/*
|
||||
** . . .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, |
||||
** transition time is wall clock time |
||||
** if absent, transition times are |
||||
** assumed to be wall clock time |
||||
** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition |
||||
** time is UT, if 0, |
||||
** transition time is local time |
||||
** if absent, transition times are |
||||
** assumed to be local time |
||||
*/ |
||||
|
||||
/*
|
||||
** If tzh_version is '2' or greater, the above is followed by a second instance |
||||
** of tzhead and a second instance of the data in which each coded transition |
||||
** time uses 8 rather than 4 chars, |
||||
** then a POSIX-TZ-environment-variable-style std::string for use in handling |
||||
** instants after the last transition time stored in the file |
||||
** (with nothing between the newlines if there is no POSIX representation for |
||||
** such instants). |
||||
** |
||||
** If tz_version is '3' or greater, the above is extended as follows. |
||||
** First, the POSIX TZ std::string's hour offset may range from -167 |
||||
** through 167 as compared to the POSIX-required 0 through 24. |
||||
** Second, its DST start time may be January 1 at 00:00 and its stop |
||||
** time December 31 at 24:00 plus the difference between DST and |
||||
** standard time, indicating DST all year. |
||||
*/ |
||||
|
||||
/*
|
||||
** In the current implementation, "tzset()" refuses to deal with files that |
||||
** exceed any of the limits below. |
||||
*/ |
||||
|
||||
#ifndef TZ_MAX_TIMES |
||||
#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 */ |
||||
#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) */ |
||||
#endif /* !defined TZ_MAX_CHARS */ |
||||
|
||||
#ifndef TZ_MAX_LEAPS |
||||
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ |
||||
#endif /* !defined TZ_MAX_LEAPS */ |
||||
|
||||
#endif /* !defined TZFILE_H */ |
@ -0,0 +1,70 @@ |
||||
// 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
|
||||
//
|
||||
// http://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 "absl/time/internal/cctz/include/cctz/zone_info_source.h" |
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
namespace cctz { |
||||
|
||||
// Defined out-of-line to avoid emitting a weak vtable in all TUs.
|
||||
ZoneInfoSource::~ZoneInfoSource() {} |
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
||||
|
||||
namespace absl { |
||||
namespace time_internal { |
||||
namespace cctz_extension { |
||||
|
||||
namespace { |
||||
|
||||
// A default for cctz_extension::zone_info_source_factory, which simply
|
||||
// defers to the fallback factory.
|
||||
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory( |
||||
const std::string& name, |
||||
const std::function<std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>( |
||||
const std::string& name)>& fallback_factory) { |
||||
return fallback_factory(name); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
// A "weak" definition for cctz_extension::zone_info_source_factory.
|
||||
// The user may override this with their own "strong" definition (see
|
||||
// zone_info_source.h).
|
||||
#if defined(_MSC_VER) |
||||
extern ZoneInfoSourceFactory zone_info_source_factory; |
||||
extern ZoneInfoSourceFactory default_factory; |
||||
ZoneInfoSourceFactory default_factory = DefaultFactory; |
||||
#if defined(_M_IX86) |
||||
#pragma comment( \ |
||||
linker, \
|
||||
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZA=?default_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZA") |
||||
#elif defined(_M_IA_64) || defined(_M_AMD64) |
||||
#pragma comment( \ |
||||
linker, \
|
||||
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZEA=?default_factory@cctz_extension@time_internal@absl@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@5@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@5@@ZEA") |
||||
#else |
||||
#error Unsupported MSVC platform |
||||
#endif |
||||
#else |
||||
ZoneInfoSourceFactory zone_info_source_factory |
||||
__attribute__((weak)) = DefaultFactory; |
||||
#endif // _MSC_VER
|
||||
|
||||
} // namespace cctz_extension
|
||||
} // namespace time_internal
|
||||
} // namespace absl
|
@ -0,0 +1,37 @@ |
||||
testdata/zoneinfo contains time-zone data files that may be used with CCTZ. |
||||
Install them in a location referenced by the ${TZDIR} environment variable. |
||||
Symbolic and hard links have been eliminated for portability. |
||||
|
||||
On Linux systems the distribution's versions of these files can probably |
||||
already be found in the default ${TZDIR} location, /usr/share/zoneinfo. |
||||
|
||||
New versions can be generated using the following shell script. |
||||
|
||||
#!/bin/sh - |
||||
set -e |
||||
DESTDIR=$(mktemp -d) |
||||
trap "rm -fr ${DESTDIR}" 0 2 15 |
||||
( |
||||
cd ${DESTDIR} |
||||
git clone https://github.com/eggert/tz.git |
||||
make --directory=tz \ |
||||
install DESTDIR=${DESTDIR} \ |
||||
DATAFORM=vanguard \ |
||||
TZDIR=/zoneinfo \ |
||||
REDO=posix_only \ |
||||
LOCALTIME=Factory \ |
||||
TZDATA_TEXT= \ |
||||
ZONETABLES=zone1970.tab |
||||
tar --create --dereference --hard-dereference --file tzfile.tar \ |
||||
--directory=tz tzfile.h |
||||
tar --create --dereference --hard-dereference --file zoneinfo.tar \ |
||||
--exclude=zoneinfo/posixrules zoneinfo \ |
||||
--directory=tz version |
||||
) |
||||
tar --extract --directory src --file ${DESTDIR}/tzfile.tar |
||||
tar --extract --directory testdata --file ${DESTDIR}/zoneinfo.tar |
||||
exit 0 |
||||
|
||||
To run the CCTZ tests using the testdata/zoneinfo files, execute: |
||||
|
||||
bazel test --test_env=TZDIR=${PWD}/testdata/zoneinfo ... |
@ -0,0 +1 @@ |
||||
2018d-2-g8d1dac0 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue