diff --git a/velox/CMakeLists.txt b/velox/CMakeLists.txt index b51e2a77073c..f13306770e10 100644 --- a/velox/CMakeLists.txt +++ b/velox/CMakeLists.txt @@ -23,6 +23,7 @@ add_subdirectory(vector) add_subdirectory(row) add_subdirectory(flag_definitions) add_subdirectory(external/date) +add_subdirectory(external/tzdb) add_subdirectory(external/md5) add_subdirectory(external/hdfs) # diff --git a/velox/docs/develop/timestamp.rst b/velox/docs/develop/timestamp.rst index b29577dea25e..7d745cc21a96 100644 --- a/velox/docs/develop/timestamp.rst +++ b/velox/docs/develop/timestamp.rst @@ -73,9 +73,9 @@ used to efficiently represent timezones, preventing the use of inefficient timezone string names like ``America/Los_Angeles``. Considering there are about 2k valid timezone definitions, 12 bits are enough to represent timezone IDs.  -Timezone IDs in Velox are based on the id map used by Presto and are -`available here `_. -They are automatically generated using `this script `_. +Timezone IDs in Velox are based on the id map used by Presto and are +`available here `_. +They are automatically generated using `this script `_. While timezone IDs are an implementation detail and ideally should not leak outside of Velox execution, they are exposed if data containing TimestampWithTimezones are serialized, for example. @@ -98,7 +98,7 @@ ID). However, unpacking/converting a TimestampWithTimezone into an absolute time definition requires a -`timezone conversion `_. +`timezone conversion `_. Conversions Across Timezones ---------------------------- @@ -119,7 +119,7 @@ for Linux.  In Velox, Timezone conversions are done using std::chrono. Starting in C++20, std::chrono `supports conversion of timestamp across timezones `_. To support older versions of the C++ standard, in Velox we vendor an -implementation of this API at `velox/external/date/ `_. +implementation of this API at `velox/external/tzdb/ `_. This class handles timezone conversions by leveraging APIs provided by the operating system, based on the tzdata database installed locally. If systems happen to have inconsistent or older versions of the tzdata database, Velox’s @@ -153,15 +153,15 @@ on the string on not: :: SELECT typeof(TIMESTAMP '1970-01-01 00:00:00'); -- timestamp - SELECT typeof(TIMESTAMP '1970-01-01 00:00:00 UTC'); -- timestamp with time zone + SELECT typeof(TIMESTAMP '1970-01-01 00:00:00 UTC'); -- timestamp with time zone Converting a TimestampWithTimezone into a Timestamp works by dropping the timezone information and returning only the timestamp portion: :: - SELECT cast(TIMESTAMP '1970-01-01 00:00:00 UTC' as timestamp); -- 1970-01-01 00:00:00.000 - SELECT cast(TIMESTAMP '1970-01-01 00:00:00 America/New_York' as timestamp); -- 1970-01-01 00:00:00.000 + SELECT cast(TIMESTAMP '1970-01-01 00:00:00 UTC' as timestamp); -- 1970-01-01 00:00:00.000 + SELECT cast(TIMESTAMP '1970-01-01 00:00:00 America/New_York' as timestamp); -- 1970-01-01 00:00:00.000 To convert a Timestamp into a TimestampWithTimezone, one needs to specify a timezone. In Presto, the session timezone is used by default: @@ -169,7 +169,7 @@ timezone. In Presto, the session timezone is used by default: :: SELECT current_timezone(); -- America/Los_Angeles - SELECT cast(TIMESTAMP '1970-01-01 00:00:00' as timestamp with time zone); -- 1970-01-01 00:00:00.000 America/Los_Angeles + SELECT cast(TIMESTAMP '1970-01-01 00:00:00' as timestamp with time zone); -- 1970-01-01 00:00:00.000 America/Los_Angeles Conversion across TimestampWithTimezone can be done using the AT TIME ZONE construct.  @@ -180,15 +180,15 @@ the clock/calendar read at the target timezone (Los Angeles)? :: - SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' AT TIME ZONE 'America/Los_Angeles'; -- 1969-12-31 16:00:00.000 America/Los_Angeles - SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' AT TIME ZONE 'UTC'; -- 1970-01-01 00:00:00.000 UTC + SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' AT TIME ZONE 'America/Los_Angeles'; -- 1969-12-31 16:00:00.000 America/Los_Angeles + SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' AT TIME ZONE 'UTC'; -- 1970-01-01 00:00:00.000 UTC Strings can be converted into Timestamp and TimestampWithTimezone: :: - SELECT cast('1970-01-01 00:00:00' as timestamp); -- 1970-01-01 00:00:00.000 - SELECT cast('1970-01-01 00:00:00 America/Los_Angeles' as timestamp with time zone); -- 1970-01-01 00:00:00.000 America/Los_Angeles + SELECT cast('1970-01-01 00:00:00' as timestamp); -- 1970-01-01 00:00:00.000 + SELECT cast('1970-01-01 00:00:00 America/Los_Angeles' as timestamp with time zone); -- 1970-01-01 00:00:00.000 America/Los_Angeles One can also convert a TimestampWithTimezone into a unix epoch/time. The semantic of this operation is: at the absolute point in time described by the @@ -197,8 +197,8 @@ that unix epoch is the number of seconds since ``1970-01-01 00:00:00`` in UTC: :: - SELECT to_unixtime(TIMESTAMP '1970-01-01 00:00:00 UTC'); -- 0.0 - SELECT to_unixtime(TIMESTAMP '1970-01-01 00:00:00 America/Los_Angeles'); -- 28800.0 + SELECT to_unixtime(TIMESTAMP '1970-01-01 00:00:00 UTC'); -- 0.0 + SELECT to_unixtime(TIMESTAMP '1970-01-01 00:00:00 America/Los_Angeles'); -- 28800.0 The opposite conversion can be achieved using ``from_unixtime()``. The function may take an optional second parameter to specify the timezone, having the same @@ -206,7 +206,7 @@ semantic as AT TIME ZONE described above: :: - SELECT from_unixtime(0); -- 1970-01-01 00:00:00.000 + SELECT from_unixtime(0); -- 1970-01-01 00:00:00.000 SELECT from_unixtime(0, 'UTC'); -- 1970-01-01 00:00:00.000 UTC  SELECT from_unixtime(0, 'America/Los_Angeles'); -- 1969-12-31 16:00:00.000 America/Los_Angeles @@ -225,8 +225,8 @@ timestamps have a different semantic: :: SET SESSION legacy_timestamp = true; - SELECT cast(TIMESTAMP '1970-01-01 00:00:00 UTC' as timestamp); -- 1969-12-31 16:00:00.000 - SELECT cast('1970-01-01 00:00:00 UTC' as timestamp); -- 1969-12-31 16:00:00.000 + SELECT cast(TIMESTAMP '1970-01-01 00:00:00 UTC' as timestamp); -- 1969-12-31 16:00:00.000 + SELECT cast('1970-01-01 00:00:00 UTC' as timestamp); -- 1969-12-31 16:00:00.000 To support the two timestamp semantics, the ``core::QueryConfig::kAdjustTimestampToTimezone`` query flag was added to Velox. diff --git a/velox/dwio/parquet/tests/reader/ParquetTableScanTest.cpp b/velox/dwio/parquet/tests/reader/ParquetTableScanTest.cpp index 62edc926b665..43bc782638fe 100644 --- a/velox/dwio/parquet/tests/reader/ParquetTableScanTest.cpp +++ b/velox/dwio/parquet/tests/reader/ParquetTableScanTest.cpp @@ -25,7 +25,6 @@ #include "velox/exec/tests/utils/HiveConnectorTestBase.h" // @manual #include "velox/exec/tests/utils/PlanBuilder.h" #include "velox/exec/tests/utils/TempDirectoryPath.h" -#include "velox/external/date/tz.h" #include "velox/type/tests/SubfieldFiltersBuilder.h" #include "velox/type/tz/TimeZoneMap.h" diff --git a/velox/expression/CastExpr-inl.h b/velox/expression/CastExpr-inl.h index ff6d6bd4fed2..2e36bc2ac1a0 100644 --- a/velox/expression/CastExpr-inl.h +++ b/velox/expression/CastExpr-inl.h @@ -19,7 +19,6 @@ #include "velox/common/base/Exceptions.h" #include "velox/core/CoreTypeSystem.h" #include "velox/expression/StringWriter.h" -#include "velox/external/date/tz.h" #include "velox/type/Type.h" #include "velox/vector/SelectivityVector.h" diff --git a/velox/expression/tests/CastExprTest.cpp b/velox/expression/tests/CastExprTest.cpp index 198c8b34934d..eef87abdf28b 100644 --- a/velox/expression/tests/CastExprTest.cpp +++ b/velox/expression/tests/CastExprTest.cpp @@ -623,11 +623,17 @@ TEST_F(CastExprTest, stringToTimestamp) { "1970-01-01 00:00 America/Sao_Paulo", "2000-01-01 12:21:56Z", "2000-01-01 12:21:56+01:01:01", + "2045-12-31 18:00:00", // Test going back and forth across DST boundaries. "2024-03-10 09:59:59 -00:00:02", "2024-03-10 10:00:01 +00:00:02", "2024-11-03 08:59:59 -00:00:02", "2024-11-03 09:00:01 +00:00:02", + // Test going back and forth across DST boundaries in the distant future. + "2100-03-14 09:59:59 -00:00:02", + "2100-03-14 10:00:01 +00:00:02", + "2100-11-07 09:59:59 -00:00:02", + "2100-11-07 10:00:01 +00:00:02", }; expected = { Timestamp(28800, 0), @@ -636,10 +642,15 @@ TEST_F(CastExprTest, stringToTimestamp) { Timestamp(10800, 0), Timestamp(946729316, 0), Timestamp(946725655, 0), + Timestamp(2398384800, 0), Timestamp(1710064801, 0), Timestamp(1710064799, 0), Timestamp(1730624401, 0), Timestamp(1730624399, 0), + Timestamp(4108701601, 0), + Timestamp(4108701599, 0), + Timestamp(4129264801, 0), + Timestamp(4129264799, 0), }; testCast("timestamp", input, expected); @@ -656,14 +667,6 @@ TEST_F(CastExprTest, stringToTimestamp) { (evaluateOnce( "try_cast(c0 as timestamp)", "201915-04-23 11:46:00.000")), "Timepoint is outside of supported year range"); - VELOX_ASSERT_THROW( - (evaluateOnce( - "cast(c0 as timestamp)", "2045-12-31 18:00:00")), - "Unable to convert timezone 'America/Los_Angeles' past 2037-11-01 09:00:00"); - VELOX_ASSERT_THROW( - (evaluateOnce( - "try_cast(c0 as timestamp)", "2045-12-31 18:00:00")), - "Unable to convert timezone 'America/Los_Angeles' past 2037-11-01 09:00:00"); // Only one white space is allowed before the offset string. VELOX_ASSERT_THROW( (evaluateOnce( @@ -777,6 +780,7 @@ TEST_F(CastExprTest, timestampToString) { Timestamp(0, 0), Timestamp(946729316, 123), Timestamp(-50049331622, 0), + Timestamp(253405036800, 0), Timestamp(-62480038022, 0), std::nullopt, }, @@ -784,27 +788,10 @@ TEST_F(CastExprTest, timestampToString) { "1969-12-31 16:00:00.000", "2000-01-01 04:21:56.000", "0384-01-01 00:00:00.000", + "10000-02-01 08:00:00.000", "-0010-02-01 02:00:00.000", std::nullopt, }); - - // Ensure external/date throws since it doesn't know how to convert large - // timestamps. - auto mustThrow = [&]() { - return testCast( - "string", {Timestamp(253405036800, 0)}, {"10000-02-01 08:00:00.000"}); - }; - VELOX_ASSERT_THROW( - mustThrow(), "Unable to convert timezone 'America/Los_Angeles' past"); - - // try_cast should also throw since it's runtime error. - auto tryCastMustThrow = [&]() { - return testTryCast( - "string", {Timestamp(253405036800, 0)}, {"10000-02-01 08:00:00.000"}); - }; - VELOX_ASSERT_THROW( - tryCastMustThrow(), - "Unable to convert timezone 'America/Los_Angeles' past"); } TEST_F(CastExprTest, dateToTimestamp) { @@ -831,6 +818,7 @@ TEST_F(CastExprTest, timestampToDate) { Timestamp(0, 0), Timestamp(946684800, 0), Timestamp(1257724800, 0), + Timestamp(2534050368, 0), std::nullopt, }; @@ -841,6 +829,7 @@ TEST_F(CastExprTest, timestampToDate) { 0, 10957, 14557, + 29329, std::nullopt, }, TIMESTAMP(), @@ -854,28 +843,11 @@ TEST_F(CastExprTest, timestampToDate) { -1, 10956, 14556, + 29328, std::nullopt, }, TIMESTAMP(), DATE()); - - // Ensure external/date throws since it doesn't know how to convert large - // timestamps. - auto mustThrow = [&]() { - return testCast( - "date", {Timestamp(253405036800, 0)}, {0}); - }; - VELOX_ASSERT_THROW( - mustThrow(), "Unable to convert timezone 'America/Los_Angeles' past"); - - // try_cast should also throw since it's runtime error. - auto tryCastMustThrow = [&]() { - return testTryCast( - "date", {Timestamp(253405036800, 0)}, {0}); - }; - VELOX_ASSERT_THROW( - tryCastMustThrow(), - "Unable to convert timezone 'America/Los_Angeles' past"); } TEST_F(CastExprTest, timestampInvalid) { diff --git a/velox/external/date/CMakeLists.txt b/velox/external/date/CMakeLists.txt index 1cc626b2b60d..b1e2d29faa4e 100644 --- a/velox/external/date/CMakeLists.txt +++ b/velox/external/date/CMakeLists.txt @@ -10,7 +10,5 @@ # See the License for the specific language governing permissions and # limitations under the License. - -velox_add_library(velox_external_date tz.cpp) -velox_include_directories(velox_external_date SYSTEM PUBLIC velox/external) -velox_compile_definitions(velox_external_date PRIVATE USE_OS_TZDB) +add_library(velox_external_date INTERFACE) +velox_include_directories(velox_external_date INTERFACE velox/external/date) diff --git a/velox/external/date/ios.h b/velox/external/date/ios.h deleted file mode 100644 index da1af46f6673..000000000000 --- a/velox/external/date/ios.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// ios.h -// DateTimeLib -// -// The MIT License (MIT) -// -// Copyright (c) 2016 Alexander Kormanovsky -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef velox_ios_hpp -#define velox_ios_hpp - -#if __APPLE__ -# include -# if TARGET_OS_IPHONE -# include - - namespace facebook - { - namespace velox - { - namespace date - { - namespace iOSUtils - { - - std::string get_tzdata_path(); - std::string get_current_timezone(); - - } // namespace iOSUtils - } // namespace date - } // namespace velox - } // namespace facebook - -# endif // TARGET_OS_IPHONE -#else // !__APPLE__ -# define TARGET_OS_IPHONE 0 -#endif // !__APPLE__ -#endif // velox_ios_hpp diff --git a/velox/external/date/patches/0002-add_namespacing-pr-8332.patch b/velox/external/date/patches/0002-add_namespacing-pr-8332.patch index d42281d3f6bd..453341c8e624 100644 --- a/velox/external/date/patches/0002-add_namespacing-pr-8332.patch +++ b/velox/external/date/patches/0002-add_namespacing-pr-8332.patch @@ -23,106 +23,6 @@ diff --git a/fbcode/velox/external/date/date.h b/fbcode/velox/external/date/date #ifdef _MSC_VER # pragma warning(pop) -diff --git a/fbcode/velox/external/date/ios.h b/fbcode/velox/external/date/ios.h ---- a/fbcode/velox/external/date/ios.h -+++ b/fbcode/velox/external/date/ios.h -@@ -32,16 +32,22 @@ - # if TARGET_OS_IPHONE - # include - -+ namespace facebook -+ { -+ namespace velox -+ { - namespace date - { - namespace iOSUtils - { -- -+ - std::string get_tzdata_path(); - std::string get_current_timezone(); -- -+ - } // namespace iOSUtils - } // namespace date -+ } // namespace velox -+ } // namespace facebook - - # endif // TARGET_OS_IPHONE - #else // !__APPLE__ -diff --git a/fbcode/velox/external/date/tz.cpp b/fbcode/velox/external/date/tz.cpp ---- a/fbcode/velox/external/date/tz.cpp -+++ b/fbcode/velox/external/date/tz.cpp -@@ -268,6 +268,10 @@ - - #endif // !USE_OS_TZDB - -+namespace facebook -+{ -+namespace velox -+{ - namespace date - { - // +---------------------+ -@@ -3860,6 +3864,8 @@ - } - - } // namespace date -+} // namespace velox -+} // namespace facebook - - #if defined(__GNUC__) && __GNUC__ < 5 - # pragma GCC diagnostic pop -diff --git a/fbcode/velox/external/date/tz.h b/fbcode/velox/external/date/tz.h ---- a/fbcode/velox/external/date/tz.h -+++ b/fbcode/velox/external/date/tz.h -@@ -143,6 +143,10 @@ - # endif - #endif - -+namespace facebook -+{ -+namespace velox -+{ - namespace date - { - -@@ -2790,5 +2794,7 @@ - #endif // !MISSING_LEAP_SECONDS - - } // namespace date -+} // namespace velox -+} // namespace facebook - - #endif // TZ_H -diff --git a/fbcode/velox/external/date/tz_private.h b/fbcode/velox/external/date/tz_private.h ---- a/fbcode/velox/external/date/tz_private.h -+++ b/fbcode/velox/external/date/tz_private.h -@@ -34,6 +34,12 @@ - #include - #endif - -+namespace facebook -+{ -+ -+namespace velox -+{ -+ - namespace date - { - -@@ -309,6 +315,10 @@ - - } // namespace date - -+} // namespace velox -+ -+} // namespace facebook -+ - #if defined(_MSC_VER) && (_MSC_VER < 1900) - #include "tz.h" - #endif diff --git a/fbcode/velox/external/date/iso_week.h b/fbcode/velox/external/date/iso_week.h --- a/fbcode/velox/external/date/iso_week.h +++ b/fbcode/velox/external/date/iso_week.h diff --git a/velox/external/date/patches/0003-throw-on-unsupported-tz-conversion-pr-10097.patch b/velox/external/date/patches/0003-throw-on-unsupported-tz-conversion-pr-10097.patch deleted file mode 100644 index 716c99d87177..000000000000 --- a/velox/external/date/patches/0003-throw-on-unsupported-tz-conversion-pr-10097.patch +++ /dev/null @@ -1,72 +0,0 @@ -diff --git a/fbcode/velox/external/date/tz.cpp b/fbcode/velox/external/date/tz.cpp ---- a/fbcode/velox/external/date/tz.cpp -+++ b/fbcode/velox/external/date/tz.cpp -@@ -2033,6 +2033,28 @@ - } - for (auto j = 0u; i < transitions_.size(); ++i, ++j) - transitions_[i].info = ttinfos_.data() + indices[j]; -+ -+ // Use getline to discard the first newline, then read the extended -+ // information -+ std::string extended_info; -+ std::getline(inf, extended_info); -+ std::getline(inf, extended_info); -+ auto comma1 = extended_info.find(','); -+ -+ // Extended info can either be just the name/offset, or name/offset and -+ // rules. If there are rules, they come in pairs (when the rule starts and -+ // when the rule ends), and are separated by commas (','). -+ if (comma1 != std::string::npos) { -+ auto comma2 = extended_info.find(',', comma1 + 1); -+ assert(comma2 != std::string::npos); -+ -+ extended_info_.extended_name_ = extended_info.substr(0, comma1); -+ extended_info_.rule_start_ = -+ extended_info.substr(comma1 + 1, comma2 - comma1 - 1); -+ extended_info_.rule_end_ = extended_info.substr(comma2 + 1); -+ } else { -+ extended_info_.extended_name_ = std::move(extended_info); -+ } - } - - void -@@ -2125,6 +2147,16 @@ - using namespace std::chrono; - assert(!transitions_.empty()); - assert(i != transitions_.begin()); -+ -+ // Conversions past OS_TZDB items are not supported if the timezone has -+ // repetition rules. Fail instead of returning wrong results -+ if (i == transitions_.end() && extended_info_.has_rules()) { -+ std::stringstream ss; -+ ss << "Unable to convert timezone '" << name_ << "' past " -+ << transitions_.back().timepoint; -+ throw std::invalid_argument(ss.str()); -+ } -+ - sys_info r; - r.begin = i[-1].timepoint; - r.end = i != transitions_.end() ? i->timepoint : -diff --git a/fbcode/velox/external/date/tz.h b/fbcode/velox/external/date/tz.h ---- a/fbcode/velox/external/date/tz.h -+++ b/fbcode/velox/external/date/tz.h -@@ -790,6 +790,19 @@ - #if USE_OS_TZDB - std::vector transitions_; - std::vector ttinfos_; -+ -+ // Stores extended OS_TZDB timezone information, in addition to possible -+ // repetition rules (although these are not supported yet) -+ struct { -+ std::string extended_name_; -+ std::string rule_start_; -+ std::string rule_end_; -+ -+ bool has_rules() const { -+ return !rule_start_.empty(); -+ } -+ } extended_info_; -+ - #else // !USE_OS_TZDB - std::vector zonelets_; - #endif // !USE_OS_TZDB diff --git a/velox/external/date/patches/0003-update-header-guards-pr-10269.patch b/velox/external/date/patches/0003-update-header-guards-pr-10269.patch new file mode 100644 index 000000000000..c76a7c0219f6 --- /dev/null +++ b/velox/external/date/patches/0003-update-header-guards-pr-10269.patch @@ -0,0 +1,18 @@ +diff --git a/velox/external/date/date.h b/velox/external/date/date.h +index ccbd4587f..ac6d636dd 100644 +--- a/velox/external/date/date.h ++++ b/velox/external/date/date.h +@@ -1,5 +1,5 @@ +-#ifndef DATE_H +-#define DATE_H ++#ifndef VELOX_DATE_H ++#define VELOX_DATE_H + + // The MIT License (MIT) + // +@@ -7949,4 +7949,4 @@ operator<<(std::basic_ostream& os, + # pragma GCC diagnostic pop + #endif + +-#endif // DATE_H ++#endif // VELOX_DATE_H diff --git a/velox/external/date/patches/0004-update-header-guards-pr-10269.patch b/velox/external/date/patches/0004-update-header-guards-pr-10269.patch deleted file mode 100644 index ab45671899af..000000000000 --- a/velox/external/date/patches/0004-update-header-guards-pr-10269.patch +++ /dev/null @@ -1,75 +0,0 @@ -diff --git a/velox/external/date/date.h b/velox/external/date/date.h -index ccbd4587f..ac6d636dd 100644 ---- a/velox/external/date/date.h -+++ b/velox/external/date/date.h -@@ -1,5 +1,5 @@ --#ifndef DATE_H --#define DATE_H -+#ifndef VELOX_DATE_H -+#define VELOX_DATE_H - - // The MIT License (MIT) - // -@@ -7949,4 +7949,4 @@ operator<<(std::basic_ostream& os, - # pragma GCC diagnostic pop - #endif - --#endif // DATE_H -+#endif // VELOX_DATE_H -diff --git a/velox/external/date/ios.h b/velox/external/date/ios.h -index 467798983..da1af46f6 100644 ---- a/velox/external/date/ios.h -+++ b/velox/external/date/ios.h -@@ -24,8 +24,8 @@ - // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - // SOFTWARE. - --#ifndef ios_hpp --#define ios_hpp -+#ifndef velox_ios_hpp -+#define velox_ios_hpp - - #if __APPLE__ - # include -@@ -53,4 +53,4 @@ - #else // !__APPLE__ - # define TARGET_OS_IPHONE 0 - #endif // !__APPLE__ --#endif // ios_hpp -+#endif // velox_ios_hpp -diff --git a/velox/external/date/tz.h b/velox/external/date/tz.h -index 9512900b7..a70bda4ad 100644 ---- a/velox/external/date/tz.h -+++ b/velox/external/date/tz.h -@@ -1,5 +1,5 @@ --#ifndef TZ_H --#define TZ_H -+#ifndef VELOX_TZ_H -+#define VELOX_TZ_H - - // The MIT License (MIT) - // -@@ -2810,4 +2810,4 @@ to_gps_time(const tai_time& t) - } // namespace velox - } // namespace facebook - --#endif // TZ_H -+#endif // VELOX_TZ_H -diff --git a/velox/external/date/tz_private.h b/velox/external/date/tz_private.h -index 3a985d955..b85a617f2 100644 ---- a/velox/external/date/tz_private.h -+++ b/velox/external/date/tz_private.h -@@ -1,5 +1,5 @@ --#ifndef TZ_PRIVATE_H --#define TZ_PRIVATE_H -+#ifndef VELOX_TZ_PRIVATE_H -+#define VELOX_TZ_PRIVATE_H - - // The MIT License (MIT) - // -@@ -323,4 +323,4 @@ struct transition - #include "tz.h" - #endif - --#endif // TZ_PRIVATE_H -+#endif // VELOX_TZ_PRIVATE_H diff --git a/velox/external/date/patches/0005-add_error_type_for_invalid_timezone.patch b/velox/external/date/patches/0005-add_error_type_for_invalid_timezone.patch deleted file mode 100644 index 90d20ed37a61..000000000000 --- a/velox/external/date/patches/0005-add_error_type_for_invalid_timezone.patch +++ /dev/null @@ -1,51 +0,0 @@ -diff --git a/velox/external/date/tz.cpp b/velox/external/date/tz.cpp -index c3a3a8f7e..69513d7d3 100644 ---- a/velox/external/date/tz.cpp -+++ b/velox/external/date/tz.cpp -@@ -3577,7 +3577,7 @@ tzdb::locate_zone(const std::string& tz_name) const - return &*zi; - } - #endif // !USE_OS_TZDB -- throw std::runtime_error(std::string(tz_name) + " not found in timezone database"); -+ throw invalid_timezone(std::string(tz_name)); - } - return &*zi; - } -diff --git a/velox/external/date/tz.h b/velox/external/date/tz.h -index a70bda4ad..c85b30eb7 100644 ---- a/velox/external/date/tz.h -+++ b/velox/external/date/tz.h -@@ -284,6 +284,33 @@ ambiguous_local_time::make_msg(local_time tp, const local_info& i) - return os.str(); - } - -+class invalid_timezone -+ : public std::runtime_error -+{ -+public: -+ invalid_timezone(const std::string tz_name); -+ -+private: -+ static -+ std::string -+ make_msg(const std::string tz_name); -+}; -+ -+inline -+invalid_timezone::invalid_timezone(const std::string tz_name) -+ : std::runtime_error(make_msg(tz_name)) -+{ -+} -+ -+inline -+std::string -+invalid_timezone::make_msg(const std::string tz_name) -+{ -+ std::ostringstream os; -+ os << tz_name << " not found in timezone database"; -+ return os.str(); -+} -+ - class time_zone; - - #if HAS_STRING_VIEW diff --git a/velox/external/date/patches/0006-add_get_time_zone_names.patch b/velox/external/date/patches/0006-add_get_time_zone_names.patch deleted file mode 100644 index f7b60effe4f3..000000000000 --- a/velox/external/date/patches/0006-add_get_time_zone_names.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/velox/external/date/tz.cpp b/velox/external/date/tz.cpp ---- a/velox/external/date/tz.cpp -+++ b/velox/external/date/tz.cpp -@@ -3538,6 +3538,14 @@ - return get_tzdb_list().front(); - } - -+std::vector get_time_zone_names() { -+ std::vector result; -+ for (const auto& z : get_tzdb().zones) { -+ result.push_back(z.name()); -+ } -+ return result; -+} -+ - const time_zone* - #if HAS_STRING_VIEW - tzdb::locate_zone(std::string_view tz_name) const -diff --git a/velox/external/date/tz.h b/velox/external/date/tz.h ---- a/velox/external/date/tz.h -+++ b/velox/external/date/tz.h -@@ -1258,6 +1258,8 @@ - - DATE_API const tzdb& get_tzdb(); - -+std::vector get_time_zone_names(); -+ - class tzdb_list - { - std::atomic head_{nullptr}; diff --git a/velox/external/date/tz.cpp b/velox/external/date/tz.cpp deleted file mode 100644 index db230bfbab54..000000000000 --- a/velox/external/date/tz.cpp +++ /dev/null @@ -1,3914 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016, 2017 Howard Hinnant -// Copyright (c) 2015 Ville Voutilainen -// Copyright (c) 2016 Alexander Kormanovsky -// Copyright (c) 2016, 2017 Jiangang Zhuang -// Copyright (c) 2017 Nicolas Veloz Savino -// Copyright (c) 2017 Florian Dang -// Copyright (c) 2017 Aaron Bishop -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -#ifdef _WIN32 - // windows.h will be included directly and indirectly (e.g. by curl). - // We need to define these macros to prevent windows.h bringing in - // more than we need and do it early so windows.h doesn't get included - // without these macros having been defined. - // min/max macros interfere with the C++ versions. -# ifndef NOMINMAX -# define NOMINMAX -# endif - // We don't need all that Windows has to offer. -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif - - // for wcstombs -# ifndef _CRT_SECURE_NO_WARNINGS -# define _CRT_SECURE_NO_WARNINGS -# endif - - // None of this happens with the MS SDK (at least VS14 which I tested), but: - // Compiling with mingw, we get "error: 'KF_FLAG_DEFAULT' was not declared in this scope." - // and error: 'SHGetKnownFolderPath' was not declared in this scope.". - // It seems when using mingw NTDDI_VERSION is undefined and that - // causes KNOWN_FOLDER_FLAG and the KF_ flags to not get defined. - // So we must define NTDDI_VERSION to get those flags on mingw. - // The docs say though here: - // https://msdn.microsoft.com/en-nz/library/windows/desktop/aa383745(v=vs.85).aspx - // that "If you define NTDDI_VERSION, you must also define _WIN32_WINNT." - // So we declare we require Vista or greater. -# ifdef __MINGW32__ - -# ifndef NTDDI_VERSION -# define NTDDI_VERSION 0x06000000 -# define _WIN32_WINNT _WIN32_WINNT_VISTA -# elif NTDDI_VERSION < 0x06000000 -# warning "If this fails to compile NTDDI_VERSION may be to low. See comments above." -# endif - // But once we define the values above we then get this linker error: - // "tz.cpp:(.rdata$.refptr.FOLDERID_Downloads[.refptr.FOLDERID_Downloads]+0x0): " - // "undefined reference to `FOLDERID_Downloads'" - // which #include cures see: - // https://support.microsoft.com/en-us/kb/130869 -# include - // But with included, the error moves on to: - // error: 'FOLDERID_Downloads' was not declared in this scope - // Which #include cures. -# include - -# endif // __MINGW32__ - -# include -#endif // _WIN32 - -#include "date/tz_private.h" - -#ifdef __APPLE__ -# include "date/ios.h" -#else -# define TARGET_OS_IPHONE 0 -#endif - -#if USE_OS_TZDB -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if USE_OS_TZDB -# include -#endif -#include -#include -#include -#include -#include - -// unistd.h is used on some platforms as part of the the means to get -// the current time zone. On Win32 windows.h provides a means to do it. -// gcc/mingw supports unistd.h on Win32 but MSVC does not. - -#ifdef _WIN32 -# ifdef WINAPI_FAMILY -# include -# if WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP -# define WINRT -# define INSTALL . -# endif -# endif - -# include // _unlink etc. - -# if defined(__clang__) - struct IUnknown; // fix for issue with static_cast<> in objbase.h - // (see https://github.com/philsquared/Catch/issues/690) -# endif - -# include // CoTaskFree, ShGetKnownFolderPath etc. -# if HAS_REMOTE_API -# include // _mkdir -# include // ShFileOperation etc. -# endif // HAS_REMOTE_API -#else // !_WIN32 -# include -# if !USE_OS_TZDB -# include -# endif -# include -# include -# if !USE_SHELL_API -# include -# include -# include -# include -# include -# include -# endif //!USE_SHELL_API -#endif // !_WIN32 - - -#if HAS_REMOTE_API - // Note curl includes windows.h so we must include curl AFTER definitions of things - // that affect windows.h such as NOMINMAX. -#if defined(_MSC_VER) && defined(SHORTENED_CURL_INCLUDE) - // For rmt_curl nuget package -# include -#else -# include -#endif -#endif - -#ifdef _WIN32 -static CONSTDATA char folder_delimiter = '\\'; -#else // !_WIN32 -static CONSTDATA char folder_delimiter = '/'; -#endif // !_WIN32 - -#if defined(__GNUC__) && __GNUC__ < 5 - // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif // defined(__GNUC__) && __GNUC__ < 5 - -#if !USE_OS_TZDB - -# ifdef _WIN32 -# ifndef WINRT - -namespace -{ - struct task_mem_deleter - { - void operator()(wchar_t buf[]) - { - if (buf != nullptr) - CoTaskMemFree(buf); - } - }; - using co_task_mem_ptr = std::unique_ptr; -} - -// We might need to know certain locations even if not using the remote API, -// so keep these routines out of that block for now. -static -std::string -get_known_folder(const GUID& folderid) -{ - std::string folder; - PWSTR pfolder = nullptr; - HRESULT hr = SHGetKnownFolderPath(folderid, KF_FLAG_DEFAULT, nullptr, &pfolder); - if (SUCCEEDED(hr)) - { - co_task_mem_ptr folder_ptr(pfolder); - const wchar_t* fptr = folder_ptr.get(); - auto state = std::mbstate_t(); - const auto required = std::wcsrtombs(nullptr, &fptr, 0, &state); - if (required != 0 && required != std::size_t(-1)) - { - folder.resize(required); - std::wcsrtombs(&folder[0], &fptr, folder.size(), &state); - } - } - return folder; -} - -# ifndef INSTALL - -// Usually something like "c:\Users\username\Downloads". -static -std::string -get_download_folder() -{ - return get_known_folder(FOLDERID_Downloads); -} - -# endif // !INSTALL - -# endif // WINRT -# else // !_WIN32 - -# if !defined(INSTALL) - -static -std::string -expand_path(std::string path) -{ -# if TARGET_OS_IPHONE - return date::iOSUtils::get_tzdata_path(); -# else // !TARGET_OS_IPHONE - ::wordexp_t w{}; - std::unique_ptr<::wordexp_t, void(*)(::wordexp_t*)> hold{&w, ::wordfree}; - ::wordexp(path.c_str(), &w, 0); - if (w.we_wordc != 1) - throw std::runtime_error("Cannot expand path: " + path); - path = w.we_wordv[0]; - return path; -# endif // !TARGET_OS_IPHONE -} - -static -std::string -get_download_folder() -{ - return expand_path("~/Downloads"); -} - -# endif // !defined(INSTALL) - -# endif // !_WIN32 - -#endif // !USE_OS_TZDB - -namespace facebook -{ -namespace velox -{ -namespace date -{ -// +---------------------+ -// | Begin Configuration | -// +---------------------+ - -using namespace detail; - -#if !USE_OS_TZDB - -static -std::string& -access_install() -{ - static std::string install -#ifndef INSTALL - - = get_download_folder() + folder_delimiter + "tzdata"; - -#else // !INSTALL - -# define STRINGIZEIMP(x) #x -# define STRINGIZE(x) STRINGIZEIMP(x) - - = STRINGIZE(INSTALL) + std::string(1, folder_delimiter) + "tzdata"; - - #undef STRINGIZEIMP - #undef STRINGIZE -#endif // !INSTALL - - return install; -} - -void -set_install(const std::string& s) -{ - access_install() = s; -} - -static -const std::string& -get_install() -{ - static const std::string& ref = access_install(); - return ref; -} - -#if HAS_REMOTE_API -static -std::string -get_download_gz_file(const std::string& version) -{ - auto file = get_install() + version + ".tar.gz"; - return file; -} -#endif // HAS_REMOTE_API - -#endif // !USE_OS_TZDB - -// These can be used to reduce the range of the database to save memory -CONSTDATA auto min_year = date::year::min(); -CONSTDATA auto max_year = date::year::max(); - -CONSTDATA auto min_day = date::January/1; -CONSTDATA auto max_day = date::December/31; - -#if USE_OS_TZDB - -CONSTCD14 const sys_seconds min_seconds = sys_days(min_year/min_day); - -#endif // USE_OS_TZDB - -#ifndef _WIN32 - -static -std::string -discover_tz_dir() -{ - struct stat sb; - using namespace std; -# ifndef __APPLE__ - CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo"; - CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc"; - - // Check special path which is valid for buildroot with uclibc builds - if(stat(tz_dir_buildroot, &sb) == 0 && S_ISDIR(sb.st_mode)) - return tz_dir_buildroot; - else if(stat(tz_dir_default, &sb) == 0 && S_ISDIR(sb.st_mode)) - return tz_dir_default; - else - throw runtime_error("discover_tz_dir failed to find zoneinfo\n"); -# else // __APPLE__ -# if TARGET_OS_IPHONE - return "/var/db/timezone/zoneinfo"; -# else - CONSTDATA auto timezone = "/etc/localtime"; - if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0)) - throw runtime_error("discover_tz_dir failed\n"); - string result; - char rp[PATH_MAX+1] = {}; - if (readlink(timezone, rp, sizeof(rp)-1) > 0) - result = string(rp); - else - throw system_error(errno, system_category(), "readlink() failed"); - auto i = result.find("zoneinfo"); - if (i == string::npos) - throw runtime_error("discover_tz_dir failed to find zoneinfo\n"); - i = result.find('/', i); - if (i == string::npos) - throw runtime_error("discover_tz_dir failed to find '/'\n"); - return result.substr(0, i); -# endif -# endif // __APPLE__ -} - -static -const std::string& -get_tz_dir() -{ - static const std::string tz_dir = discover_tz_dir(); - return tz_dir; -} - -#endif - -// +-------------------+ -// | End Configuration | -// +-------------------+ - -#ifndef _MSC_VER -static_assert(min_year <= max_year, "Configuration error"); -#endif - -static std::unique_ptr init_tzdb(); - -tzdb_list::~tzdb_list() -{ - const tzdb* ptr = head_; - head_ = nullptr; - while (ptr != nullptr) - { - auto next = ptr->next; - delete ptr; - ptr = next; - } -} - -tzdb_list::tzdb_list(tzdb_list&& x) noexcept - : head_{x.head_.exchange(nullptr)} -{ -} - -void -tzdb_list::push_front(tzdb* tzdb) noexcept -{ - tzdb->next = head_; - head_ = tzdb; -} - -tzdb_list::const_iterator -tzdb_list::erase_after(const_iterator p) noexcept -{ - auto t = p.p_->next; - p.p_->next = p.p_->next->next; - delete t; - return ++p; -} - -struct tzdb_list::undocumented_helper -{ - static void push_front(tzdb_list& db_list, tzdb* tzdb) noexcept - { - db_list.push_front(tzdb); - } -}; - -static -tzdb_list -create_tzdb() -{ - tzdb_list tz_db; - tzdb_list::undocumented_helper::push_front(tz_db, init_tzdb().release()); - return tz_db; -} - -tzdb_list& -get_tzdb_list() -{ - static tzdb_list tz_db = create_tzdb(); - return tz_db; -} - -#if !USE_OS_TZDB - -#ifdef _WIN32 - -static -void -sort_zone_mappings(std::vector& mappings) -{ - std::sort(mappings.begin(), mappings.end(), - [](const date::detail::timezone_mapping& lhs, - const date::detail::timezone_mapping& rhs)->bool - { - auto other_result = lhs.other.compare(rhs.other); - if (other_result < 0) - return true; - else if (other_result == 0) - { - auto territory_result = lhs.territory.compare(rhs.territory); - if (territory_result < 0) - return true; - else if (territory_result == 0) - { - if (lhs.type < rhs.type) - return true; - } - } - return false; - }); -} - -static -bool -native_to_standard_timezone_name(const std::string& native_tz_name, - std::string& standard_tz_name) -{ - // TOOD! Need be a case insensitive compare? - if (native_tz_name == "UTC") - { - standard_tz_name = "Etc/UTC"; - return true; - } - standard_tz_name.clear(); - // TODO! we can improve on linear search. - const auto& mappings = date::get_tzdb().mappings; - for (const auto& tzm : mappings) - { - if (tzm.other == native_tz_name) - { - standard_tz_name = tzm.type; - return true; - } - } - return false; -} - -// Parse this XML file: -// https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml -// The parsing method is designed to be simple and quick. It is not overly -// forgiving of change but it should diagnose basic format issues. -// See timezone_mapping structure for more info. -static -std::vector -load_timezone_mappings_from_xml_file(const std::string& input_path) -{ - std::size_t line_num = 0; - std::vector mappings; - std::string line; - - std::ifstream is(input_path); - if (!is.is_open()) - { - // We don't emit file exceptions because that's an implementation detail. - std::string msg = "Error opening time zone mapping file \""; - msg += input_path; - msg += "\"."; - throw std::runtime_error(msg); - } - - auto error = [&input_path, &line_num](const char* info) - { - std::string msg = "Error loading time zone mapping file \""; - msg += input_path; - msg += "\" at line "; - msg += std::to_string(line_num); - msg += ": "; - msg += info; - throw std::runtime_error(msg); - }; - // [optional space]a="b" - auto read_attribute = [&line, &error] - (const char* name, std::string& value, std::size_t startPos) - ->std::size_t - { - value.clear(); - // Skip leading space before attribute name. - std::size_t spos = line.find_first_not_of(' ', startPos); - if (spos == std::string::npos) - spos = startPos; - // Assume everything up to next = is the attribute name - // and that an = will always delimit that. - std::size_t epos = line.find('=', spos); - if (epos == std::string::npos) - error("Expected \'=\' right after attribute name."); - std::size_t name_len = epos - spos; - // Expect the name we find matches the name we expect. - if (line.compare(spos, name_len, name) != 0) - { - std::string msg; - msg = "Expected attribute name \'"; - msg += name; - msg += "\' around position "; - msg += std::to_string(spos); - msg += " but found something else."; - error(msg.c_str()); - } - ++epos; // Skip the '=' that is after the attribute name. - spos = epos; - if (spos < line.length() && line[spos] == '\"') - ++spos; // Skip the quote that is before the attribute value. - else - { - std::string msg = "Expected '\"' to begin value of attribute \'"; - msg += name; - msg += "\'."; - error(msg.c_str()); - } - epos = line.find('\"', spos); - if (epos == std::string::npos) - { - std::string msg = "Expected '\"' to end value of attribute \'"; - msg += name; - msg += "\'."; - error(msg.c_str()); - } - // Extract everything in between the quotes. Note no escaping is done. - std::size_t value_len = epos - spos; - value.assign(line, spos, value_len); - ++epos; // Skip the quote that is after the attribute value; - return epos; - }; - - // Quick but not overly forgiving XML mapping file processing. - bool mapTimezonesOpenTagFound = false; - bool mapTimezonesCloseTagFound = false; - std::size_t mapZonePos = std::string::npos; - std::size_t mapTimezonesPos = std::string::npos; - CONSTDATA char mapTimeZonesOpeningTag[] = { ""); - mapTimezonesCloseTagFound = (mapTimezonesPos != std::string::npos); - if (!mapTimezonesCloseTagFound) - { - std::size_t commentPos = line.find(" " << x.target_; -} - -// leap_second - -leap_second::leap_second(const std::string& s, detail::undocumented) -{ - using namespace date; - std::istringstream in(s); - in.exceptions(std::ios::failbit | std::ios::badbit); - std::string word; - int y; - MonthDayTime date; - in >> word >> y >> date; - date_ = date.to_time_point(year(y)); -} - -static -bool -file_exists(const std::string& filename) -{ -#ifdef _WIN32 - return ::_access(filename.c_str(), 0) == 0; -#else - return ::access(filename.c_str(), F_OK) == 0; -#endif -} - -#if HAS_REMOTE_API - -// CURL tools - -static -int -curl_global() -{ - if (::curl_global_init(CURL_GLOBAL_DEFAULT) != 0) - throw std::runtime_error("CURL global initialization failed"); - return 0; -} - -namespace -{ - -struct curl_deleter -{ - void operator()(CURL* p) const - { - ::curl_easy_cleanup(p); - } -}; - -} // unnamed namespace - -static -std::unique_ptr -curl_init() -{ - static const auto curl_is_now_initialized = curl_global(); - static_cast(curl_is_now_initialized); - return std::unique_ptr{::curl_easy_init()}; -} - -static -bool -download_to_string(const std::string& url, std::string& str) -{ - str.clear(); - auto curl = curl_init(); - if (!curl) - return false; - std::string version; - curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, "curl"); - // Use TLS 1.2 or later. - curl_easy_setopt(curl.get(), CURLOPT_SSLVERSION, static_cast(CURL_SSLVERSION_TLSv1_2)); - curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()); - curl_write_callback write_cb = [](char* contents, std::size_t size, std::size_t nmemb, - void* userp) -> std::size_t - { - auto& userstr = *static_cast(userp); - auto realsize = size * nmemb; - userstr.append(contents, realsize); - return realsize; - }; - curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str); - curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, false); - auto res = curl_easy_perform(curl.get()); - return (res == CURLE_OK); -} - -namespace -{ - enum class download_file_options { binary, text }; -} - -static -bool -download_to_file(const std::string& url, const std::string& local_filename, - download_file_options opts, char* error_buffer) -{ - auto curl = curl_init(); - if (!curl) - return false; - curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, false); - if (error_buffer) - curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, error_buffer); - curl_write_callback write_cb = [](char* contents, std::size_t size, std::size_t nmemb, - void* userp) -> std::size_t - { - auto& of = *static_cast(userp); - auto realsize = size * nmemb; - of.write(contents, static_cast(realsize)); - return realsize; - }; - curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_cb); - decltype(curl_easy_perform(curl.get())) res; - { - std::ofstream of(local_filename, - opts == download_file_options::binary ? - std::ofstream::out | std::ofstream::binary : - std::ofstream::out); - of.exceptions(std::ios::badbit); - curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &of); - res = curl_easy_perform(curl.get()); - } - return res == CURLE_OK; -} - -std::string -remote_version() -{ - std::string version; - std::string str; - if (download_to_string("https://www.iana.org/time-zones", str)) - { - CONSTDATA char db[] = "/time-zones/releases/tzdata"; - CONSTDATA auto db_size = sizeof(db) - 1; - auto p = str.find(db, 0, db_size); - const int ver_str_len = 5; - if (p != std::string::npos && p + (db_size + ver_str_len) <= str.size()) - version = str.substr(p + db_size, ver_str_len); - } - return version; -} - - -// TODO! Using system() create a process and a console window. -// This is useful to see what errors may occur but is slow and distracting. -// Consider implementing this functionality more directly, such as -// using _mkdir and CreateProcess etc. -// But use the current means now as matches Unix implementations and while -// in proof of concept / testing phase. -// TODO! Use eventually. -static -bool -remove_folder_and_subfolders(const std::string& folder) -{ -# ifdef _WIN32 -# if USE_SHELL_API - // Delete the folder contents by deleting the folder. - std::string cmd = "rd /s /q \""; - cmd += folder; - cmd += '\"'; - return std::system(cmd.c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - // Create a buffer containing the path to delete. It must be terminated - // by two nuls. Who designs these API's... - std::vector from; - from.assign(folder.begin(), folder.end()); - from.push_back('\0'); - from.push_back('\0'); - SHFILEOPSTRUCT fo{}; // Zero initialize. - fo.wFunc = FO_DELETE; - fo.pFrom = from.data(); - fo.fFlags = FOF_NO_UI; - int ret = SHFileOperation(&fo); - if (ret == 0 && !fo.fAnyOperationsAborted) - return true; - return false; -# endif // !USE_SHELL_API -# else // !_WIN32 -# if USE_SHELL_API - return std::system(("rm -R " + folder).c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - struct dir_deleter { - dir_deleter() {} - void operator()(DIR* d) const - { - if (d != nullptr) - { - int result = closedir(d); - assert(result == 0); - } - } - }; - using closedir_ptr = std::unique_ptr; - - std::string filename; - struct stat statbuf; - std::size_t folder_len = folder.length(); - struct dirent* p = nullptr; - - closedir_ptr d(opendir(folder.c_str())); - bool r = d.get() != nullptr; - while (r && (p=readdir(d.get())) != nullptr) - { - if (strcmp(p->d_name, ".") == 0 || strcmp(p->d_name, "..") == 0) - continue; - - // + 2 for path delimiter and nul terminator. - std::size_t buf_len = folder_len + strlen(p->d_name) + 2; - filename.resize(buf_len); - std::size_t path_len = static_cast( - snprintf(&filename[0], buf_len, "%s/%s", folder.c_str(), p->d_name)); - assert(path_len == buf_len - 1); - filename.resize(path_len); - - if (stat(filename.c_str(), &statbuf) == 0) - r = S_ISDIR(statbuf.st_mode) - ? remove_folder_and_subfolders(filename) - : unlink(filename.c_str()) == 0; - } - d.reset(); - - if (r) - r = rmdir(folder.c_str()) == 0; - - return r; -# endif // !USE_SHELL_API -# endif // !_WIN32 -} - -static -bool -make_directory(const std::string& folder) -{ -# ifdef _WIN32 -# if USE_SHELL_API - // Re-create the folder. - std::string cmd = "mkdir \""; - cmd += folder; - cmd += '\"'; - return std::system(cmd.c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - return _mkdir(folder.c_str()) == 0; -# endif // !USE_SHELL_API -# else // !_WIN32 -# if USE_SHELL_API - return std::system(("mkdir -p " + folder).c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - return mkdir(folder.c_str(), 0777) == 0; -# endif // !USE_SHELL_API -# endif // !_WIN32 -} - -static -bool -delete_file(const std::string& file) -{ -# ifdef _WIN32 -# if USE_SHELL_API - std::string cmd = "del \""; - cmd += file; - cmd += '\"'; - return std::system(cmd.c_str()) == 0; -# else // !USE_SHELL_API - return _unlink(file.c_str()) == 0; -# endif // !USE_SHELL_API -# else // !_WIN32 -# if USE_SHELL_API - return std::system(("rm " + file).c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - return unlink(file.c_str()) == 0; -# endif // !USE_SHELL_API -# endif // !_WIN32 -} - -# ifdef _WIN32 - -static -bool -move_file(const std::string& from, const std::string& to) -{ -# if USE_SHELL_API - std::string cmd = "move \""; - cmd += from; - cmd += "\" \""; - cmd += to; - cmd += '\"'; - return std::system(cmd.c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - return !!::MoveFile(from.c_str(), to.c_str()); -# endif // !USE_SHELL_API -} - -// Usually something like "c:\Program Files". -static -std::string -get_program_folder() -{ - return get_known_folder(FOLDERID_ProgramFiles); -} - -// Note folder can and usually does contain spaces. -static -std::string -get_unzip_program() -{ - std::string path; - - // 7-Zip appears to note its location in the registry. - // If that doesn't work, fall through and take a guess, but it will likely be wrong. - HKEY hKey = nullptr; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\7-Zip", 0, KEY_READ, &hKey) == ERROR_SUCCESS) - { - char value_buffer[MAX_PATH + 1]; // fyi 260 at time of writing. - // in/out parameter. Documentation say that size is a count of bytes not chars. - DWORD size = sizeof(value_buffer) - sizeof(value_buffer[0]); - DWORD tzi_type = REG_SZ; - // Testing shows Path key value is "C:\Program Files\7-Zip\" i.e. always with trailing \. - bool got_value = (RegQueryValueExA(hKey, "Path", nullptr, &tzi_type, - reinterpret_cast(value_buffer), &size) == ERROR_SUCCESS); - RegCloseKey(hKey); // Close now incase of throw later. - if (got_value) - { - // Function does not guarantee to null terminate. - value_buffer[size / sizeof(value_buffer[0])] = '\0'; - path = value_buffer; - if (!path.empty()) - { - path += "7z.exe"; - return path; - } - } - } - path += get_program_folder(); - path += folder_delimiter; - path += "7-Zip\\7z.exe"; - return path; -} - -# if !USE_SHELL_API -static -int -run_program(const std::string& command) -{ - STARTUPINFO si{}; - si.cb = sizeof(si); - PROCESS_INFORMATION pi{}; - - // Allegedly CreateProcess overwrites the command line. Ugh. - std::string mutable_command(command); - if (CreateProcess(nullptr, &mutable_command[0], - nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) - { - WaitForSingleObject(pi.hProcess, INFINITE); - DWORD exit_code; - bool got_exit_code = !!GetExitCodeProcess(pi.hProcess, &exit_code); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - // Not 100% sure about this still active thing is correct, - // but I'm going with it because I *think* WaitForSingleObject might - // return in some cases without INFINITE-ly waiting. - // But why/wouldn't GetExitCodeProcess return false in that case? - if (got_exit_code && exit_code != STILL_ACTIVE) - return static_cast(exit_code); - } - return EXIT_FAILURE; -} -# endif // !USE_SHELL_API - -static -std::string -get_download_tar_file(const std::string& version) -{ - auto file = get_install(); - file += folder_delimiter; - file += "tzdata"; - file += version; - file += ".tar"; - return file; -} - -static -bool -extract_gz_file(const std::string& version, const std::string& gz_file, - const std::string& dest_folder) -{ - auto unzip_prog = get_unzip_program(); - bool unzip_result = false; - // Use the unzip program to extract the tar file from the archive. - - // Aim to create a string like: - // "C:\Program Files\7-Zip\7z.exe" x "C:\Users\SomeUser\Downloads\tzdata2016d.tar.gz" - // -o"C:\Users\SomeUser\Downloads\tzdata" - std::string cmd; - cmd = '\"'; - cmd += unzip_prog; - cmd += "\" x \""; - cmd += gz_file; - cmd += "\" -o\""; - cmd += dest_folder; - cmd += '\"'; - -# if USE_SHELL_API - // When using shelling out with std::system() extra quotes are required around the - // whole command. It's weird but necessary it seems, see: - // http://stackoverflow.com/q/27975969/576911 - - cmd = "\"" + cmd + "\""; - if (std::system(cmd.c_str()) == EXIT_SUCCESS) - unzip_result = true; -# else // !USE_SHELL_API - if (run_program(cmd) == EXIT_SUCCESS) - unzip_result = true; -# endif // !USE_SHELL_API - if (unzip_result) - delete_file(gz_file); - - // Use the unzip program extract the data from the tar file that was - // just extracted from the archive. - auto tar_file = get_download_tar_file(version); - cmd = '\"'; - cmd += unzip_prog; - cmd += "\" x \""; - cmd += tar_file; - cmd += "\" -o\""; - cmd += get_install(); - cmd += '\"'; -# if USE_SHELL_API - cmd = "\"" + cmd + "\""; - if (std::system(cmd.c_str()) == EXIT_SUCCESS) - unzip_result = true; -# else // !USE_SHELL_API - if (run_program(cmd) == EXIT_SUCCESS) - unzip_result = true; -# endif // !USE_SHELL_API - - if (unzip_result) - delete_file(tar_file); - - return unzip_result; -} - -static -std::string -get_download_mapping_file(const std::string& version) -{ - auto file = get_install() + version + "windowsZones.xml"; - return file; -} - -# else // !_WIN32 - -# if !USE_SHELL_API -static -int -run_program(const char* prog, const char*const args[]) -{ - pid_t pid = fork(); - if (pid == -1) // Child failed to start. - return EXIT_FAILURE; - - if (pid != 0) - { - // We are in the parent. Child started. Wait for it. - pid_t ret; - int status; - while ((ret = waitpid(pid, &status, 0)) == -1) - { - if (errno != EINTR) - break; - } - if (ret != -1) - { - if (WIFEXITED(status)) - return WEXITSTATUS(status); - } - printf("Child issues!\n"); - - return EXIT_FAILURE; // Not sure what status of child is. - } - else // We are in the child process. Start the program the parent wants to run. - { - - if (execv(prog, const_cast(args)) == -1) // Does not return. - { - perror("unreachable 0\n"); - _Exit(127); - } - printf("unreachable 2\n"); - } - printf("unreachable 2\n"); - // Unreachable. - assert(false); - exit(EXIT_FAILURE); - return EXIT_FAILURE; -} -# endif // !USE_SHELL_API - -static -bool -extract_gz_file(const std::string&, const std::string& gz_file, const std::string&) -{ -# if USE_SHELL_API - bool unzipped = std::system(("tar -xzf " + gz_file + " -C " + get_install()).c_str()) == EXIT_SUCCESS; -# else // !USE_SHELL_API - const char prog[] = {"/usr/bin/tar"}; - const char*const args[] = - { - prog, "-xzf", gz_file.c_str(), "-C", get_install().c_str(), nullptr - }; - bool unzipped = (run_program(prog, args) == EXIT_SUCCESS); -# endif // !USE_SHELL_API - if (unzipped) - { - delete_file(gz_file); - return true; - } - return false; -} - -# endif // !_WIN32 - -bool -remote_download(const std::string& version, char* error_buffer) -{ - assert(!version.empty()); - -# ifdef _WIN32 - // Download folder should be always available for Windows -# else // !_WIN32 - // Create download folder if it does not exist on UNIX system - auto download_folder = get_install(); - if (!file_exists(download_folder)) - { - if (!make_directory(download_folder)) - return false; - } -# endif // _WIN32 - - auto url = "https://data.iana.org/time-zones/releases/tzdata" + version + - ".tar.gz"; - bool result = download_to_file(url, get_download_gz_file(version), - download_file_options::binary, error_buffer); -# ifdef _WIN32 - if (result) - { - auto mapping_file = get_download_mapping_file(version); - result = download_to_file( - "https://raw.githubusercontent.com/unicode-org/cldr/master/" - "common/supplemental/windowsZones.xml", - mapping_file, download_file_options::text, error_buffer); - } -# endif // _WIN32 - return result; -} - -bool -remote_install(const std::string& version) -{ - auto success = false; - assert(!version.empty()); - - std::string install = get_install(); - auto gz_file = get_download_gz_file(version); - if (file_exists(gz_file)) - { - if (file_exists(install)) - remove_folder_and_subfolders(install); - if (make_directory(install)) - { - if (extract_gz_file(version, gz_file, install)) - success = true; -# ifdef _WIN32 - auto mapping_file_source = get_download_mapping_file(version); - auto mapping_file_dest = get_install(); - mapping_file_dest += folder_delimiter; - mapping_file_dest += "windowsZones.xml"; - if (!move_file(mapping_file_source, mapping_file_dest)) - success = false; -# endif // _WIN32 - } - } - return success; -} - -#endif // HAS_REMOTE_API - -static -std::string -get_version(const std::string& path) -{ - std::string version; - std::ifstream infile(path + "version"); - if (infile.is_open()) - { - infile >> version; - if (!infile.fail()) - return version; - } - else - { - infile.open(path + "NEWS"); - while (infile) - { - infile >> version; - if (version == "Release") - { - infile >> version; - return version; - } - } - } - throw std::runtime_error("Unable to get Timezone database version from " + path); -} - -static -std::unique_ptr -init_tzdb() -{ - using namespace date; - const std::string install = get_install(); - const std::string path = install + folder_delimiter; - std::string line; - bool continue_zone = false; - std::unique_ptr db(new tzdb); - -#if AUTO_DOWNLOAD - if (!file_exists(install)) - { - auto rv = remote_version(); - if (!rv.empty() && remote_download(rv)) - { - if (!remote_install(rv)) - { - std::string msg = "Timezone database version \""; - msg += rv; - msg += "\" did not install correctly to \""; - msg += install; - msg += "\""; - throw std::runtime_error(msg); - } - } - if (!file_exists(install)) - { - std::string msg = "Timezone database not found at \""; - msg += install; - msg += "\""; - throw std::runtime_error(msg); - } - db->version = get_version(path); - } - else - { - db->version = get_version(path); - auto rv = remote_version(); - if (!rv.empty() && db->version != rv) - { - if (remote_download(rv)) - { - remote_install(rv); - db->version = get_version(path); - } - } - } -#else // !AUTO_DOWNLOAD - if (!file_exists(install)) - { - std::string msg = "Timezone database not found at \""; - msg += install; - msg += "\""; - throw std::runtime_error(msg); - } - db->version = get_version(path); -#endif // !AUTO_DOWNLOAD - - CONSTDATA char*const files[] = - { - "africa", "antarctica", "asia", "australasia", "backward", "etcetera", "europe", - "pacificnew", "northamerica", "southamerica", "systemv", "leapseconds" - }; - - for (const auto& filename : files) - { - std::ifstream infile(path + filename); - while (infile) - { - std::getline(infile, line); - if (!line.empty() && line[0] != '#') - { - std::istringstream in(line); - std::string word; - in >> word; - if (word == "Rule") - { - db->rules.push_back(Rule(line)); - continue_zone = false; - } - else if (word == "Link") - { - db->links.push_back(time_zone_link(line)); - continue_zone = false; - } - else if (word == "Leap") - { - db->leap_seconds.push_back(leap_second(line, detail::undocumented{})); - continue_zone = false; - } - else if (word == "Zone") - { - db->zones.push_back(time_zone(line, detail::undocumented{})); - continue_zone = true; - } - else if (line[0] == '\t' && continue_zone) - { - db->zones.back().add(line); - } - else - { - std::cerr << line << '\n'; - } - } - } - } - std::sort(db->rules.begin(), db->rules.end()); - Rule::split_overlaps(db->rules); - std::sort(db->zones.begin(), db->zones.end()); - db->zones.shrink_to_fit(); - std::sort(db->links.begin(), db->links.end()); - db->links.shrink_to_fit(); - std::sort(db->leap_seconds.begin(), db->leap_seconds.end()); - db->leap_seconds.shrink_to_fit(); - -#ifdef _WIN32 - std::string mapping_file = get_install() + folder_delimiter + "windowsZones.xml"; - db->mappings = load_timezone_mappings_from_xml_file(mapping_file); - sort_zone_mappings(db->mappings); -#endif // _WIN32 - - return db; -} - -const tzdb& -reload_tzdb() -{ -#if AUTO_DOWNLOAD - auto const& v = get_tzdb_list().front().version; - if (!v.empty() && v == remote_version()) - return get_tzdb_list().front(); -#endif // AUTO_DOWNLOAD - tzdb_list::undocumented_helper::push_front(get_tzdb_list(), init_tzdb().release()); - return get_tzdb_list().front(); -} - -#endif // !USE_OS_TZDB - -const tzdb& -get_tzdb() -{ - return get_tzdb_list().front(); -} - -std::vector get_time_zone_names() { - std::vector result; - for (const auto& z : get_tzdb().zones) { - result.push_back(z.name()); - } - return result; -} - -const time_zone* -#if HAS_STRING_VIEW -tzdb::locate_zone(std::string_view tz_name) const -#else -tzdb::locate_zone(const std::string& tz_name) const -#endif -{ - auto zi = std::lower_bound(zones.begin(), zones.end(), tz_name, -#if HAS_STRING_VIEW - [](const time_zone& z, const std::string_view& nm) -#else - [](const time_zone& z, const std::string& nm) -#endif - { - return z.name() < nm; - }); - if (zi == zones.end() || zi->name() != tz_name) - { -#if !USE_OS_TZDB - auto li = std::lower_bound(links.begin(), links.end(), tz_name, -#if HAS_STRING_VIEW - [](const time_zone_link& z, const std::string_view& nm) -#else - [](const time_zone_link& z, const std::string& nm) -#endif - { - return z.name() < nm; - }); - if (li != links.end() && li->name() == tz_name) - { - zi = std::lower_bound(zones.begin(), zones.end(), li->target(), - [](const time_zone& z, const std::string& nm) - { - return z.name() < nm; - }); - if (zi != zones.end() && zi->name() == li->target()) - return &*zi; - } -#endif // !USE_OS_TZDB - throw invalid_timezone(std::string(tz_name)); - } - return &*zi; -} - -const time_zone* -#if HAS_STRING_VIEW -locate_zone(std::string_view tz_name) -#else -locate_zone(const std::string& tz_name) -#endif -{ - return get_tzdb().locate_zone(tz_name); -} - -#if USE_OS_TZDB - -std::ostream& -operator<<(std::ostream& os, const tzdb& db) -{ - os << "Version: " << db.version << "\n\n"; - for (const auto& x : db.zones) - os << x << '\n'; -#if !MISSING_LEAP_SECONDS - os << '\n'; - for (const auto& x : db.leap_seconds) - os << x << '\n'; -#endif // !MISSING_LEAP_SECONDS - return os; -} - -#else // !USE_OS_TZDB - -std::ostream& -operator<<(std::ostream& os, const tzdb& db) -{ - os << "Version: " << db.version << '\n'; - std::string title("--------------------------------------------" - "--------------------------------------------\n" - "Name ""Start Y ""End Y " - "Beginning ""Offset " - "Designator\n" - "--------------------------------------------" - "--------------------------------------------\n"); - int count = 0; - for (const auto& x : db.rules) - { - if (count++ % 50 == 0) - os << title; - os << x << '\n'; - } - os << '\n'; - title = std::string("---------------------------------------------------------" - "--------------------------------------------------------\n" - "Name ""Offset " - "Rule ""Abrev ""Until\n" - "---------------------------------------------------------" - "--------------------------------------------------------\n"); - count = 0; - for (const auto& x : db.zones) - { - if (count++ % 10 == 0) - os << title; - os << x << '\n'; - } - os << '\n'; - title = std::string("---------------------------------------------------------" - "--------------------------------------------------------\n" - "Alias ""To\n" - "---------------------------------------------------------" - "--------------------------------------------------------\n"); - count = 0; - for (const auto& x : db.links) - { - if (count++ % 45 == 0) - os << title; - os << x << '\n'; - } - os << '\n'; - title = std::string("---------------------------------------------------------" - "--------------------------------------------------------\n" - "Leap second on\n" - "---------------------------------------------------------" - "--------------------------------------------------------\n"); - os << title; - for (const auto& x : db.leap_seconds) - os << x << '\n'; - return os; -} - -#endif // !USE_OS_TZDB - -// ----------------------- - -#ifdef _WIN32 - -static -std::string -getTimeZoneKeyName() -{ - DYNAMIC_TIME_ZONE_INFORMATION dtzi{}; - auto result = GetDynamicTimeZoneInformation(&dtzi); - if (result == TIME_ZONE_ID_INVALID) - throw std::runtime_error("current_zone(): GetDynamicTimeZoneInformation()" - " reported TIME_ZONE_ID_INVALID."); - auto wlen = wcslen(dtzi.TimeZoneKeyName); - char buf[128] = {}; - assert(sizeof(buf) >= wlen+1); - wcstombs(buf, dtzi.TimeZoneKeyName, wlen); - if (strcmp(buf, "Coordinated Universal Time") == 0) - return "UTC"; - return buf; -} - -const time_zone* -tzdb::current_zone() const -{ - std::string win_tzid = getTimeZoneKeyName(); - std::string standard_tzid; - if (!native_to_standard_timezone_name(win_tzid, standard_tzid)) - { - std::string msg; - msg = "current_zone() failed: A mapping from the Windows Time Zone id \""; - msg += win_tzid; - msg += "\" was not found in the time zone mapping database."; - throw std::runtime_error(msg); - } - return locate_zone(standard_tzid); -} - -#else // !_WIN32 - -#if HAS_STRING_VIEW - -static -std::string_view -extract_tz_name(char const* rp) -{ - using namespace std; - string_view result = rp; - CONSTDATA string_view zoneinfo = "zoneinfo"; - size_t pos = result.rfind(zoneinfo); - if (pos == result.npos) - throw runtime_error( - "current_zone() failed to find \"zoneinfo\" in " + string(result)); - pos = result.find('/', pos); - result.remove_prefix(pos + 1); - return result; -} - -#else // !HAS_STRING_VIEW - -static -std::string -extract_tz_name(char const* rp) -{ - using namespace std; - string result = rp; - CONSTDATA char zoneinfo[] = "zoneinfo"; - size_t pos = result.rfind(zoneinfo); - if (pos == result.npos) - throw runtime_error( - "current_zone() failed to find \"zoneinfo\" in " + result); - pos = result.find('/', pos); - result.erase(0, pos + 1); - return result; -} - -#endif // HAS_STRING_VIEW - -static -bool -sniff_realpath(const char* timezone) -{ - using namespace std; - char rp[PATH_MAX+1] = {}; - if (realpath(timezone, rp) == nullptr) - throw system_error(errno, system_category(), "realpath() failed"); - auto result = extract_tz_name(rp); - return result != "posixrules"; -} - -const time_zone* -tzdb::current_zone() const -{ - // On some OS's a file called /etc/localtime may - // exist and it may be either a real file - // containing time zone details or a symlink to such a file. - // On MacOS and BSD Unix if this file is a symlink it - // might resolve to a path like this: - // "/usr/share/zoneinfo/America/Los_Angeles" - // If it does, we try to determine the current - // timezone from the remainder of the path by removing the prefix - // and hoping the rest resolves to a valid timezone. - // It may not always work though. If it doesn't then an - // exception will be thrown by local_timezone. - // The path may also take a relative form: - // "../usr/share/zoneinfo/America/Los_Angeles". - { - struct stat sb; - CONSTDATA auto timezone = "/etc/localtime"; - if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) - { - using namespace std; - static const bool use_realpath = sniff_realpath(timezone); - char rp[PATH_MAX+1] = {}; - if (use_realpath) - { - if (realpath(timezone, rp) == nullptr) - throw system_error(errno, system_category(), "realpath() failed"); - } - else - { - if (readlink(timezone, rp, sizeof(rp)-1) <= 0) - throw system_error(errno, system_category(), "readlink() failed"); - } - return locate_zone(extract_tz_name(rp)); - } - } - // On embedded systems e.g. buildroot with uclibc the timezone is linked - // into /etc/TZ which is a symlink to path like this: - // "/usr/share/zoneinfo/uclibc/America/Los_Angeles" - // If it does, we try to determine the current - // timezone from the remainder of the path by removing the prefix - // and hoping the rest resolves to valid timezone. - // It may not always work though. If it doesn't then an - // exception will be thrown by local_timezone. - // The path may also take a relative form: - // "../usr/share/zoneinfo/uclibc/America/Los_Angeles". - { - struct stat sb; - CONSTDATA auto timezone = "/etc/TZ"; - if (lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0) { - using namespace std; - string result; - char rp[PATH_MAX+1] = {}; - if (readlink(timezone, rp, sizeof(rp)-1) > 0) - result = string(rp); - else - throw system_error(errno, system_category(), "readlink() failed"); - - const size_t pos = result.find(get_tz_dir()); - if (pos != result.npos) - result.erase(0, get_tz_dir().size() + 1 + pos); - return locate_zone(result); - } - } - { - // On some versions of some linux distro's (e.g. Ubuntu), - // the current timezone might be in the first line of - // the /etc/timezone file. - std::ifstream timezone_file("/etc/timezone"); - if (timezone_file.is_open()) - { - std::string result; - std::getline(timezone_file, result); - if (!result.empty()) - return locate_zone(result); - } - // Fall through to try other means. - } - { - // On some versions of some bsd distro's (e.g. FreeBSD), - // the current timezone might be in the first line of - // the /var/db/zoneinfo file. - std::ifstream timezone_file("/var/db/zoneinfo"); - if (timezone_file.is_open()) - { - std::string result; - std::getline(timezone_file, result); - if (!result.empty()) - return locate_zone(result); - } - // Fall through to try other means. - } - { - // On some versions of some bsd distro's (e.g. iOS), - // it is not possible to use file based approach, - // we switch to system API, calling functions in - // CoreFoundation framework. -#if TARGET_OS_IPHONE - std::string result = date::iOSUtils::get_current_timezone(); - if (!result.empty()) - return locate_zone(result); -#endif - // Fall through to try other means. - } - { - // On some versions of some linux distro's (e.g. Red Hat), - // the current timezone might be in the first line of - // the /etc/sysconfig/clock file as: - // ZONE="US/Eastern" - std::ifstream timezone_file("/etc/sysconfig/clock"); - std::string result; - while (timezone_file) - { - std::getline(timezone_file, result); - auto p = result.find("ZONE=\""); - if (p != std::string::npos) - { - result.erase(p, p+6); - result.erase(result.rfind('"')); - return locate_zone(result); - } - } - // Fall through to try other means. - } - throw std::runtime_error("Could not get current timezone"); -} - -#endif // !_WIN32 - -const time_zone* -current_zone() -{ - return get_tzdb().current_zone(); -} - -} // namespace date -} // namespace velox -} // namespace facebook - -#if defined(__GNUC__) && __GNUC__ < 5 -# pragma GCC diagnostic pop -#endif diff --git a/velox/external/date/tz.h b/velox/external/date/tz.h deleted file mode 100644 index aa6d42c8d359..000000000000 --- a/velox/external/date/tz.h +++ /dev/null @@ -1,2842 +0,0 @@ -#ifndef VELOX_TZ_H -#define VELOX_TZ_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016, 2017 Howard Hinnant -// Copyright (c) 2017 Jiangang Zhuang -// Copyright (c) 2017 Aaron Bishop -// Copyright (c) 2017 Tomasz Kamiński -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -// Get more recent database at http://www.iana.org/time-zones - -// The notion of "current timezone" is something the operating system is expected to "just -// know". How it knows this is system specific. It's often a value set by the user at OS -// installation time and recorded by the OS somewhere. On Linux and Mac systems the current -// timezone name is obtained by looking at the name or contents of a particular file on -// disk. On Windows the current timezone name comes from the registry. In either method, -// there is no guarantee that the "native" current timezone name obtained will match any -// of the "Standard" names in this library's "database". On Linux, the names usually do -// seem to match so mapping functions to map from native to "Standard" are typically not -// required. On Windows, the names are never "Standard" so mapping is always required. -// Technically any OS may use the mapping process but currently only Windows does use it. - -#ifndef USE_OS_TZDB -# define USE_OS_TZDB 0 -#endif - -#ifndef HAS_REMOTE_API -# if USE_OS_TZDB == 0 -# ifdef _WIN32 -# define HAS_REMOTE_API 0 -# else -# define HAS_REMOTE_API 1 -# endif -# else // HAS_REMOTE_API makes no since when using the OS timezone database -# define HAS_REMOTE_API 0 -# endif -#endif - -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wconstant-logical-operand" -#endif - -static_assert(!(USE_OS_TZDB && HAS_REMOTE_API), - "USE_OS_TZDB and HAS_REMOTE_API can not be used together"); - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - -#ifndef AUTO_DOWNLOAD -# define AUTO_DOWNLOAD HAS_REMOTE_API -#endif - -static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true, - "AUTO_DOWNLOAD can not be turned on without HAS_REMOTE_API"); - -#ifndef USE_SHELL_API -# define USE_SHELL_API 1 -#endif - -#if USE_OS_TZDB -# ifdef _WIN32 -# error "USE_OS_TZDB can not be used on Windows" -# endif -# ifndef MISSING_LEAP_SECONDS -# ifdef __APPLE__ -# define MISSING_LEAP_SECONDS 1 -# else -# define MISSING_LEAP_SECONDS 0 -# endif -# endif -#else -# define MISSING_LEAP_SECONDS 0 -#endif - -#ifndef HAS_DEDUCTION_GUIDES -# if __cplusplus >= 201703 -# define HAS_DEDUCTION_GUIDES 1 -# else -# define HAS_DEDUCTION_GUIDES 0 -# endif -#endif // HAS_DEDUCTION_GUIDES - -#include "date.h" - -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#include "tz_private.h" -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -# ifdef DATE_BUILD_DLL -# define DATE_API __declspec(dllexport) -# elif defined(DATE_USE_DLL) -# define DATE_API __declspec(dllimport) -# else -# define DATE_API -# endif -#else -# ifdef DATE_BUILD_DLL -# define DATE_API __attribute__ ((visibility ("default"))) -# else -# define DATE_API -# endif -#endif - -namespace facebook -{ -namespace velox -{ -namespace date -{ - -enum class choose {earliest, latest}; - -namespace detail -{ - struct undocumented; - - template - struct nodeduct - { - using type = T; - }; - - template - using nodeduct_t = typename nodeduct::type; -} - -struct sys_info -{ - sys_seconds begin; - sys_seconds end; - std::chrono::seconds offset; - std::chrono::minutes save; - std::string abbrev; -}; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const sys_info& r) -{ - os << r.begin << '\n'; - os << r.end << '\n'; - os << make_time(r.offset) << "\n"; - os << make_time(r.save) << "\n"; - os << r.abbrev << '\n'; - return os; -} - -struct local_info -{ - enum {unique, nonexistent, ambiguous} result; - sys_info first; - sys_info second; -}; - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_info& r) -{ - if (r.result == local_info::nonexistent) - os << "nonexistent between\n"; - else if (r.result == local_info::ambiguous) - os << "ambiguous between\n"; - os << r.first; - if (r.result != local_info::unique) - { - os << "and\n"; - os << r.second; - } - return os; -} - -class nonexistent_local_time - : public std::runtime_error -{ -public: - template - nonexistent_local_time(local_time tp, const local_info& i); - -private: - template - static - std::string - make_msg(local_time tp, const local_info& i); -}; - -template -inline -nonexistent_local_time::nonexistent_local_time(local_time tp, - const local_info& i) - : std::runtime_error(make_msg(tp, i)) -{ -} - -template -std::string -nonexistent_local_time::make_msg(local_time tp, const local_info& i) -{ - assert(i.result == local_info::nonexistent); - std::ostringstream os; - os << tp << " is in a gap between\n" - << local_seconds{i.first.end.time_since_epoch()} + i.first.offset << ' ' - << i.first.abbrev << " and\n" - << local_seconds{i.second.begin.time_since_epoch()} + i.second.offset << ' ' - << i.second.abbrev - << " which are both equivalent to\n" - << i.first.end << " UTC"; - return os.str(); -} - -class ambiguous_local_time - : public std::runtime_error -{ -public: - template - ambiguous_local_time(local_time tp, const local_info& i); - -private: - template - static - std::string - make_msg(local_time tp, const local_info& i); -}; - -template -inline -ambiguous_local_time::ambiguous_local_time(local_time tp, const local_info& i) - : std::runtime_error(make_msg(tp, i)) -{ -} - -template -std::string -ambiguous_local_time::make_msg(local_time tp, const local_info& i) -{ - assert(i.result == local_info::ambiguous); - std::ostringstream os; - os << tp << " is ambiguous. It could be\n" - << tp << ' ' << i.first.abbrev << " == " - << tp - i.first.offset << " UTC or\n" - << tp << ' ' << i.second.abbrev << " == " - << tp - i.second.offset << " UTC"; - return os.str(); -} - -class invalid_timezone - : public std::runtime_error -{ -public: - invalid_timezone(const std::string& tz_name); - -private: - static - std::string - make_msg(const std::string& tz_name); -}; - -inline -invalid_timezone::invalid_timezone(const std::string& tz_name) - : std::runtime_error(make_msg(tz_name)) -{ -} - -inline -std::string -invalid_timezone::make_msg(const std::string& tz_name) -{ - std::ostringstream os; - os << tz_name << " not found in timezone database"; - return os.str(); -} - -class time_zone; - -#if HAS_STRING_VIEW -DATE_API const time_zone* locate_zone(std::string_view tz_name); -#else -DATE_API const time_zone* locate_zone(const std::string& tz_name); -#endif - -DATE_API const time_zone* current_zone(); - -template -struct zoned_traits -{ -}; - -template <> -struct zoned_traits -{ - static - const time_zone* - default_zone() - { - return date::locate_zone("Etc/UTC"); - } - -#if HAS_STRING_VIEW - - static - const time_zone* - locate_zone(std::string_view name) - { - return date::locate_zone(name); - } - -#else // !HAS_STRING_VIEW - - static - const time_zone* - locate_zone(const std::string& name) - { - return date::locate_zone(name); - } - - static - const time_zone* - locate_zone(const char* name) - { - return date::locate_zone(name); - } - -#endif // !HAS_STRING_VIEW -}; - -template -class zoned_time; - -template -bool -operator==(const zoned_time& x, - const zoned_time& y); - -template -class zoned_time -{ -public: - using duration = typename std::common_type::type; - -private: - TimeZonePtr zone_; - sys_time tp_; - -public: -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::default_zone())> -#endif - zoned_time(); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::default_zone())> -#endif - zoned_time(const sys_time& st); - explicit zoned_time(TimeZonePtr z); - -#if HAS_STRING_VIEW - template ::locate_zone(std::string_view())) - >::value - >::type> - explicit zoned_time(std::string_view name); -#else -# if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())) - >::value - >::type> -# endif - explicit zoned_time(const std::string& name); -#endif - - template , - sys_time>::value - >::type> - zoned_time(const zoned_time& zt) NOEXCEPT; - - zoned_time(TimeZonePtr z, const sys_time& st); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ()->to_sys(local_time{})), - sys_time - >::value - >::type> -#endif - zoned_time(TimeZonePtr z, const local_time& tp); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ()->to_sys(local_time{}, - choose::earliest)), - sys_time - >::value - >::type> -#endif - zoned_time(TimeZonePtr z, const local_time& tp, choose c); - - template , - sys_time>::value - >::type> - zoned_time(TimeZonePtr z, const zoned_time& zt); - - template , - sys_time>::value - >::type> - zoned_time(TimeZonePtr z, const zoned_time& zt, choose); - -#if HAS_STRING_VIEW - - template ::locate_zone(std::string_view())), - sys_time - >::value - >::type> - zoned_time(std::string_view name, detail::nodeduct_t&> st); - - template ::locate_zone(std::string_view())), - local_time - >::value - >::type> - zoned_time(std::string_view name, detail::nodeduct_t&> tp); - - template ::locate_zone(std::string_view())), - local_time, - choose - >::value - >::type> - zoned_time(std::string_view name, detail::nodeduct_t&> tp, choose c); - - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string_view())), - zoned_time - >::value - >::type> - zoned_time(std::string_view name, const zoned_time& zt); - - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string_view())), - zoned_time, - choose - >::value - >::type> - zoned_time(std::string_view name, const zoned_time& zt, choose); - -#else // !HAS_STRING_VIEW - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - sys_time - >::value - >::type> -#endif - zoned_time(const std::string& name, const sys_time& st); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - sys_time - >::value - >::type> -#endif - zoned_time(const char* name, const sys_time& st); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - local_time - >::value - >::type> -#endif - zoned_time(const std::string& name, const local_time& tp); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - local_time - >::value - >::type> -#endif - zoned_time(const char* name, const local_time& tp); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - local_time, - choose - >::value - >::type> -#endif - zoned_time(const std::string& name, const local_time& tp, choose c); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template ::locate_zone(std::string())), - local_time, - choose - >::value - >::type> -#endif - zoned_time(const char* name, const local_time& tp, choose c); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string())), - zoned_time - >::value - >::type> -#else - template -#endif - zoned_time(const std::string& name, const zoned_time& zt); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string())), - zoned_time - >::value - >::type> -#else - template -#endif - zoned_time(const char* name, const zoned_time& zt); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string())), - zoned_time, - choose - >::value - >::type> -#else - template -#endif - zoned_time(const std::string& name, const zoned_time& zt, - choose); - -#if !defined(_MSC_VER) || (_MSC_VER > 1916) - template , - sys_time>::value && - std::is_constructible - < - zoned_time, - decltype(zoned_traits::locate_zone(std::string())), - zoned_time, - choose - >::value - >::type> -#else - template -#endif - zoned_time(const char* name, const zoned_time& zt, - choose); - -#endif // !HAS_STRING_VIEW - - zoned_time& operator=(const sys_time& st); - zoned_time& operator=(const local_time& ut); - - explicit operator sys_time() const; - explicit operator local_time() const; - - TimeZonePtr get_time_zone() const; - local_time get_local_time() const; - sys_time get_sys_time() const; - sys_info get_info() const; - - template - friend - bool - operator==(const zoned_time& x, - const zoned_time& y); - - template - friend - std::basic_ostream& - operator<<(std::basic_ostream& os, - const zoned_time& t); - -private: - template friend class zoned_time; -}; - -using zoned_seconds = zoned_time; - -#if HAS_DEDUCTION_GUIDES - -namespace detail -{ - template - using time_zone_representation = - std::conditional_t - < - std::is_convertible::value, - time_zone const*, - std::remove_cv_t> - >; -} - -zoned_time() - -> zoned_time; - -template -zoned_time(sys_time) - -> zoned_time>; - -template -zoned_time(TimeZonePtrOrName&&) - -> zoned_time>; - -template -zoned_time(TimeZonePtrOrName&&, sys_time) - -> zoned_time, detail::time_zone_representation>; - -template -zoned_time(TimeZonePtrOrName&&, local_time, choose = choose::earliest) - -> zoned_time, detail::time_zone_representation>; - -template -zoned_time(TimeZonePtrOrName&&, zoned_time, choose = choose::earliest) - -> zoned_time, detail::time_zone_representation>; - -#endif // HAS_DEDUCTION_GUIDES - -template -inline -bool -operator==(const zoned_time& x, - const zoned_time& y) -{ - return x.zone_ == y.zone_ && x.tp_ == y.tp_; -} - -template -inline -bool -operator!=(const zoned_time& x, - const zoned_time& y) -{ - return !(x == y); -} - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - -namespace detail -{ -# if USE_OS_TZDB - struct transition; - struct expanded_ttinfo; -# else // !USE_OS_TZDB - struct zonelet; - class Rule; -# endif // !USE_OS_TZDB -} - -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - -class time_zone -{ -private: - std::string name_; -#if USE_OS_TZDB - std::vector transitions_; - std::vector ttinfos_; - - // Stores extended OS_TZDB timezone information, in addition to possible - // repetition rules (although these are not supported yet) - struct { - std::string extended_name_; - std::string rule_start_; - std::string rule_end_; - - bool has_rules() const { - return !rule_start_.empty(); - } - } extended_info_; - -#else // !USE_OS_TZDB - std::vector zonelets_; -#endif // !USE_OS_TZDB - std::unique_ptr adjusted_; - -public: -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - time_zone(time_zone&&) = default; - time_zone& operator=(time_zone&&) = default; -#else // defined(_MSC_VER) && (_MSC_VER < 1900) - time_zone(time_zone&& src); - time_zone& operator=(time_zone&& src); -#endif // defined(_MSC_VER) && (_MSC_VER < 1900) - - DATE_API explicit time_zone(const std::string& s, detail::undocumented); - - const std::string& name() const NOEXCEPT; - - template sys_info get_info(sys_time st) const; - template local_info get_info(local_time tp) const; - - template - sys_time::type> - to_sys(local_time tp) const; - - template - sys_time::type> - to_sys(local_time tp, choose z) const; - - template - local_time::type> - to_local(sys_time tp) const; - - friend bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT; - friend bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT; - friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone& z); - -#if !USE_OS_TZDB - DATE_API void add(const std::string& s); -#endif // !USE_OS_TZDB - -private: - DATE_API sys_info get_info_impl(sys_seconds tp) const; - DATE_API local_info get_info_impl(local_seconds tp) const; - - template - sys_time::type> - to_sys_impl(local_time tp, choose z, std::false_type) const; - template - sys_time::type> - to_sys_impl(local_time tp, choose, std::true_type) const; - -#if USE_OS_TZDB - DATE_API void init() const; - DATE_API void init_impl(); - DATE_API sys_info - load_sys_info(std::vector::const_iterator i) const; - - template - DATE_API void - load_data(std::istream& inf, std::int32_t tzh_leapcnt, std::int32_t tzh_timecnt, - std::int32_t tzh_typecnt, std::int32_t tzh_charcnt); -#else // !USE_OS_TZDB - DATE_API sys_info get_info_impl(sys_seconds tp, int timezone) const; - DATE_API void adjust_infos(const std::vector& rules); - DATE_API void parse_info(std::istream& in); -#endif // !USE_OS_TZDB -}; - -#if defined(_MSC_VER) && (_MSC_VER < 1900) - -inline -time_zone::time_zone(time_zone&& src) - : name_(std::move(src.name_)) - , zonelets_(std::move(src.zonelets_)) - , adjusted_(std::move(src.adjusted_)) - {} - -inline -time_zone& -time_zone::operator=(time_zone&& src) -{ - name_ = std::move(src.name_); - zonelets_ = std::move(src.zonelets_); - adjusted_ = std::move(src.adjusted_); - return *this; -} - -#endif // defined(_MSC_VER) && (_MSC_VER < 1900) - -inline -const std::string& -time_zone::name() const NOEXCEPT -{ - return name_; -} - -template -inline -sys_info -time_zone::get_info(sys_time st) const -{ - return get_info_impl(date::floor(st)); -} - -template -inline -local_info -time_zone::get_info(local_time tp) const -{ - return get_info_impl(date::floor(tp)); -} - -template -inline -sys_time::type> -time_zone::to_sys(local_time tp) const -{ - return to_sys_impl(tp, choose{}, std::true_type{}); -} - -template -inline -sys_time::type> -time_zone::to_sys(local_time tp, choose z) const -{ - return to_sys_impl(tp, z, std::false_type{}); -} - -template -inline -local_time::type> -time_zone::to_local(sys_time tp) const -{ - using LT = local_time::type>; - auto i = get_info(tp); - return LT{(tp + i.offset).time_since_epoch()}; -} - -inline bool operator==(const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ == y.name_;} -inline bool operator< (const time_zone& x, const time_zone& y) NOEXCEPT {return x.name_ < y.name_;} - -inline bool operator!=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x == y);} -inline bool operator> (const time_zone& x, const time_zone& y) NOEXCEPT {return y < x;} -inline bool operator<=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(y < x);} -inline bool operator>=(const time_zone& x, const time_zone& y) NOEXCEPT {return !(x < y);} - -template -sys_time::type> -time_zone::to_sys_impl(local_time tp, choose z, std::false_type) const -{ - auto i = get_info(tp); - if (i.result == local_info::nonexistent) - { - return i.first.end; - } - else if (i.result == local_info::ambiguous) - { - if (z == choose::latest) - return sys_time{tp.time_since_epoch()} - i.second.offset; - } - return sys_time{tp.time_since_epoch()} - i.first.offset; -} - -template -sys_time::type> -time_zone::to_sys_impl(local_time tp, choose, std::true_type) const -{ - auto i = get_info(tp); - if (i.result == local_info::nonexistent) - throw nonexistent_local_time(tp, i); - else if (i.result == local_info::ambiguous) - throw ambiguous_local_time(tp, i); - return sys_time{tp.time_since_epoch()} - i.first.offset; -} - -#if !USE_OS_TZDB - -class time_zone_link -{ -private: - std::string name_; - std::string target_; -public: - DATE_API explicit time_zone_link(const std::string& s); - - const std::string& name() const {return name_;} - const std::string& target() const {return target_;} - - friend bool operator==(const time_zone_link& x, const time_zone_link& y) {return x.name_ == y.name_;} - friend bool operator< (const time_zone_link& x, const time_zone_link& y) {return x.name_ < y.name_;} - - friend DATE_API std::ostream& operator<<(std::ostream& os, const time_zone_link& x); -}; - -using link = time_zone_link; - -inline bool operator!=(const time_zone_link& x, const time_zone_link& y) {return !(x == y);} -inline bool operator> (const time_zone_link& x, const time_zone_link& y) {return y < x;} -inline bool operator<=(const time_zone_link& x, const time_zone_link& y) {return !(y < x);} -inline bool operator>=(const time_zone_link& x, const time_zone_link& y) {return !(x < y);} - -#endif // !USE_OS_TZDB - -#if !MISSING_LEAP_SECONDS - -class leap_second -{ -private: - sys_seconds date_; - -public: -#if USE_OS_TZDB - DATE_API explicit leap_second(const sys_seconds& s, detail::undocumented); -#else - DATE_API explicit leap_second(const std::string& s, detail::undocumented); -#endif - - sys_seconds date() const {return date_;} - - friend bool operator==(const leap_second& x, const leap_second& y) {return x.date_ == y.date_;} - friend bool operator< (const leap_second& x, const leap_second& y) {return x.date_ < y.date_;} - - template - friend - bool - operator==(const leap_second& x, const sys_time& y) - { - return x.date_ == y; - } - - template - friend - bool - operator< (const leap_second& x, const sys_time& y) - { - return x.date_ < y; - } - - template - friend - bool - operator< (const sys_time& x, const leap_second& y) - { - return x < y.date_; - } - - friend DATE_API std::ostream& operator<<(std::ostream& os, const leap_second& x); -}; - -inline bool operator!=(const leap_second& x, const leap_second& y) {return !(x == y);} -inline bool operator> (const leap_second& x, const leap_second& y) {return y < x;} -inline bool operator<=(const leap_second& x, const leap_second& y) {return !(y < x);} -inline bool operator>=(const leap_second& x, const leap_second& y) {return !(x < y);} - -template -inline -bool -operator==(const sys_time& x, const leap_second& y) -{ - return y == x; -} - -template -inline -bool -operator!=(const leap_second& x, const sys_time& y) -{ - return !(x == y); -} - -template -inline -bool -operator!=(const sys_time& x, const leap_second& y) -{ - return !(x == y); -} - -template -inline -bool -operator> (const leap_second& x, const sys_time& y) -{ - return y < x; -} - -template -inline -bool -operator> (const sys_time& x, const leap_second& y) -{ - return y < x; -} - -template -inline -bool -operator<=(const leap_second& x, const sys_time& y) -{ - return !(y < x); -} - -template -inline -bool -operator<=(const sys_time& x, const leap_second& y) -{ - return !(y < x); -} - -template -inline -bool -operator>=(const leap_second& x, const sys_time& y) -{ - return !(x < y); -} - -template -inline -bool -operator>=(const sys_time& x, const leap_second& y) -{ - return !(x < y); -} - -using leap = leap_second; - -#endif // !MISSING_LEAP_SECONDS - -#ifdef _WIN32 - -namespace detail -{ - -// The time zone mapping is modelled after this data file: -// http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml -// and the field names match the element names from the mapZone element -// of windowsZones.xml. -// The website displays this file here: -// http://www.unicode.org/cldr/charts/latest/supplemental/zone_tzid.html -// The html view is sorted before being displayed but is otherwise the same -// There is a mapping between the os centric view (in this case windows) -// the html displays uses and the generic view the xml file. -// That mapping is this: -// display column "windows" -> xml field "other". -// display column "region" -> xml field "territory". -// display column "tzid" -> xml field "type". -// This structure uses the generic terminology because it could be -// used to to support other os/native name conversions, not just windows, -// and using the same generic names helps retain the connection to the -// origin of the data that we are using. -struct timezone_mapping -{ - timezone_mapping(const char* other, const char* territory, const char* type) - : other(other), territory(territory), type(type) - { - } - timezone_mapping() = default; - std::string other; - std::string territory; - std::string type; -}; - -} // detail - -#endif // _WIN32 - -struct tzdb -{ - std::string version = "unknown"; - std::vector zones; -#if !USE_OS_TZDB - std::vector links; -#endif -#if !MISSING_LEAP_SECONDS - std::vector leap_seconds; -#endif -#if !USE_OS_TZDB - std::vector rules; -#endif -#ifdef _WIN32 - std::vector mappings; -#endif - tzdb* next = nullptr; - - tzdb() = default; -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - tzdb(tzdb&&) = default; - tzdb& operator=(tzdb&&) = default; -#else // defined(_MSC_VER) && (_MSC_VER < 1900) - tzdb(tzdb&& src) - : version(std::move(src.version)) - , zones(std::move(src.zones)) - , links(std::move(src.links)) - , leap_seconds(std::move(src.leap_seconds)) - , rules(std::move(src.rules)) - , mappings(std::move(src.mappings)) - {} - - tzdb& operator=(tzdb&& src) - { - version = std::move(src.version); - zones = std::move(src.zones); - links = std::move(src.links); - leap_seconds = std::move(src.leap_seconds); - rules = std::move(src.rules); - mappings = std::move(src.mappings); - return *this; - } -#endif // defined(_MSC_VER) && (_MSC_VER < 1900) - -#if HAS_STRING_VIEW - const time_zone* locate_zone(std::string_view tz_name) const; -#else - const time_zone* locate_zone(const std::string& tz_name) const; -#endif - const time_zone* current_zone() const; -}; - -using TZ_DB = tzdb; - -DATE_API std::ostream& -operator<<(std::ostream& os, const tzdb& db); - -DATE_API const tzdb& get_tzdb(); - -std::vector get_time_zone_names(); - -class tzdb_list -{ - std::atomic head_{nullptr}; - -public: - ~tzdb_list(); - tzdb_list() = default; - tzdb_list(tzdb_list&& x) noexcept; - - const tzdb& front() const noexcept {return *head_;} - tzdb& front() noexcept {return *head_;} - - class const_iterator; - - const_iterator begin() const noexcept; - const_iterator end() const noexcept; - - const_iterator cbegin() const noexcept; - const_iterator cend() const noexcept; - - const_iterator erase_after(const_iterator p) noexcept; - - struct undocumented_helper; -private: - void push_front(tzdb* tzdb) noexcept; -}; - -class tzdb_list::const_iterator -{ - tzdb* p_ = nullptr; - - explicit const_iterator(tzdb* p) noexcept : p_{p} {} -public: - const_iterator() = default; - - using iterator_category = std::forward_iterator_tag; - using value_type = tzdb; - using reference = const value_type&; - using pointer = const value_type*; - using difference_type = std::ptrdiff_t; - - reference operator*() const noexcept {return *p_;} - pointer operator->() const noexcept {return p_;} - - const_iterator& operator++() noexcept {p_ = p_->next; return *this;} - const_iterator operator++(int) noexcept {auto t = *this; ++(*this); return t;} - - friend - bool - operator==(const const_iterator& x, const const_iterator& y) noexcept - {return x.p_ == y.p_;} - - friend - bool - operator!=(const const_iterator& x, const const_iterator& y) noexcept - {return !(x == y);} - - friend class tzdb_list; -}; - -inline -tzdb_list::const_iterator -tzdb_list::begin() const noexcept -{ - return const_iterator{head_}; -} - -inline -tzdb_list::const_iterator -tzdb_list::end() const noexcept -{ - return const_iterator{nullptr}; -} - -inline -tzdb_list::const_iterator -tzdb_list::cbegin() const noexcept -{ - return begin(); -} - -inline -tzdb_list::const_iterator -tzdb_list::cend() const noexcept -{ - return end(); -} - -DATE_API tzdb_list& get_tzdb_list(); - -#if !USE_OS_TZDB - -DATE_API const tzdb& reload_tzdb(); -DATE_API void set_install(const std::string& install); - -#endif // !USE_OS_TZDB - -#if HAS_REMOTE_API - -DATE_API std::string remote_version(); -// if provided error_buffer size should be at least CURL_ERROR_SIZE -DATE_API bool remote_download(const std::string& version, char* error_buffer = nullptr); -DATE_API bool remote_install(const std::string& version); - -#endif - -// zoned_time - -namespace detail -{ - -template -inline -T* -to_raw_pointer(T* p) noexcept -{ - return p; -} - -template -inline -auto -to_raw_pointer(Pointer p) noexcept - -> decltype(detail::to_raw_pointer(p.operator->())) -{ - return detail::to_raw_pointer(p.operator->()); -} - -} // namespace detail - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time() - : zone_(zoned_traits::default_zone()) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const sys_time& st) - : zone_(zoned_traits::default_zone()) - , tp_(st) - {} - -template -inline -zoned_time::zoned_time(TimeZonePtr z) - : zone_(std::move(z)) - {assert(detail::to_raw_pointer(zone_) != nullptr);} - -#if HAS_STRING_VIEW - -template -template -inline -zoned_time::zoned_time(std::string_view name) - : zoned_time(zoned_traits::locate_zone(name)) - {} - -#else // !HAS_STRING_VIEW - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const std::string& name) - : zoned_time(zoned_traits::locate_zone(name)) - {} - -#endif // !HAS_STRING_VIEW - -template -template -inline -zoned_time::zoned_time(const zoned_time& zt) NOEXCEPT - : zone_(zt.zone_) - , tp_(zt.tp_) - {} - -template -inline -zoned_time::zoned_time(TimeZonePtr z, const sys_time& st) - : zone_(std::move(z)) - , tp_(st) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(TimeZonePtr z, const local_time& t) - : zone_(std::move(z)) - , tp_(zone_->to_sys(t)) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(TimeZonePtr z, const local_time& t, - choose c) - : zone_(std::move(z)) - , tp_(zone_->to_sys(t, c)) - {} - -template -template -inline -zoned_time::zoned_time(TimeZonePtr z, - const zoned_time& zt) - : zone_(std::move(z)) - , tp_(zt.tp_) - {} - -template -template -inline -zoned_time::zoned_time(TimeZonePtr z, - const zoned_time& zt, choose) - : zoned_time(std::move(z), zt) - {} - -#if HAS_STRING_VIEW - -template -template -inline -zoned_time::zoned_time(std::string_view name, - detail::nodeduct_t&> st) - : zoned_time(zoned_traits::locate_zone(name), st) - {} - -template -template -inline -zoned_time::zoned_time(std::string_view name, - detail::nodeduct_t&> t) - : zoned_time(zoned_traits::locate_zone(name), t) - {} - -template -template -inline -zoned_time::zoned_time(std::string_view name, - detail::nodeduct_t&> t, choose c) - : zoned_time(zoned_traits::locate_zone(name), t, c) - {} - -template -template -inline -zoned_time::zoned_time(std::string_view name, - const zoned_time& zt) - : zoned_time(zoned_traits::locate_zone(name), zt) - {} - -template -template -inline -zoned_time::zoned_time(std::string_view name, - const zoned_time& zt, - choose c) - : zoned_time(zoned_traits::locate_zone(name), zt, c) - {} - -#else // !HAS_STRING_VIEW - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const sys_time& st) - : zoned_time(zoned_traits::locate_zone(name), st) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const char* name, - const sys_time& st) - : zoned_time(zoned_traits::locate_zone(name), st) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const local_time& t) - : zoned_time(zoned_traits::locate_zone(name), t) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const char* name, - const local_time& t) - : zoned_time(zoned_traits::locate_zone(name), t) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const local_time& t, choose c) - : zoned_time(zoned_traits::locate_zone(name), t, c) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#endif -inline -zoned_time::zoned_time(const char* name, - const local_time& t, choose c) - : zoned_time(zoned_traits::locate_zone(name), t, c) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#else -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const zoned_time& zt) - : zoned_time(zoned_traits::locate_zone(name), zt) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#else -template -#endif -inline -zoned_time::zoned_time(const char* name, - const zoned_time& zt) - : zoned_time(zoned_traits::locate_zone(name), zt) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#else -template -#endif -inline -zoned_time::zoned_time(const std::string& name, - const zoned_time& zt, - choose c) - : zoned_time(zoned_traits::locate_zone(name), zt, c) - {} - -template -#if !defined(_MSC_VER) || (_MSC_VER > 1916) -template -#else -template -#endif -inline -zoned_time::zoned_time(const char* name, - const zoned_time& zt, - choose c) - : zoned_time(zoned_traits::locate_zone(name), zt, c) - {} - -#endif // HAS_STRING_VIEW - -template -inline -zoned_time& -zoned_time::operator=(const sys_time& st) -{ - tp_ = st; - return *this; -} - -template -inline -zoned_time& -zoned_time::operator=(const local_time& ut) -{ - tp_ = zone_->to_sys(ut); - return *this; -} - -template -inline -zoned_time::operator local_time::duration>() const -{ - return get_local_time(); -} - -template -inline -zoned_time::operator sys_time::duration>() const -{ - return get_sys_time(); -} - -template -inline -TimeZonePtr -zoned_time::get_time_zone() const -{ - return zone_; -} - -template -inline -local_time::duration> -zoned_time::get_local_time() const -{ - return zone_->to_local(tp_); -} - -template -inline -sys_time::duration> -zoned_time::get_sys_time() const -{ - return tp_; -} - -template -inline -sys_info -zoned_time::get_info() const -{ - return zone_->get_info(tp_); -} - -// make_zoned_time - -inline -zoned_time -make_zoned() -{ - return zoned_time(); -} - -template -inline -zoned_time::type> -make_zoned(const sys_time& tp) -{ - return zoned_time::type>(tp); -} - -template 1916) -#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) - , class = typename std::enable_if - < - std::is_class - < - typename std::decay - < - decltype(*detail::to_raw_pointer(std::declval())) - >::type - >{} - >::type -#endif -#endif - > -inline -zoned_time -make_zoned(TimeZonePtr z) -{ - return zoned_time(std::move(z)); -} - -inline -zoned_seconds -make_zoned(const std::string& name) -{ - return zoned_seconds(name); -} - -template 1916) -#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) - , class = typename std::enable_if - < - std::is_class())>::type>{} - >::type -#endif -#endif - > -inline -zoned_time::type, TimeZonePtr> -make_zoned(TimeZonePtr zone, const local_time& tp) -{ - return zoned_time::type, - TimeZonePtr>(std::move(zone), tp); -} - -template 1916) -#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) - , class = typename std::enable_if - < - std::is_class())>::type>{} - >::type -#endif -#endif - > -inline -zoned_time::type, TimeZonePtr> -make_zoned(TimeZonePtr zone, const local_time& tp, choose c) -{ - return zoned_time::type, - TimeZonePtr>(std::move(zone), tp, c); -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const local_time& tp) -{ - return zoned_time::type>(name, tp); -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const local_time& tp, choose c) -{ - return zoned_time::type>(name, tp, c); -} - -template -inline -zoned_time -make_zoned(TimeZonePtr zone, const zoned_time& zt) -{ - return zoned_time(std::move(zone), zt); -} - -template -inline -zoned_time -make_zoned(const std::string& name, const zoned_time& zt) -{ - return zoned_time(name, zt); -} - -template -inline -zoned_time -make_zoned(TimeZonePtr zone, const zoned_time& zt, choose c) -{ - return zoned_time(std::move(zone), zt, c); -} - -template -inline -zoned_time -make_zoned(const std::string& name, const zoned_time& zt, choose c) -{ - return zoned_time(name, zt, c); -} - -template 1916) -#if !defined(__INTEL_COMPILER) || (__INTEL_COMPILER > 1600) - , class = typename std::enable_if - < - std::is_class())>::type>{} - >::type -#endif -#endif - > -inline -zoned_time::type, TimeZonePtr> -make_zoned(TimeZonePtr zone, const sys_time& st) -{ - return zoned_time::type, - TimeZonePtr>(std::move(zone), st); -} - -template -inline -zoned_time::type> -make_zoned(const std::string& name, const sys_time& st) -{ - return zoned_time::type>(name, st); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const zoned_time& tp) -{ - using duration = typename zoned_time::duration; - using LT = local_time; - auto const st = tp.get_sys_time(); - auto const info = tp.get_time_zone()->get_info(st); - return to_stream(os, fmt, LT{(st+info.offset).time_since_epoch()}, - &info.abbrev, &info.offset); -} - -template -inline -std::basic_ostream& -operator<<(std::basic_ostream& os, const zoned_time& t) -{ - const CharT fmt[] = {'%', 'F', ' ', '%', 'T', ' ', '%', 'Z', CharT{}}; - return to_stream(os, fmt, t); -} - -#if !MISSING_LEAP_SECONDS - -class utc_clock -{ -public: - using duration = std::chrono::system_clock::duration; - using rep = duration::rep; - using period = duration::period; - using time_point = std::chrono::time_point; - static CONSTDATA bool is_steady = false; - - static time_point now(); - - template - static - std::chrono::time_point::type> - to_sys(const std::chrono::time_point&); - - template - static - std::chrono::time_point::type> - from_sys(const std::chrono::time_point&); - - template - static - std::chrono::time_point::type> - to_local(const std::chrono::time_point&); - - template - static - std::chrono::time_point::type> - from_local(const std::chrono::time_point&); -}; - -template - using utc_time = std::chrono::time_point; - -using utc_seconds = utc_time; - -template -utc_time::type> -utc_clock::from_sys(const sys_time& st) -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - auto const& leaps = get_tzdb().leap_seconds; - auto const lt = std::upper_bound(leaps.begin(), leaps.end(), st); - return utc_time{st.time_since_epoch() + seconds{lt-leaps.begin()}}; -} - -// Return pair -// first is true if ut is during a leap second insertion, otherwise false. -// If ut is during a leap second insertion, that leap second is included in the count -template -std::pair -is_leap_second(date::utc_time const& ut) -{ - using std::chrono::seconds; - using duration = typename std::common_type::type; - auto const& leaps = get_tzdb().leap_seconds; - auto tp = sys_time{ut.time_since_epoch()}; - auto const lt = std::upper_bound(leaps.begin(), leaps.end(), tp); - auto ds = seconds{lt-leaps.begin()}; - tp -= ds; - auto ls = false; - if (lt > leaps.begin()) - { - if (tp < lt[-1]) - { - if (tp >= lt[-1].date() - seconds{1}) - ls = true; - else - --ds; - } - } - return {ls, ds}; -} - -struct leap_second_info -{ - bool is_leap_second; - std::chrono::seconds elapsed; -}; - -template -leap_second_info -get_leap_second_info(date::utc_time const& ut) -{ - auto p = is_leap_second(ut); - return {p.first, p.second}; -} - -template -sys_time::type> -utc_clock::to_sys(const utc_time& ut) -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - auto ls = is_leap_second(ut); - auto tp = sys_time{ut.time_since_epoch() - ls.second}; - if (ls.first) - tp = floor(tp) + seconds{1} - CD{1}; - return tp; -} - -inline -utc_clock::time_point -utc_clock::now() -{ - return from_sys(std::chrono::system_clock::now()); -} - -template -utc_time::type> -utc_clock::from_local(const local_time& st) -{ - return from_sys(sys_time{st.time_since_epoch()}); -} - -template -local_time::type> -utc_clock::to_local(const utc_time& ut) -{ - using CD = typename std::common_type::type; - return local_time{to_sys(ut).time_since_epoch()}; -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const utc_time& t) -{ - using std::chrono::seconds; - using CT = typename std::common_type::type; - const std::string abbrev("UTC"); - CONSTDATA seconds offset{0}; - auto ls = is_leap_second(t); - auto tp = sys_time{t.time_since_epoch() - ls.second}; - auto const sd = floor(tp); - year_month_day ymd = sd; - auto time = make_time(tp - sys_seconds{sd}); - time.seconds(detail::undocumented{}) += seconds{ls.first}; - fields fds{ymd, time}; - return to_stream(os, fmt, fds, &abbrev, &offset); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const utc_time& t) -{ - const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; - return to_stream(os, fmt, t); -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - utc_time& tp, std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - using std::chrono::seconds; - using std::chrono::minutes; - using CT = typename std::common_type::type; - minutes offset_local{}; - auto offptr = offset ? offset : &offset_local; - fields fds{}; - fds.has_tod = true; - from_stream(is, fmt, fds, abbrev, offptr); - if (!fds.ymd.ok()) - is.setstate(std::ios::failbit); - if (!is.fail()) - { - bool is_60_sec = fds.tod.seconds() == seconds{60}; - if (is_60_sec) - fds.tod.seconds(detail::undocumented{}) -= seconds{1}; - auto tmp = utc_clock::from_sys(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); - if (is_60_sec) - tmp += seconds{1}; - if (is_60_sec != is_leap_second(tmp).first || !fds.tod.in_conventional_range()) - { - is.setstate(std::ios::failbit); - return is; - } - tp = std::chrono::time_point_cast(tmp); - } - return is; -} - -// tai_clock - -class tai_clock -{ -public: - using duration = std::chrono::system_clock::duration; - using rep = duration::rep; - using period = duration::period; - using time_point = std::chrono::time_point; - static const bool is_steady = false; - - static time_point now(); - - template - static - std::chrono::time_point::type> - to_utc(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - from_utc(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - to_local(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - from_local(const std::chrono::time_point&) NOEXCEPT; -}; - -template - using tai_time = std::chrono::time_point; - -using tai_seconds = tai_time; - -template -inline -utc_time::type> -tai_clock::to_utc(const tai_time& t) NOEXCEPT -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - return utc_time{t.time_since_epoch()} - - (sys_days(year{1970}/January/1) - sys_days(year{1958}/January/1) + seconds{10}); -} - -template -inline -tai_time::type> -tai_clock::from_utc(const utc_time& t) NOEXCEPT -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - return tai_time{t.time_since_epoch()} + - (sys_days(year{1970}/January/1) - sys_days(year{1958}/January/1) + seconds{10}); -} - -inline -tai_clock::time_point -tai_clock::now() -{ - return from_utc(utc_clock::now()); -} - -template -inline -local_time::type> -tai_clock::to_local(const tai_time& t) NOEXCEPT -{ - using CD = typename std::common_type::type; - return local_time{t.time_since_epoch()} - - (local_days(year{1970}/January/1) - local_days(year{1958}/January/1)); -} - -template -inline -tai_time::type> -tai_clock::from_local(const local_time& t) NOEXCEPT -{ - using CD = typename std::common_type::type; - return tai_time{t.time_since_epoch()} + - (local_days(year{1970}/January/1) - local_days(year{1958}/January/1)); -} - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const tai_time& t) -{ - const std::string abbrev("TAI"); - CONSTDATA std::chrono::seconds offset{0}; - return to_stream(os, fmt, tai_clock::to_local(t), &abbrev, &offset); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const tai_time& t) -{ - const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; - return to_stream(os, fmt, t); -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - tai_time& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - local_time lp; - from_stream(is, fmt, lp, abbrev, offset); - if (!is.fail()) - tp = tai_clock::from_local(lp); - return is; -} - -// gps_clock - -class gps_clock -{ -public: - using duration = std::chrono::system_clock::duration; - using rep = duration::rep; - using period = duration::period; - using time_point = std::chrono::time_point; - static const bool is_steady = false; - - static time_point now(); - - template - static - std::chrono::time_point::type> - to_utc(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - from_utc(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - to_local(const std::chrono::time_point&) NOEXCEPT; - - template - static - std::chrono::time_point::type> - from_local(const std::chrono::time_point&) NOEXCEPT; -}; - -template - using gps_time = std::chrono::time_point; - -using gps_seconds = gps_time; - -template -inline -utc_time::type> -gps_clock::to_utc(const gps_time& t) NOEXCEPT -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - return utc_time{t.time_since_epoch()} + - (sys_days(year{1980}/January/Sunday[1]) - sys_days(year{1970}/January/1) + - seconds{9}); -} - -template -inline -gps_time::type> -gps_clock::from_utc(const utc_time& t) NOEXCEPT -{ - using std::chrono::seconds; - using CD = typename std::common_type::type; - return gps_time{t.time_since_epoch()} - - (sys_days(year{1980}/January/Sunday[1]) - sys_days(year{1970}/January/1) + - seconds{9}); -} - -inline -gps_clock::time_point -gps_clock::now() -{ - return from_utc(utc_clock::now()); -} - -template -inline -local_time::type> -gps_clock::to_local(const gps_time& t) NOEXCEPT -{ - using CD = typename std::common_type::type; - return local_time{t.time_since_epoch()} + - (local_days(year{1980}/January/Sunday[1]) - local_days(year{1970}/January/1)); -} - -template -inline -gps_time::type> -gps_clock::from_local(const local_time& t) NOEXCEPT -{ - using CD = typename std::common_type::type; - return gps_time{t.time_since_epoch()} - - (local_days(year{1980}/January/Sunday[1]) - local_days(year{1970}/January/1)); -} - - -template -std::basic_ostream& -to_stream(std::basic_ostream& os, const CharT* fmt, - const gps_time& t) -{ - const std::string abbrev("GPS"); - CONSTDATA std::chrono::seconds offset{0}; - return to_stream(os, fmt, gps_clock::to_local(t), &abbrev, &offset); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const gps_time& t) -{ - const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}}; - return to_stream(os, fmt, t); -} - -template > -std::basic_istream& -from_stream(std::basic_istream& is, const CharT* fmt, - gps_time& tp, - std::basic_string* abbrev = nullptr, - std::chrono::minutes* offset = nullptr) -{ - local_time lp; - from_stream(is, fmt, lp, abbrev, offset); - if (!is.fail()) - tp = gps_clock::from_local(lp); - return is; -} - -// clock_time_conversion - -template -struct clock_time_conversion -{}; - -template <> -struct clock_time_conversion -{ - template - CONSTCD14 - sys_time - operator()(const sys_time& st) const - { - return st; - } -}; - -template <> -struct clock_time_conversion -{ - template - CONSTCD14 - utc_time - operator()(const utc_time& ut) const - { - return ut; - } -}; - -template<> -struct clock_time_conversion -{ - template - CONSTCD14 - local_time - operator()(const local_time& lt) const - { - return lt; - } -}; - -template <> -struct clock_time_conversion -{ - template - utc_time::type> - operator()(const sys_time& st) const - { - return utc_clock::from_sys(st); - } -}; - -template <> -struct clock_time_conversion -{ - template - sys_time::type> - operator()(const utc_time& ut) const - { - return utc_clock::to_sys(ut); - } -}; - -template<> -struct clock_time_conversion -{ - template - CONSTCD14 - local_time - operator()(const sys_time& st) const - { - return local_time{st.time_since_epoch()}; - } -}; - -template<> -struct clock_time_conversion -{ - template - CONSTCD14 - sys_time - operator()(const local_time& lt) const - { - return sys_time{lt.time_since_epoch()}; - } -}; - -template<> -struct clock_time_conversion -{ - template - utc_time::type> - operator()(const local_time& lt) const - { - return utc_clock::from_local(lt); - } -}; - -template<> -struct clock_time_conversion -{ - template - local_time::type> - operator()(const utc_time& ut) const - { - return utc_clock::to_local(ut); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - std::chrono::time_point - operator()(const std::chrono::time_point& tp) const - { - return tp; - } -}; - -namespace ctc_detail -{ - -template - using time_point = std::chrono::time_point; - -using std::declval; -using std::chrono::system_clock; - -//Check if TimePoint is time for given clock, -//if not emits hard error -template -struct return_clock_time -{ - using clock_time_point = time_point; - using type = TimePoint; - - static_assert(std::is_same::value, - "time point with appropariate clock shall be returned"); -}; - -// Check if Clock has to_sys method accepting TimePoint with given duration const& and -// returning sys_time. If so has nested type member equal to return type to_sys. -template -struct return_to_sys -{}; - -template -struct return_to_sys - < - Clock, Duration, - decltype(Clock::to_sys(declval const&>()), void()) - > - : return_clock_time - < - system_clock, - decltype(Clock::to_sys(declval const&>())) - > -{}; - -// Similar to above -template -struct return_from_sys -{}; - -template -struct return_from_sys - < - Clock, Duration, - decltype(Clock::from_sys(declval const&>()), - void()) - > - : return_clock_time - < - Clock, - decltype(Clock::from_sys(declval const&>())) - > -{}; - -// Similar to above -template -struct return_to_utc -{}; - -template -struct return_to_utc - < - Clock, Duration, - decltype(Clock::to_utc(declval const&>()), void()) - > - : return_clock_time - < - utc_clock, - decltype(Clock::to_utc(declval const&>()))> -{}; - -// Similar to above -template -struct return_from_utc -{}; - -template -struct return_from_utc - < - Clock, Duration, - decltype(Clock::from_utc(declval const&>()), - void()) - > - : return_clock_time - < - Clock, - decltype(Clock::from_utc(declval const&>())) - > -{}; - -// Similar to above -template -struct return_to_local -{}; - -template -struct return_to_local - < - Clock, Duration, - decltype(Clock::to_local(declval const&>()), - void()) - > - : return_clock_time - < - local_t, - decltype(Clock::to_local(declval const&>())) - > -{}; - -// Similar to above -template -struct return_from_local -{}; - -template -struct return_from_local - < - Clock, Duration, - decltype(Clock::from_local(declval const&>()), - void()) - > - : return_clock_time - < - Clock, - decltype(Clock::from_local(declval const&>())) - > -{}; - -} // namespace ctc_detail - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_to_sys::type - operator()(const std::chrono::time_point& tp) const - { - return SrcClock::to_sys(tp); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_from_sys::type - operator()(const sys_time& st) const - { - return DstClock::from_sys(st); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_to_utc::type - operator()(const std::chrono::time_point& tp) const - { - return SrcClock::to_utc(tp); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_from_utc::type - operator()(const utc_time& ut) const - { - return DstClock::from_utc(ut); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_to_local::type - operator()(const std::chrono::time_point& tp) const - { - return SrcClock::to_local(tp); - } -}; - -template -struct clock_time_conversion -{ - template - CONSTCD14 - typename ctc_detail::return_from_local::type - operator()(const local_time& lt) const - { - return DstClock::from_local(lt); - } -}; - -namespace clock_cast_detail -{ - -template - using time_point = std::chrono::time_point; -using std::chrono::system_clock; - -template -CONSTCD14 -auto -conv_clock(const time_point& t) - -> decltype(std::declval>()(t)) -{ - return clock_time_conversion{}(t); -} - -//direct trait conversion, 1st candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, const time_point*) - -> decltype(conv_clock(t)) -{ - return conv_clock(t); -} - -//conversion through sys, 2nd candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, const void*) - -> decltype(conv_clock(conv_clock(t))) -{ - return conv_clock(conv_clock(t)); -} - -//conversion through utc, 2nd candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, const void*) - -> decltype(0, // MSVC_WORKAROUND - conv_clock(conv_clock(t))) -{ - return conv_clock(conv_clock(t)); -} - -//conversion through sys and utc, 3rd candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, ...) - -> decltype(conv_clock(conv_clock(conv_clock(t)))) -{ - return conv_clock(conv_clock(conv_clock(t))); -} - -//conversion through utc and sys, 3rd candidate -template -CONSTCD14 -auto -cc_impl(const time_point& t, ...) - -> decltype(0, // MSVC_WORKAROUND - conv_clock(conv_clock(conv_clock(t)))) -{ - return conv_clock(conv_clock(conv_clock(t))); -} - -} // namespace clock_cast_detail - -template -CONSTCD14 -auto -clock_cast(const std::chrono::time_point& tp) - -> decltype(clock_cast_detail::cc_impl(tp, &tp)) -{ - return clock_cast_detail::cc_impl(tp, &tp); -} - -// Deprecated API - -template -inline -sys_time::type> -to_sys_time(const utc_time& t) -{ - return utc_clock::to_sys(t); -} - -template -inline -sys_time::type> -to_sys_time(const tai_time& t) -{ - return utc_clock::to_sys(tai_clock::to_utc(t)); -} - -template -inline -sys_time::type> -to_sys_time(const gps_time& t) -{ - return utc_clock::to_sys(gps_clock::to_utc(t)); -} - - -template -inline -utc_time::type> -to_utc_time(const sys_time& t) -{ - return utc_clock::from_sys(t); -} - -template -inline -utc_time::type> -to_utc_time(const tai_time& t) -{ - return tai_clock::to_utc(t); -} - -template -inline -utc_time::type> -to_utc_time(const gps_time& t) -{ - return gps_clock::to_utc(t); -} - - -template -inline -tai_time::type> -to_tai_time(const sys_time& t) -{ - return tai_clock::from_utc(utc_clock::from_sys(t)); -} - -template -inline -tai_time::type> -to_tai_time(const utc_time& t) -{ - return tai_clock::from_utc(t); -} - -template -inline -tai_time::type> -to_tai_time(const gps_time& t) -{ - return tai_clock::from_utc(gps_clock::to_utc(t)); -} - - -template -inline -gps_time::type> -to_gps_time(const sys_time& t) -{ - return gps_clock::from_utc(utc_clock::from_sys(t)); -} - -template -inline -gps_time::type> -to_gps_time(const utc_time& t) -{ - return gps_clock::from_utc(t); -} - -template -inline -gps_time::type> -to_gps_time(const tai_time& t) -{ - return gps_clock::from_utc(tai_clock::to_utc(t)); -} - -#endif // !MISSING_LEAP_SECONDS - -} // namespace date -} // namespace velox -} // namespace facebook - -#endif // VELOX_TZ_H diff --git a/velox/external/date/tz_private.h b/velox/external/date/tz_private.h deleted file mode 100644 index b85a617f2d6d..000000000000 --- a/velox/external/date/tz_private.h +++ /dev/null @@ -1,326 +0,0 @@ -#ifndef VELOX_TZ_PRIVATE_H -#define VELOX_TZ_PRIVATE_H - -// The MIT License (MIT) -// -// Copyright (c) 2015, 2016 Howard Hinnant -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// -// Our apologies. When the previous paragraph was written, lowercase had not yet -// been invented (that would involve another several millennia of evolution). -// We did not mean to shout. - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) -#include "tz.h" -#else -#include "date.h" -#include -#endif - -namespace facebook -{ - -namespace velox -{ - -namespace date -{ - -namespace detail -{ - -#if !USE_OS_TZDB - -enum class tz {utc, local, standard}; - -//forward declare to avoid warnings in gcc 6.2 -class MonthDayTime; -std::istream& operator>>(std::istream& is, MonthDayTime& x); -std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); - - -class MonthDayTime -{ -private: - struct pair - { -#if defined(_MSC_VER) && (_MSC_VER < 1900) - pair() : month_day_(date::jan / 1), weekday_(0U) {} - - pair(const date::month_day& month_day, const date::weekday& weekday) - : month_day_(month_day), weekday_(weekday) {} -#endif - - date::month_day month_day_; - date::weekday weekday_; - }; - - enum Type {month_day, month_last_dow, lteq, gteq}; - - Type type_{month_day}; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - union U -#else - struct U -#endif - { - date::month_day month_day_; - date::month_weekday_last month_weekday_last_; - pair month_day_weekday_; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - U() : month_day_{date::jan/1} {} -#else - U() : - month_day_(date::jan/1), - month_weekday_last_(date::month(0U), date::weekday_last(date::weekday(0U))) - {} - -#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) - - U& operator=(const date::month_day& x); - U& operator=(const date::month_weekday_last& x); - U& operator=(const pair& x); - } u; - - std::chrono::hours h_{}; - std::chrono::minutes m_{}; - std::chrono::seconds s_{}; - tz zone_{tz::local}; - -public: - MonthDayTime() = default; - MonthDayTime(local_seconds tp, tz timezone); - MonthDayTime(const date::month_day& md, tz timezone); - - date::day day() const; - date::month month() const; - tz zone() const {return zone_;} - - void canonicalize(date::year y); - - sys_seconds - to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const; - sys_days to_sys_days(date::year y) const; - - sys_seconds to_time_point(date::year y) const; - int compare(date::year y, const MonthDayTime& x, date::year yx, - std::chrono::seconds offset, std::chrono::minutes prev_save) const; - - friend std::istream& operator>>(std::istream& is, MonthDayTime& x); - friend std::ostream& operator<<(std::ostream& os, const MonthDayTime& x); -}; - -// A Rule specifies one or more set of datetimes without using an offset. -// Multiple dates are specified with multiple years. The years in effect -// go from starting_year_ to ending_year_, inclusive. starting_year_ <= -// ending_year_. save_ is in effect for times from the specified time -// onward, including the specified time. When the specified time is -// local, it uses the save_ from the chronologically previous Rule, or if -// there is none, 0. - -//forward declare to avoid warnings in gcc 6.2 -class Rule; -bool operator==(const Rule& x, const Rule& y); -bool operator<(const Rule& x, const Rule& y); -bool operator==(const Rule& x, const date::year& y); -bool operator<(const Rule& x, const date::year& y); -bool operator==(const date::year& x, const Rule& y); -bool operator<(const date::year& x, const Rule& y); -bool operator==(const Rule& x, const std::string& y); -bool operator<(const Rule& x, const std::string& y); -bool operator==(const std::string& x, const Rule& y); -bool operator<(const std::string& x, const Rule& y); -std::ostream& operator<<(std::ostream& os, const Rule& r); - -class Rule -{ -private: - std::string name_; - date::year starting_year_{0}; - date::year ending_year_{0}; - MonthDayTime starting_at_; - std::chrono::minutes save_{0}; - std::string abbrev_; - -public: - Rule() = default; - explicit Rule(const std::string& s); - Rule(const Rule& r, date::year starting_year, date::year ending_year); - - const std::string& name() const {return name_;} - const std::string& abbrev() const {return abbrev_;} - - const MonthDayTime& mdt() const {return starting_at_;} - const date::year& starting_year() const {return starting_year_;} - const date::year& ending_year() const {return ending_year_;} - const std::chrono::minutes& save() const {return save_;} - - static void split_overlaps(std::vector& rules); - - friend bool operator==(const Rule& x, const Rule& y); - friend bool operator<(const Rule& x, const Rule& y); - friend bool operator==(const Rule& x, const date::year& y); - friend bool operator<(const Rule& x, const date::year& y); - friend bool operator==(const date::year& x, const Rule& y); - friend bool operator<(const date::year& x, const Rule& y); - friend bool operator==(const Rule& x, const std::string& y); - friend bool operator<(const Rule& x, const std::string& y); - friend bool operator==(const std::string& x, const Rule& y); - friend bool operator<(const std::string& x, const Rule& y); - - friend std::ostream& operator<<(std::ostream& os, const Rule& r); - -private: - date::day day() const; - date::month month() const; - static void split_overlaps(std::vector& rules, std::size_t i, std::size_t& e); - static bool overlaps(const Rule& x, const Rule& y); - static void split(std::vector& rules, std::size_t i, std::size_t k, - std::size_t& e); -}; - -inline bool operator!=(const Rule& x, const Rule& y) {return !(x == y);} -inline bool operator> (const Rule& x, const Rule& y) {return y < x;} -inline bool operator<=(const Rule& x, const Rule& y) {return !(y < x);} -inline bool operator>=(const Rule& x, const Rule& y) {return !(x < y);} - -inline bool operator!=(const Rule& x, const date::year& y) {return !(x == y);} -inline bool operator> (const Rule& x, const date::year& y) {return y < x;} -inline bool operator<=(const Rule& x, const date::year& y) {return !(y < x);} -inline bool operator>=(const Rule& x, const date::year& y) {return !(x < y);} - -inline bool operator!=(const date::year& x, const Rule& y) {return !(x == y);} -inline bool operator> (const date::year& x, const Rule& y) {return y < x;} -inline bool operator<=(const date::year& x, const Rule& y) {return !(y < x);} -inline bool operator>=(const date::year& x, const Rule& y) {return !(x < y);} - -inline bool operator!=(const Rule& x, const std::string& y) {return !(x == y);} -inline bool operator> (const Rule& x, const std::string& y) {return y < x;} -inline bool operator<=(const Rule& x, const std::string& y) {return !(y < x);} -inline bool operator>=(const Rule& x, const std::string& y) {return !(x < y);} - -inline bool operator!=(const std::string& x, const Rule& y) {return !(x == y);} -inline bool operator> (const std::string& x, const Rule& y) {return y < x;} -inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);} -inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);} - -struct zonelet -{ - enum tag {has_rule, has_save, is_empty}; - - std::chrono::seconds gmtoff_; - tag tag_ = has_rule; - -#if !defined(_MSC_VER) || (_MSC_VER >= 1900) - union U -#else - struct U -#endif - { - std::string rule_; - std::chrono::minutes save_; - - ~U() {} - U() {} - U(const U&) {} - U& operator=(const U&) = delete; - } u; - - std::string format_; - date::year until_year_{0}; - MonthDayTime until_date_; - sys_seconds until_utc_; - local_seconds until_std_; - local_seconds until_loc_; - std::chrono::minutes initial_save_{}; - std::string initial_abbrev_; - std::pair first_rule_{nullptr, date::year::min()}; - std::pair last_rule_{nullptr, date::year::max()}; - - ~zonelet(); - zonelet(); - zonelet(const zonelet& i); - zonelet& operator=(const zonelet&) = delete; -}; - -#else // USE_OS_TZDB - -struct ttinfo -{ - std::int32_t tt_gmtoff; - unsigned char tt_isdst; - unsigned char tt_abbrind; - unsigned char pad[2]; -}; - -static_assert(sizeof(ttinfo) == 8, ""); - -struct expanded_ttinfo -{ - std::chrono::seconds offset; - std::string abbrev; - bool is_dst; -}; - -struct transition -{ - sys_seconds timepoint; - const expanded_ttinfo* info; - - transition(sys_seconds tp, const expanded_ttinfo* i = nullptr) - : timepoint(tp) - , info(i) - {} - - friend - std::ostream& - operator<<(std::ostream& os, const transition& t) - { - using date::operator<<; - os << t.timepoint << "Z "; - if (t.info->offset >= std::chrono::seconds{0}) - os << '+'; - os << make_time(t.info->offset); - if (t.info->is_dst > 0) - os << " daylight "; - else - os << " standard "; - os << t.info->abbrev; - return os; - } -}; - -#endif // USE_OS_TZDB - -} // namespace detail - -} // namespace date - -} // namespace velox - -} // namespace facebook - -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#include "tz.h" -#endif - -#endif // VELOX_TZ_PRIVATE_H diff --git a/velox/external/tzdb/CMakeLists.txt b/velox/external/tzdb/CMakeLists.txt new file mode 100644 index 000000000000..a324fd16ab69 --- /dev/null +++ b/velox/external/tzdb/CMakeLists.txt @@ -0,0 +1,21 @@ +# 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. + + +velox_add_library(velox_external_tzdb chrono_exception.cpp time_zone.cpp +tzdb_list.cpp tzdb.cpp) + +velox_link_libraries( + velox_external_tzdb + velox_external_date + Folly::folly + fmt::fmt) diff --git a/velox/external/tzdb/LICENSE.txt b/velox/external/tzdb/LICENSE.txt new file mode 100644 index 000000000000..5715176572a4 --- /dev/null +++ b/velox/external/tzdb/LICENSE.txt @@ -0,0 +1,278 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. diff --git a/velox/external/tzdb/chrono_exception.cpp b/velox/external/tzdb/chrono_exception.cpp new file mode 100644 index 000000000000..771e7c0bf871 --- /dev/null +++ b/velox/external/tzdb/chrono_exception.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "velox/external/tzdb/exception.h" + +namespace facebook::velox::tzdb { + +nonexistent_local_time::~nonexistent_local_time() = default; // key function + +ambiguous_local_time::~ambiguous_local_time() = default; // key function + +invalid_time_zone::~invalid_time_zone() = default; // key function + +[[noreturn]] void __throw_invalid_time_zone( + [[maybe_unused]] const std::string_view& __tz_name) { + throw invalid_time_zone(__tz_name); +} + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/exception.h b/velox/external/tzdb/exception.h new file mode 100644 index 000000000000..53d861b3e6b3 --- /dev/null +++ b/velox/external/tzdb/exception.h @@ -0,0 +1,139 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include + +#include +#include +#include +#include +#include "velox/external/date/date.h" +#include "velox/external/tzdb/local_info.h" + +namespace facebook::velox::tzdb { + +class nonexistent_local_time : public std::runtime_error { + public: + template + nonexistent_local_time( + const date::local_time<_Duration>& __time, + const local_info& __info) + : runtime_error{__create_message(__time, __info)} { + // [time.zone.exception.nonexist]/2 + // Preconditions: i.result == local_info::nonexistent is true. + // The value of __info.result is not used. + if (__info.result != local_info::nonexistent) { + throw std::runtime_error( + "creating an nonexistent_local_time from a local_info that is not non-existent"); + } + } + + nonexistent_local_time(const nonexistent_local_time&) = default; + nonexistent_local_time& operator=(const nonexistent_local_time&) = default; + + ~nonexistent_local_time() override; // exported as key function + + private: + template + std::string __create_message( + const date::local_time<_Duration>& __time, + const local_info& __info) { + using namespace facebook::velox::date; + + std::ostringstream os; + os << __time << " is in a gap between\n" + << date::local_seconds{__info.first.end.time_since_epoch()} + + __info.first.offset + << ' ' << __info.first.abbrev << " and\n" + << date::local_seconds{__info.second.begin.time_since_epoch()} + + __info.second.offset + << ' ' << __info.second.abbrev << " which are both equivalent to\n" + << __info.first.end << " UTC"; + return os.str(); + } +}; + +template +[[noreturn]] void __throw_nonexistent_local_time( + [[maybe_unused]] const date::local_time<_Duration>& __time, + [[maybe_unused]] const local_info& __info) { + throw nonexistent_local_time(__time, __info); +} + +class ambiguous_local_time : public std::runtime_error { + public: + template + ambiguous_local_time( + const date::local_time<_Duration>& __time, + const local_info& __info) + : runtime_error{__create_message(__time, __info)} { + // [time.zone.exception.ambig]/2 + // Preconditions: i.result == local_info::ambiguous is true. + // The value of __info.result is not used. + if (__info.result != local_info::ambiguous) { + throw std::runtime_error( + "creating an ambiguous_local_time from a local_info that is not ambiguous"); + } + } + + ambiguous_local_time(const ambiguous_local_time&) = default; + ambiguous_local_time& operator=(const ambiguous_local_time&) = default; + + ~ambiguous_local_time() override; // exported as key function + + private: + template + std::string __create_message( + const date::local_time<_Duration>& __time, + const local_info& __info) { + std::ostringstream os; + os << __time << " is ambiguous. It could be\n" + << __time << ' ' << __info.first.abbrev + << " == " << __time - __info.first.offset << " UTC or\n" + << __time << ' ' << __info.second.abbrev + << " == " << __time - __info.second.offset << " UTC"; + return os.str(); + } +}; + +template +[[noreturn]] void __throw_ambiguous_local_time( + [[maybe_unused]] const date::local_time<_Duration>& __time, + [[maybe_unused]] const local_info& __info) { + throw ambiguous_local_time(__time, __info); +} + +class invalid_time_zone : public std::runtime_error { + public: + invalid_time_zone( + const std::string_view& __tz_name) + : runtime_error{__create_message(__tz_name)} {} + + invalid_time_zone(const invalid_time_zone&) = default; + invalid_time_zone& operator=(const invalid_time_zone&) = default; + + ~invalid_time_zone() override; // exported as key function + + private: + std::string __create_message( + const std::string_view& __tz_name) { + std::ostringstream os; + os << __tz_name << " not found in timezone database"; + return os.str(); + } +}; + +[[noreturn]] void __throw_invalid_time_zone( + [[maybe_unused]] const std::string_view& __tz_name); + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/leap_second.h b/velox/external/tzdb/leap_second.h new file mode 100644 index 000000000000..3e6debe33729 --- /dev/null +++ b/velox/external/tzdb/leap_second.h @@ -0,0 +1,134 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include +#include "velox/external/date/date.h" + +namespace facebook::velox::tzdb { + +class leap_second { + public: + [[nodiscard]] + explicit constexpr leap_second( + date::sys_seconds __date, + std::chrono::seconds __value) + : __date_(__date), __value_(__value) {} + + leap_second(const leap_second&) = default; + leap_second& operator=(const leap_second&) = default; + + [[nodiscard]] constexpr date::sys_seconds date() const noexcept { + return __date_; + } + + [[nodiscard]] constexpr std::chrono::seconds value() const noexcept { + return __value_; + } + + private: + date::sys_seconds __date_; + std::chrono::seconds __value_; + + // The function + // template + // requires three_way_comparable_with> + // constexpr auto operator<=>(const leap_second& x, const + // sys_time& y) noexcept; + // + // Has constraints that are recursive (LWG4139). The proposed resolution is + // to make the funcion a hidden friend. For consistency make this change for + // all comparison functions. + + friend constexpr bool operator==( + const leap_second& __x, + const leap_second& __y) { + return __x.date() == __y.date(); + } + + // friend constexpr strong_ordering operator<=>(const leap_second& __x, const + // leap_second& __y) { + // return __x.date() <=> __y.date(); + // } + + template + friend constexpr bool operator==( + const leap_second& __x, + const date::sys_time<_Duration>& __y) { + return __x.date() == __y; + } + + template + friend constexpr bool operator<( + const leap_second& __x, + const date::sys_time<_Duration>& __y) { + return __x.date() < __y; + } + + template + friend constexpr bool operator<( + const date::sys_time<_Duration>& __x, + const leap_second& __y) { + return __x < __y.date(); + } + + template + friend constexpr bool operator>( + const leap_second& __x, + const date::sys_time<_Duration>& __y) { + return __y < __x; + } + + template + friend constexpr bool operator>( + const date::sys_time<_Duration>& __x, + const leap_second& __y) { + return __y < __x; + } + + template + friend constexpr bool operator<=( + const leap_second& __x, + const date::sys_time<_Duration>& __y) { + return !(__y < __x); + } + + template + friend constexpr bool operator<=( + const date::sys_time<_Duration>& __x, + const leap_second& __y) { + return !(__y < __x); + } + + template + friend constexpr bool operator>=( + const leap_second& __x, + const date::sys_time<_Duration>& __y) { + return !(__x < __y); + } + + template + friend constexpr bool operator>=( + const date::sys_time<_Duration>& __x, + const leap_second& __y) { + return !(__x < __y); + } + + // template + // requires three_way_comparable_with> + // _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const leap_second& + // __x, const sys_time<_Duration>& __y) { + // return __x.date() <=> __y; + // } +}; + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/local_info.h b/velox/external/tzdb/local_info.h new file mode 100644 index 000000000000..3e691747137d --- /dev/null +++ b/velox/external/tzdb/local_info.h @@ -0,0 +1,28 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include "velox/external/tzdb/sys_info.h" + +namespace facebook::velox::tzdb { + +struct local_info { + static constexpr int unique = 0; + static constexpr int nonexistent = 1; + static constexpr int ambiguous = 2; + + int result; + sys_info first; + sys_info second; +}; + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/patches/0001-initial-import-pr-12422.patch b/velox/external/tzdb/patches/0001-initial-import-pr-12422.patch new file mode 100644 index 000000000000..36c38f0567f4 --- /dev/null +++ b/velox/external/tzdb/patches/0001-initial-import-pr-12422.patch @@ -0,0 +1,4464 @@ +diff --git a/fbcode/velox/external/tzdb/chrono_exception.cpp b/fbcode/velox/external/tzdb/chrono_exception.cpp +--- a/fbcode/velox/external/tzdb/chrono_exception.cpp ++++ b/fbcode/velox/external/tzdb/chrono_exception.cpp +@@ -6,17 +6,12 @@ + // + //===----------------------------------------------------------------------===// + +-#include ++#include "velox/external/tzdb/exception.h" + +-_LIBCPP_BEGIN_NAMESPACE_STD ++namespace facebook::velox::tzdb { + +-namespace chrono { ++nonexistent_local_time::~nonexistent_local_time() = default; // key function + +-_LIBCPP_AVAILABILITY_TZDB +-_LIBCPP_EXPORTED_FROM_ABI nonexistent_local_time::~nonexistent_local_time() = default; // key function +-_LIBCPP_AVAILABILITY_TZDB +-_LIBCPP_EXPORTED_FROM_ABI ambiguous_local_time::~ambiguous_local_time() = default; // key function ++ambiguous_local_time::~ambiguous_local_time() = default; // key function + +-} // namespace chrono +- +-_LIBCPP_END_NAMESPACE_STD ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/exception.h b/fbcode/velox/external/tzdb/exception.h +--- a/fbcode/velox/external/tzdb/exception.h ++++ b/fbcode/velox/external/tzdb/exception.h +@@ -9,127 +9,108 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP___CHRONO_EXCEPTION_H +-#define _LIBCPP___CHRONO_EXCEPTION_H ++#pragma once + + #include +-// Enable the contents of the header only when libc++ was built with experimental features enabled. +-#if _LIBCPP_HAS_EXPERIMENTAL_TZDB + +-# include <__chrono/calendar.h> +-# include <__chrono/local_info.h> +-# include <__chrono/time_point.h> +-# include <__config> +-# include <__configuration/availability.h> +-# include <__verbose_abort> +-# include +-# include +-# include ++#include ++#include ++#include ++#include ++#include "velox/external/date/date.h" ++#include "velox/external/tzdb/local_info.h" + +-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +-# pragma GCC system_header +-# endif ++namespace facebook::velox::tzdb { + +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-# if _LIBCPP_STD_VER >= 20 +- +-namespace chrono { +- +-class nonexistent_local_time : public runtime_error { +-public: ++class nonexistent_local_time : public std::runtime_error { ++ public: + template +- _LIBCPP_HIDE_FROM_ABI nonexistent_local_time(const local_time<_Duration>& __time, const local_info& __info) ++ nonexistent_local_time( ++ const date::local_time<_Duration>& __time, ++ const local_info& __info) + : runtime_error{__create_message(__time, __info)} { + // [time.zone.exception.nonexist]/2 + // Preconditions: i.result == local_info::nonexistent is true. + // The value of __info.result is not used. +- _LIBCPP_ASSERT_PEDANTIC(__info.result == local_info::nonexistent, +- "creating an nonexistent_local_time from a local_info that is not non-existent"); ++ if (__info.result != local_info::nonexistent) { ++ throw std::runtime_error( ++ "creating an nonexistent_local_time from a local_info that is not non-existent"); ++ } + } + +- _LIBCPP_HIDE_FROM_ABI nonexistent_local_time(const nonexistent_local_time&) = default; +- _LIBCPP_HIDE_FROM_ABI nonexistent_local_time& operator=(const nonexistent_local_time&) = default; ++ nonexistent_local_time(const nonexistent_local_time&) = default; ++ nonexistent_local_time& operator=(const nonexistent_local_time&) = default; + +- _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ~nonexistent_local_time() override; // exported as key function ++ ~nonexistent_local_time() override; // exported as key function + +-private: ++ private: + template +- _LIBCPP_HIDE_FROM_ABI string __create_message(const local_time<_Duration>& __time, const local_info& __info) { +- return std::format( +- R"({} is in a gap between +-{} {} and +-{} {} which are both equivalent to +-{} UTC)", +- __time, +- local_seconds{__info.first.end.time_since_epoch()} + __info.first.offset, +- __info.first.abbrev, +- local_seconds{__info.second.begin.time_since_epoch()} + __info.second.offset, +- __info.second.abbrev, +- __info.first.end); ++ std::string __create_message( ++ const date::local_time<_Duration>& __time, ++ const local_info& __info) { ++ using namespace facebook::velox::date; ++ ++ std::ostringstream os; ++ os << __time << " is in a gap between\n" ++ << date::local_seconds{__info.first.end.time_since_epoch()} + ++ __info.first.offset ++ << ' ' << __info.first.abbrev << " and\n" ++ << date::local_seconds{__info.second.begin.time_since_epoch()} + ++ __info.second.offset ++ << ' ' << __info.second.abbrev << " which are both equivalent to\n" ++ << __info.first.end << " UTC"; ++ return os.str(); + } + }; + + template +-[[noreturn]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI void __throw_nonexistent_local_time( +- [[maybe_unused]] const local_time<_Duration>& __time, [[maybe_unused]] const local_info& __info) { +-# if _LIBCPP_HAS_EXCEPTIONS ++[[noreturn]] void __throw_nonexistent_local_time( ++ [[maybe_unused]] const date::local_time<_Duration>& __time, ++ [[maybe_unused]] const local_info& __info) { + throw nonexistent_local_time(__time, __info); +-# else +- _LIBCPP_VERBOSE_ABORT("nonexistent_local_time was thrown in -fno-exceptions mode"); +-# endif + } + +-class ambiguous_local_time : public runtime_error { +-public: ++class ambiguous_local_time : public std::runtime_error { ++ public: + template +- _LIBCPP_HIDE_FROM_ABI ambiguous_local_time(const local_time<_Duration>& __time, const local_info& __info) ++ ambiguous_local_time( ++ const date::local_time<_Duration>& __time, ++ const local_info& __info) + : runtime_error{__create_message(__time, __info)} { + // [time.zone.exception.ambig]/2 + // Preconditions: i.result == local_info::ambiguous is true. + // The value of __info.result is not used. +- _LIBCPP_ASSERT_PEDANTIC(__info.result == local_info::ambiguous, +- "creating an ambiguous_local_time from a local_info that is not ambiguous"); ++ if (__info.result != local_info::ambiguous) { ++ throw std::runtime_error( ++ "creating an ambiguous_local_time from a local_info that is not ambiguous"); ++ } + } + +- _LIBCPP_HIDE_FROM_ABI ambiguous_local_time(const ambiguous_local_time&) = default; +- _LIBCPP_HIDE_FROM_ABI ambiguous_local_time& operator=(const ambiguous_local_time&) = default; ++ ambiguous_local_time(const ambiguous_local_time&) = default; ++ ambiguous_local_time& operator=(const ambiguous_local_time&) = default; + +- _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI ~ambiguous_local_time() override; // exported as key function ++ ~ambiguous_local_time() override; // exported as key function + +-private: ++ private: + template +- _LIBCPP_HIDE_FROM_ABI string __create_message(const local_time<_Duration>& __time, const local_info& __info) { +- return std::format( +- // There are two spaces after the full-stop; this has been verified +- // in the sources of the Standard. +- R"({0} is ambiguous. It could be +-{0} {1} == {2} UTC or +-{0} {3} == {4} UTC)", +- __time, +- __info.first.abbrev, +- __time - __info.first.offset, +- __info.second.abbrev, +- __time - __info.second.offset); ++ std::string __create_message( ++ const date::local_time<_Duration>& __time, ++ const local_info& __info) { ++ std::ostringstream os; ++ os << __time << " is ambiguous. It could be\n" ++ << __time << ' ' << __info.first.abbrev ++ << " == " << __time - __info.first.offset << " UTC or\n" ++ << __time << ' ' << __info.second.abbrev ++ << " == " << __time - __info.second.offset << " UTC"; ++ return os.str(); + } + }; + + template +-[[noreturn]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI void __throw_ambiguous_local_time( +- [[maybe_unused]] const local_time<_Duration>& __time, [[maybe_unused]] const local_info& __info) { +-# if _LIBCPP_HAS_EXCEPTIONS ++[[noreturn]] void __throw_ambiguous_local_time( ++ [[maybe_unused]] const date::local_time<_Duration>& __time, ++ [[maybe_unused]] const local_info& __info) { + throw ambiguous_local_time(__time, __info); +-# else +- _LIBCPP_VERBOSE_ABORT("ambiguous_local_time was thrown in -fno-exceptions mode"); +-# endif + } + +-} // namespace chrono +- +-# endif // _LIBCPP_STD_VER >= 20 +- +-_LIBCPP_END_NAMESPACE_STD +- +-#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB +- +-#endif // _LIBCPP___CHRONO_EXCEPTION_H ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/leap_second.h b/fbcode/velox/external/tzdb/leap_second.h +--- a/fbcode/velox/external/tzdb/leap_second.h ++++ b/fbcode/velox/external/tzdb/leap_second.h +@@ -9,123 +9,126 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP___CHRONO_LEAP_SECOND_H +-#define _LIBCPP___CHRONO_LEAP_SECOND_H ++#pragma once + +-#include +-// Enable the contents of the header only when libc++ was built with experimental features enabled. +-#if _LIBCPP_HAS_EXPERIMENTAL_TZDB ++#include ++#include "velox/external/date/date.h" + +-# include <__chrono/duration.h> +-# include <__chrono/system_clock.h> +-# include <__chrono/time_point.h> +-# include <__compare/ordering.h> +-# include <__compare/three_way_comparable.h> +-# include <__config> +-# include <__utility/private_constructor_tag.h> +- +-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +-# pragma GCC system_header +-# endif +- +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-# if _LIBCPP_STD_VER >= 20 +- +-namespace chrono { ++namespace facebook::velox::tzdb { + + class leap_second { +-public: ++ public: + [[nodiscard]] +- _LIBCPP_HIDE_FROM_ABI explicit constexpr leap_second(__private_constructor_tag, sys_seconds __date, seconds __value) ++ explicit constexpr leap_second( ++ date::sys_seconds __date, ++ std::chrono::seconds __value) + : __date_(__date), __value_(__value) {} + +- _LIBCPP_HIDE_FROM_ABI leap_second(const leap_second&) = default; +- _LIBCPP_HIDE_FROM_ABI leap_second& operator=(const leap_second&) = default; ++ leap_second(const leap_second&) = default; ++ leap_second& operator=(const leap_second&) = default; + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sys_seconds date() const noexcept { return __date_; } ++ [[nodiscard]] constexpr date::sys_seconds date() const noexcept { ++ return __date_; ++ } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr seconds value() const noexcept { return __value_; } ++ [[nodiscard]] constexpr std::chrono::seconds value() const noexcept { ++ return __value_; ++ } + +-private: +- sys_seconds __date_; +- seconds __value_; ++ private: ++ date::sys_seconds __date_; ++ std::chrono::seconds __value_; + + // The function + // template + // requires three_way_comparable_with> +- // constexpr auto operator<=>(const leap_second& x, const sys_time& y) noexcept; ++ // constexpr auto operator<=>(const leap_second& x, const ++ // sys_time& y) noexcept; + // + // Has constraints that are recursive (LWG4139). The proposed resolution is + // to make the funcion a hidden friend. For consistency make this change for + // all comparison functions. + +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const leap_second& __x, const leap_second& __y) { ++ friend constexpr bool operator==( ++ const leap_second& __x, ++ const leap_second& __y) { + return __x.date() == __y.date(); + } + +- _LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering operator<=>(const leap_second& __x, const leap_second& __y) { +- return __x.date() <=> __y.date(); +- } ++ // friend constexpr strong_ordering operator<=>(const leap_second& __x, const ++ // leap_second& __y) { ++ // return __x.date() <=> __y.date(); ++ // } + + template +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const leap_second& __x, const sys_time<_Duration>& __y) { ++ friend constexpr bool operator==( ++ const leap_second& __x, ++ const date::sys_time<_Duration>& __y) { + return __x.date() == __y; + } + + template +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const leap_second& __x, const sys_time<_Duration>& __y) { ++ friend constexpr bool operator<( ++ const leap_second& __x, ++ const date::sys_time<_Duration>& __y) { + return __x.date() < __y; + } + + template +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<(const sys_time<_Duration>& __x, const leap_second& __y) { ++ friend constexpr bool operator<( ++ const date::sys_time<_Duration>& __x, ++ const leap_second& __y) { + return __x < __y.date(); + } + + template +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const leap_second& __x, const sys_time<_Duration>& __y) { ++ friend constexpr bool operator>( ++ const leap_second& __x, ++ const date::sys_time<_Duration>& __y) { + return __y < __x; + } + + template +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>(const sys_time<_Duration>& __x, const leap_second& __y) { ++ friend constexpr bool operator>( ++ const date::sys_time<_Duration>& __x, ++ const leap_second& __y) { + return __y < __x; + } + + template +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const leap_second& __x, const sys_time<_Duration>& __y) { ++ friend constexpr bool operator<=( ++ const leap_second& __x, ++ const date::sys_time<_Duration>& __y) { + return !(__y < __x); + } + + template +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator<=(const sys_time<_Duration>& __x, const leap_second& __y) { ++ friend constexpr bool operator<=( ++ const date::sys_time<_Duration>& __x, ++ const leap_second& __y) { + return !(__y < __x); + } + + template +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const leap_second& __x, const sys_time<_Duration>& __y) { ++ friend constexpr bool operator>=( ++ const leap_second& __x, ++ const date::sys_time<_Duration>& __y) { + return !(__x < __y); + } + + template +- _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator>=(const sys_time<_Duration>& __x, const leap_second& __y) { ++ friend constexpr bool operator>=( ++ const date::sys_time<_Duration>& __x, ++ const leap_second& __y) { + return !(__x < __y); + } + +- template +- requires three_way_comparable_with> +- _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const leap_second& __x, const sys_time<_Duration>& __y) { +- return __x.date() <=> __y; +- } ++ // template ++ // requires three_way_comparable_with> ++ // _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(const leap_second& ++ // __x, const sys_time<_Duration>& __y) { ++ // return __x.date() <=> __y; ++ // } + }; + +-} // namespace chrono +- +-# endif // _LIBCPP_STD_VER >= 20 +- +-_LIBCPP_END_NAMESPACE_STD +- +-#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB +- +-#endif // _LIBCPP___CHRONO_LEAP_SECOND_H ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/local_info.h b/fbcode/velox/external/tzdb/local_info.h +--- a/fbcode/velox/external/tzdb/local_info.h ++++ b/fbcode/velox/external/tzdb/local_info.h +@@ -9,42 +9,20 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP___CHRONO_LOCAL_INFO_H +-#define _LIBCPP___CHRONO_LOCAL_INFO_H ++#pragma once + +-#include +-// Enable the contents of the header only when libc++ was built with experimental features enabled. +-#if _LIBCPP_HAS_EXPERIMENTAL_TZDB ++#include "velox/external/tzdb/sys_info.h" + +-# include <__chrono/sys_info.h> +-# include <__config> +- +-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +-# pragma GCC system_header +-# endif +- +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-# if _LIBCPP_STD_VER >= 20 +- +-namespace chrono { ++namespace facebook::velox::tzdb { + + struct local_info { +- static constexpr int unique = 0; ++ static constexpr int unique = 0; + static constexpr int nonexistent = 1; +- static constexpr int ambiguous = 2; ++ static constexpr int ambiguous = 2; + + int result; + sys_info first; + sys_info second; + }; + +-} // namespace chrono +- +-# endif // _LIBCPP_STD_VER >= 20 +- +-_LIBCPP_END_NAMESPACE_STD +- +-#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB +- +-#endif // _LIBCPP___CHRONO_LOCAL_INFO_H ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/sys_info.h b/fbcode/velox/external/tzdb/sys_info.h +--- a/fbcode/velox/external/tzdb/sys_info.h ++++ b/fbcode/velox/external/tzdb/sys_info.h +@@ -9,43 +9,20 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP___CHRONO_SYS_INFO_H +-#define _LIBCPP___CHRONO_SYS_INFO_H ++#pragma once + +-#include +-// Enable the contents of the header only when libc++ was built with experimental features enabled. +-#if _LIBCPP_HAS_EXPERIMENTAL_TZDB ++#include ++#include ++#include "velox/external/date/date.h" + +-# include <__chrono/duration.h> +-# include <__chrono/system_clock.h> +-# include <__chrono/time_point.h> +-# include <__config> +-# include +- +-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +-# pragma GCC system_header +-# endif +- +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-# if _LIBCPP_STD_VER >= 20 +- +-namespace chrono { ++namespace facebook::velox::tzdb { + + struct sys_info { +- sys_seconds begin; +- sys_seconds end; +- seconds offset; +- minutes save; +- string abbrev; ++ date::sys_seconds begin; ++ date::sys_seconds end; ++ std::chrono::seconds offset; ++ std::chrono::minutes save; ++ std::string abbrev; + }; + +-} // namespace chrono +- +-# endif // _LIBCPP_STD_VER >= 20 +- +-_LIBCPP_END_NAMESPACE_STD +- +-#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB +- +-#endif // _LIBCPP___CHRONO_SYS_INFO_H ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/time_zone.cpp b/fbcode/velox/external/tzdb/time_zone.cpp +--- a/fbcode/velox/external/tzdb/time_zone.cpp ++++ b/fbcode/velox/external/tzdb/time_zone.cpp +@@ -29,41 +29,20 @@ + // These quirks often use a 12h interval; this is the scan interval of zdump, + // which implies there are no sys_info objects with a duration of less than 12h. + ++#include ++#include ++#include + #include +-#include +-#include +-#include + #include +-#include +-#include ++#include + +-#include "include/tzdb/time_zone_private.h" +-#include "include/tzdb/tzdb_list_private.h" ++#include "velox/external/date/date.h" ++#include "velox/external/tzdb/time_zone_private.h" ++#include "velox/external/tzdb/types_private.h" + +-// TODO TZDB remove debug printing +-#ifdef PRINT +-# include +-#endif ++namespace facebook::velox::tzdb { + +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-#ifdef PRINT +-template <> +-struct formatter { +- template +- constexpr typename ParseContext::iterator parse(ParseContext& ctx) { +- return ctx.begin(); +- } +- +- template +- typename FormatContext::iterator format(const chrono::sys_info& info, FormatContext& ctx) const { +- return std::format_to( +- ctx.out(), "[{}, {}) {:%Q%q} {:%Q%q} {}", info.begin, info.end, info.offset, info.save, info.abbrev); +- } +-}; +-#endif +- +-namespace chrono { ++using namespace std::chrono_literals; + + //===----------------------------------------------------------------------===// + // Details +@@ -82,22 +61,23 @@ + // current result can be merged with the next result. + // - The unexpected result means no sys_info object was found and the time is + // the time to be used for the next search iteration. +-using __sys_info_result = expected<__sys_info, sys_seconds>; +- +-template , _Proj>> _Comp = ranges::less> +-[[nodiscard]] static ranges::borrowed_iterator_t<_Range> +-__binary_find(_Range&& __r, const _Type& __value, _Comp __comp = {}, _Proj __proj = {}) { +- auto __end = ranges::end(__r); +- auto __ret = ranges::lower_bound(ranges::begin(__r), __end, __value, __comp, __proj); ++using __sys_info_result = folly::Expected<__sys_info, date::sys_seconds>; ++ ++[[nodiscard]] static auto __binary_find( ++ const __rules_storage_type& __rules_db, ++ const std::string& __rule_name) { ++ auto __end = __rules_db.end(); ++ auto __ret = std::lower_bound( ++ __rules_db.begin(), ++ __rules_db.end(), ++ __rule_name, ++ [](const auto& rule, const auto& name) { return rule.first < name; }); + if (__ret == __end) + return __end; + + // When the value does not match the predicate it's equal and a valid result + // was found. +- return !std::invoke(__comp, __value, std::invoke(__proj, *__ret)) ? __ret : __end; ++ return __rule_name >= __ret->first ? __ret : __end; + } + + // Format based on https://data.iana.org/time-zones/tz-how-to.html +@@ -115,39 +95,51 @@ + // + // Rule 1 is not strictly validated since America/Barbados uses a two letter + // abbreviation AT. +-[[nodiscard]] static string +-__format(const __tz::__continuation& __continuation, const string& __letters, seconds __save) { ++[[nodiscard]] static std::string __format( ++ const facebook::velox::tzdb::__continuation& __continuation, ++ const std::string& __letters, ++ std::chrono::seconds __save) { + bool __shift = false; +- string __result; ++ std::string __result; + for (char __c : __continuation.__format) { + if (__shift) { + switch (__c) { +- case 's': +- std::ranges::copy(__letters, std::back_inserter(__result)); +- break; +- +- case 'z': { +- if (__continuation.__format.size() != 2) ++ case 's': ++ std::copy( ++ __letters.begin(), __letters.end(), std::back_inserter(__result)); ++ break; ++ ++ case 'z': { ++ if (__continuation.__format.size() != 2) ++ std::__throw_runtime_error( ++ fmt::format( ++ "corrupt tzdb FORMAT field: %z should be the entire contents, instead contains '{}'", ++ __continuation.__format) ++ .c_str()); ++ date::hh_mm_ss __offset{__continuation.__stdoff + __save}; ++ if (__offset.is_negative()) { ++ __result += '-'; ++ __offset = date::hh_mm_ss{-(__continuation.__stdoff + __save)}; ++ } else ++ __result += '+'; ++ ++ if (__offset.minutes() != 0min) ++ fmt::format_to( ++ std::back_inserter(__result), ++ "{:%H}{:%M}", ++ __offset.hours(), ++ __offset.minutes()); ++ else ++ fmt::format_to( ++ std::back_inserter(__result), "{:%H}", __offset.hours()); ++ } break; ++ ++ default: + std::__throw_runtime_error( +- std::format("corrupt tzdb FORMAT field: %z should be the entire contents, instead contains '{}'", +- __continuation.__format) ++ fmt::format( ++ "corrupt tzdb FORMAT field: invalid sequence '%{}' found, expected %s or %z", ++ __c) + .c_str()); +- chrono::hh_mm_ss __offset{__continuation.__stdoff + __save}; +- if (__offset.is_negative()) { +- __result += '-'; +- __offset = chrono::hh_mm_ss{-(__continuation.__stdoff + __save)}; +- } else +- __result += '+'; +- +- if (__offset.minutes() != 0min) +- std::format_to(std::back_inserter(__result), "{:%H%M}", __offset); +- else +- std::format_to(std::back_inserter(__result), "{:%H}", __offset); +- } break; +- +- default: +- std::__throw_runtime_error( +- std::format("corrupt tzdb FORMAT field: invalid sequence '%{}' found, expected %s or %z", __c).c_str()); + } + __shift = false; + +@@ -163,14 +155,16 @@ + __result.push_back(__c); + } else { + std::__throw_runtime_error( +- std::format( +- "corrupt tzdb FORMAT field: invalid character '{}' found, expected +, -, or an alphanumeric value", __c) ++ fmt::format( ++ "corrupt tzdb FORMAT field: invalid character '{}' found, expected +, -, or an alphanumeric value", ++ __c) + .c_str()); + } + } + + if (__shift) +- std::__throw_runtime_error("corrupt tzdb FORMAT field: input ended with the start of the escape sequence '%'"); ++ std::__throw_runtime_error( ++ "corrupt tzdb FORMAT field: input ended with the start of the escape sequence '%'"); + + if (__result.empty()) + std::__throw_runtime_error("corrupt tzdb FORMAT field: result is empty"); +@@ -178,67 +172,84 @@ + return __result; + } + +-[[nodiscard]] static sys_seconds __to_sys_seconds(year_month_day __ymd, seconds __seconds) { +- seconds __result = static_cast(__ymd).time_since_epoch() + __seconds; +- return sys_seconds{__result}; ++[[nodiscard]] static date::sys_seconds __to_sys_seconds( ++ date::year_month_day __ymd, ++ std::chrono::seconds __seconds) { ++ std::chrono::seconds __result = ++ static_cast(__ymd).time_since_epoch() + __seconds; ++ return date::sys_seconds{__result}; + } + +-[[nodiscard]] static seconds __at_to_sys_seconds(const __tz::__continuation& __continuation) { ++template ++struct False : std::bool_constant {}; ++ ++[[nodiscard]] static std::chrono::seconds __at_to_sys_seconds( ++ const facebook::velox::tzdb::__continuation& __continuation) { + switch (__continuation.__at.__clock) { +- case __tz::__clock::__local: +- return __continuation.__at.__time - __continuation.__stdoff - +- std::visit( +- [](const auto& __value) { +- using _Tp = decay_t; +- if constexpr (same_as<_Tp, monostate>) +- return chrono::seconds{0}; +- else if constexpr (same_as<_Tp, __tz::__save>) +- return chrono::duration_cast(__value.__time); +- else if constexpr (same_as<_Tp, std::string>) +- // For a named rule based continuation the SAVE depends on the RULE +- // active at the end. This should be determined separately. +- return chrono::seconds{0}; +- else +- static_assert(false); +- +- std::__libcpp_unreachable(); +- }, +- __continuation.__rules); +- +- case __tz::__clock::__universal: +- return __continuation.__at.__time; +- +- case __tz::__clock::__standard: +- return __continuation.__at.__time - __continuation.__stdoff; ++ case facebook::velox::tzdb::__clock::__local: ++ return __continuation.__at.__time - __continuation.__stdoff - ++ std::visit( ++ [](const auto& __value) { ++ using _Tp = std::decay_t; ++ if constexpr (std::is_same_v<_Tp, std::monostate>) ++ return std::chrono::seconds{0}; ++ else if constexpr (std::is_same_v< ++ _Tp, ++ facebook::velox::tzdb::__save>) ++ return std::chrono::duration_cast( ++ __value.__time); ++ else if constexpr (std::is_same_v<_Tp, std::string>) ++ // For a named rule based continuation the SAVE depends on ++ // the RULE active at the end. This should be determined ++ // separately. ++ return std::chrono::seconds{0}; ++ else ++ static_assert(False<_Tp>::value); ++ ++ throw std::runtime_error("unreachable"); ++ }, ++ __continuation.__rules); ++ ++ case facebook::velox::tzdb::__clock::__universal: ++ return __continuation.__at.__time; ++ ++ case facebook::velox::tzdb::__clock::__standard: ++ return __continuation.__at.__time - __continuation.__stdoff; + } +- std::__libcpp_unreachable(); ++ throw std::runtime_error("unreachable"); + } + +-[[nodiscard]] static year_month_day __to_year_month_day(year __year, month __month, __tz::__on __on) { ++[[nodiscard]] static date::year_month_day __to_year_month_day( ++ date::year __year, ++ date::month __month, ++ facebook::velox::tzdb::__on __on) { + return std::visit( + [&](const auto& __value) { +- using _Tp = decay_t; +- if constexpr (same_as<_Tp, chrono::day>) +- return year_month_day{__year, __month, __value}; +- else if constexpr (same_as<_Tp, weekday_last>) +- return year_month_day{static_cast(year_month_weekday_last{__year, __month, __value})}; +- else if constexpr (same_as<_Tp, __tz::__constrained_weekday>) ++ using _Tp = std::decay_t; ++ if constexpr (std::is_same_v<_Tp, date::day>) ++ return date::year_month_day{__year, __month, __value}; ++ else if constexpr (std::is_same_v<_Tp, date::weekday_last>) ++ return date::year_month_day{static_cast( ++ date::year_month_weekday_last{__year, __month, __value})}; ++ else if constexpr (std::is_same_v<_Tp, __constrained_weekday>) + return __value(__year, __month); + else +- static_assert(false); ++ static_assert(False<_Tp>::value); + +- std::__libcpp_unreachable(); ++ throw std::runtime_error("unreachable"); + }, + __on); + } + +-[[nodiscard]] static sys_seconds __until_to_sys_seconds(const __tz::__continuation& __continuation) { ++[[nodiscard]] static date::sys_seconds __until_to_sys_seconds( ++ const facebook::velox::tzdb::__continuation& __continuation) { + // Does UNTIL contain the magic value for the last continuation? +- if (__continuation.__year == chrono::year::min()) +- return sys_seconds::max(); ++ if (__continuation.__year == date::year::min()) ++ return date::sys_seconds::max(); + +- year_month_day __ymd = chrono::__to_year_month_day(__continuation.__year, __continuation.__in, __continuation.__on); +- return chrono::__to_sys_seconds(__ymd, chrono::__at_to_sys_seconds(__continuation)); ++ date::year_month_day __ymd = __to_year_month_day( ++ __continuation.__year, __continuation.__in, __continuation.__on); ++ return __to_sys_seconds(__ymd, __at_to_sys_seconds(__continuation)); + } + + // Holds the UNTIL time for a continuation with a named rule. +@@ -247,9 +258,9 @@ + // This means when the UNTIL uses the local wall time the actual UNTIL value can + // only be determined when the SAVE is known. This class holds that abstraction. + class __named_rule_until { +-public: +- explicit __named_rule_until(const __tz::__continuation& __continuation) +- : __until_{chrono::__until_to_sys_seconds(__continuation)}, ++ public: ++ explicit __named_rule_until(const facebook::velox::tzdb::__continuation& __continuation) ++ : __until_{__until_to_sys_seconds(__continuation)}, + __needs_adjustment_{ + // The last continuation of a ZONE has no UNTIL which basically is + // until the end of _local_ time. This value is expressed by +@@ -257,57 +268,75 @@ + // However SAVE can be negative, which would add a value to maximum + // leading to undefined behaviour. In practice this often results in + // an overflow to a very small value. +- __until_ != sys_seconds::max() && __continuation.__at.__clock == __tz::__clock::__local} {} ++ __until_ != date::sys_seconds::max() && ++ __continuation.__at.__clock == ++ facebook::velox::tzdb::__clock::__local} {} + + // Gives the unadjusted until value, this is useful when the SAVE is not known + // at all. +- sys_seconds __until() const noexcept { return __until_; } ++ date::sys_seconds __until() const noexcept { ++ return __until_; ++ } + +- bool __needs_adjustment() const noexcept { return __needs_adjustment_; } ++ bool __needs_adjustment() const noexcept { ++ return __needs_adjustment_; ++ } + + // Returns the UNTIL adjusted for SAVE. +- sys_seconds operator()(seconds __save) const noexcept { return __until_ - __needs_adjustment_ * __save; } ++ date::sys_seconds operator()(std::chrono::seconds __save) const noexcept { ++ return __until_ - __needs_adjustment_ * __save; ++ } + +-private: +- sys_seconds __until_; ++ private: ++ date::sys_seconds __until_; + bool __needs_adjustment_; + }; + +-[[nodiscard]] static seconds __at_to_seconds(seconds __stdoff, const __tz::__rule& __rule) { ++[[nodiscard]] static std::chrono::seconds __at_to_seconds( ++ std::chrono::seconds __stdoff, ++ const __rule& __rule) { + switch (__rule.__at.__clock) { +- case __tz::__clock::__local: +- // Local time and standard time behave the same. This is not +- // correct. Local time needs to adjust for the current saved time. +- // To know the saved time the rules need to be known and sorted. +- // This needs a time so to avoid the chicken and egg adjust the +- // saving of the local time later. +- return __rule.__at.__time - __stdoff; +- +- case __tz::__clock::__universal: +- return __rule.__at.__time; +- +- case __tz::__clock::__standard: +- return __rule.__at.__time - __stdoff; ++ case facebook::velox::tzdb::__clock::__local: ++ // Local time and standard time behave the same. This is not ++ // correct. Local time needs to adjust for the current saved time. ++ // To know the saved time the rules need to be known and sorted. ++ // This needs a time so to avoid the chicken and egg adjust the ++ // saving of the local time later. ++ return __rule.__at.__time - __stdoff; ++ ++ case facebook::velox::tzdb::__clock::__universal: ++ return __rule.__at.__time; ++ ++ case facebook::velox::tzdb::__clock::__standard: ++ return __rule.__at.__time - __stdoff; + } +- std::__libcpp_unreachable(); ++ throw std::runtime_error("unreachable"); + } + +-[[nodiscard]] static sys_seconds __from_to_sys_seconds(seconds __stdoff, const __tz::__rule& __rule, year __year) { +- year_month_day __ymd = chrono::__to_year_month_day(__year, __rule.__in, __rule.__on); ++[[nodiscard]] static date::sys_seconds __from_to_sys_seconds( ++ std::chrono::seconds __stdoff, ++ const __rule& __rule, ++ date::year __year) { ++ date::year_month_day __ymd = ++ __to_year_month_day(__year, __rule.__in, __rule.__on); + +- seconds __at = chrono::__at_to_seconds(__stdoff, __rule); +- return chrono::__to_sys_seconds(__ymd, __at); ++ std::chrono::seconds __at = __at_to_seconds(__stdoff, __rule); ++ return __to_sys_seconds(__ymd, __at); + } + +-[[nodiscard]] static sys_seconds __from_to_sys_seconds(seconds __stdoff, const __tz::__rule& __rule) { +- return chrono::__from_to_sys_seconds(__stdoff, __rule, __rule.__from); ++[[nodiscard]] static date::sys_seconds __from_to_sys_seconds( ++ std::chrono::seconds __stdoff, ++ const __rule& __rule) { ++ return __from_to_sys_seconds(__stdoff, __rule, __rule.__from); + } + +-[[nodiscard]] static const vector<__tz::__rule>& +-__get_rules(const __tz::__rules_storage_type& __rules_db, const string& __rule_name) { +- auto __result = chrono::__binary_find(__rules_db, __rule_name, {}, [](const auto& __p) { return __p.first; }); ++[[nodiscard]] static const std::vector<__rule>& __get_rules( ++ const __rules_storage_type& __rules_db, ++ const std::string& __rule_name) { ++ auto __result = __binary_find(__rules_db, __rule_name); + if (__result == std::end(__rules_db)) +- std::__throw_runtime_error(("corrupt tzdb: rule '" + __rule_name + " 'does not exist").c_str()); ++ std::__throw_runtime_error( ++ ("corrupt tzdb: rule '" + __rule_name + " 'does not exist").c_str()); + + return __result->second; + } +@@ -326,17 +355,17 @@ + // transition with a SAVE of zero. + // + // This function implements case 2. +-[[nodiscard]] static string __letters_before_first_rule(const vector<__tz::__rule>& __rules) { +- auto __letters = +- __rules // +- | views::filter([](const __tz::__rule& __rule) { return __rule.__save.__time == 0s; }) // +- | views::transform([](const __tz::__rule& __rule) { return __rule.__letters; }) // +- | views::take(1); ++[[nodiscard]] static std::string __letters_before_first_rule( ++ const std::vector<__rule>& __rules) { ++ for (const auto& __rule : __rules) { ++ if (__rule.__save.__time != 0s) { ++ continue; ++ } + +- if (__letters.empty()) +- std::__throw_runtime_error("corrupt tzdb: rule has zero entries"); ++ return __rule.__letters; ++ } + +- return __letters.front(); ++ std::__throw_runtime_error("corrupt tzdb: rule has zero entries"); + } + + // Determines the information based on the continuation and the rules. +@@ -356,55 +385,68 @@ + // after (1) and until (3) no rule entry is associated with the time. + + [[nodiscard]] static sys_info __get_sys_info_before_first_rule( +- sys_seconds __begin, +- sys_seconds __end, +- const __tz::__continuation& __continuation, +- const vector<__tz::__rule>& __rules) { ++ date::sys_seconds __begin, ++ date::sys_seconds __end, ++ const facebook::velox::tzdb::__continuation& __continuation, ++ const std::vector<__rule>& __rules) { + return sys_info{ + __begin, + __end, + __continuation.__stdoff, +- chrono::minutes(0), +- chrono::__format(__continuation, __letters_before_first_rule(__rules), 0s)}; ++ std::chrono::minutes(0), ++ __format(__continuation, __letters_before_first_rule(__rules), 0s)}; + } + + // Returns the sys_info object for a time before the first rule. + // When this first rule has a SAVE of 0s the sys_info for the time before the + // first rule and for the first rule are identical and will be merged. + [[nodiscard]] static sys_info __get_sys_info_before_first_rule( +- sys_seconds __begin, +- sys_seconds __rule_end, // The end used when SAVE != 0s +- sys_seconds __next_end, // The end used when SAVE == 0s the times are merged +- const __tz::__continuation& __continuation, +- const vector<__tz::__rule>& __rules, +- vector<__tz::__rule>::const_iterator __rule) { ++ date::sys_seconds __begin, ++ date::sys_seconds __rule_end, // The end used when SAVE != 0s ++ date::sys_seconds ++ __next_end, // The end used when SAVE == 0s the times are merged ++ const facebook::velox::tzdb::__continuation& __continuation, ++ const std::vector<__rule>& __rules, ++ std::vector<__rule>::const_iterator __rule) { + if (__rule->__save.__time != 0s) +- return __get_sys_info_before_first_rule(__begin, __rule_end, __continuation, __rules); ++ return __get_sys_info_before_first_rule( ++ __begin, __rule_end, __continuation, __rules); + + return sys_info{ +- __begin, __next_end, __continuation.__stdoff, 0min, chrono::__format(__continuation, __rule->__letters, 0s)}; ++ __begin, ++ __next_end, ++ __continuation.__stdoff, ++ 0min, ++ __format(__continuation, __rule->__letters, 0s)}; + } + +-[[nodiscard]] static seconds __at_to_seconds(seconds __stdoff, seconds __save, const __tz::__rule& __rule) { ++[[nodiscard]] static std::chrono::seconds __at_to_seconds( ++ std::chrono::seconds __stdoff, ++ std::chrono::seconds __save, ++ const __rule& __rule) { + switch (__rule.__at.__clock) { +- case __tz::__clock::__local: +- return __rule.__at.__time - __stdoff - __save; ++ case facebook::velox::tzdb::__clock::__local: ++ return __rule.__at.__time - __stdoff - __save; + +- case __tz::__clock::__universal: +- return __rule.__at.__time; ++ case facebook::velox::tzdb::__clock::__universal: ++ return __rule.__at.__time; + +- case __tz::__clock::__standard: +- return __rule.__at.__time - __stdoff; ++ case facebook::velox::tzdb::__clock::__standard: ++ return __rule.__at.__time - __stdoff; + } +- std::__libcpp_unreachable(); ++ throw std::runtime_error("unreachable"); + } + +-[[nodiscard]] static sys_seconds +-__rule_to_sys_seconds(seconds __stdoff, seconds __save, const __tz::__rule& __rule, year __year) { +- year_month_day __ymd = chrono::__to_year_month_day(__year, __rule.__in, __rule.__on); ++[[nodiscard]] static date::sys_seconds __rule_to_sys_seconds( ++ std::chrono::seconds __stdoff, ++ std::chrono::seconds __save, ++ const __rule& __rule, ++ date::year __year) { ++ date::year_month_day __ymd = ++ __to_year_month_day(__year, __rule.__in, __rule.__on); + +- seconds __at = chrono::__at_to_seconds(__stdoff, __save, __rule); +- return chrono::__to_sys_seconds(__ymd, __at); ++ std::chrono::seconds __at = __at_to_seconds(__stdoff, __save, __rule); ++ return __to_sys_seconds(__ymd, __at); + } + + // Returns the first rule after __time. +@@ -420,32 +462,38 @@ + // R So 1945 o - N 18 2s 0 - + // + // Has 3 rules that are all only active in 1945. +-[[nodiscard]] static pair::const_iterator> +-__next_rule(sys_seconds __time, +- seconds __stdoff, +- seconds __save, +- const vector<__tz::__rule>& __rules, +- vector<__tz::__rule>::const_iterator __current) { +- year __year = year_month_day{chrono::floor(__time)}.year(); ++[[nodiscard]] static std:: ++ pair::const_iterator> ++ __next_rule( ++ date::sys_seconds __time, ++ std::chrono::seconds __stdoff, ++ std::chrono::seconds __save, ++ const std::vector<__rule>& __rules, ++ std::vector<__rule>::const_iterator __current) { ++ date::year __year = ++ date::year_month_day{std::chrono::floor(__time)}.year(); + + // Note it would probably be better to store the pairs in a vector and then + // use min() to get the smallest element +- map::const_iterator> __candidates; ++ std::map::const_iterator> __candidates; + // Note this evaluates all rules which is a waste of effort; when the entries + // are beyond the current year's "next year" (where "next year" is not always + // year + 1) the algorithm should end. + for (auto __it = __rules.begin(); __it != __rules.end(); ++__it) { +- for (year __y = __it->__from; __y <= __it->__to; ++__y) { ++ for (date::year __y = __it->__from; __y <= __it->__to; ++__y) { + // Adding the current entry for the current year may lead to infinite + // loops due to the SAVE adjustment. Skip these entries. + if (__y == __year && __it == __current) + continue; + +- sys_seconds __t = chrono::__rule_to_sys_seconds(__stdoff, __save, *__it, __y); ++ date::sys_seconds __t = ++ __rule_to_sys_seconds(__stdoff, __save, *__it, __y); + if (__t <= __time) + continue; + +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(!__candidates.contains(__t), "duplicated rule"); ++ if (__candidates.count(__t) > 0) { ++ throw std::runtime_error("duplicated rule"); ++ } + __candidates[__t] = __it; + break; + } +@@ -454,9 +502,9 @@ + if (!__candidates.empty()) [[likely]] { + auto __it = __candidates.begin(); + +- // When no rule is selected the time before the first rule and the first rule +- // should not be merged. +- if (__time == sys_seconds::min()) ++ // When no rule is selected the time before the first rule and the first ++ // rule should not be merged. ++ if (__time == date::sys_seconds::min()) + return *__it; + + // There can be two constitutive rules that are the same. For example, +@@ -469,14 +517,15 @@ + // 1974-04-20 18:30:00 R2 becomes active. + // Both rules have a SAVE of 1 hour and LETTERS are S for both of them. + while (__it != __candidates.end()) { +- if (__current->__save.__time != __it->second->__save.__time || __current->__letters != __it->second->__letters) ++ if (__current->__save.__time != __it->second->__save.__time || ++ __current->__letters != __it->second->__letters) + return *__it; + + ++__it; + } + } + +- return {sys_seconds::max(), __rules.end()}; ++ return {date::sys_seconds::max(), __rules.end()}; + } + + // Returns the first rule of a set of rules. +@@ -484,23 +533,29 @@ + // R Sa 2008 2009 - Mar Su>=8 0 0 - + // R Sa 2007 2008 - O Su>=8 0 1 - + // The transition in October 2007 happens before the transition in March 2008. +-[[nodiscard]] static vector<__tz::__rule>::const_iterator +-__first_rule(seconds __stdoff, const vector<__tz::__rule>& __rules) { +- return chrono::__next_rule(sys_seconds::min(), __stdoff, 0s, __rules, __rules.end()).second; ++[[nodiscard]] static std::vector<__rule>::const_iterator __first_rule( ++ std::chrono::seconds __stdoff, ++ const std::vector<__rule>& __rules) { ++ return __next_rule( ++ date::sys_seconds::min(), __stdoff, 0s, __rules, __rules.end()) ++ .second; + } + + [[nodiscard]] static __sys_info_result __get_sys_info_rule( +- sys_seconds __time, +- sys_seconds __continuation_begin, +- const __tz::__continuation& __continuation, +- const vector<__tz::__rule>& __rules) { +- auto __rule = chrono::__first_rule(__continuation.__stdoff, __rules); +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__rule != __rules.end(), "the set of rules has no first rule"); ++ date::sys_seconds __time, ++ date::sys_seconds __continuation_begin, ++ const facebook::velox::tzdb::__continuation& __continuation, ++ const std::vector<__rule>& __rules) { ++ auto __rule = __first_rule(__continuation.__stdoff, __rules); ++ if (__rule == __rules.end()) { ++ throw std::runtime_error("the set of rules has no first rule"); ++ } + + // Avoid selecting a time before the start of the continuation + __time = std::max(__time, __continuation_begin); + +- sys_seconds __rule_begin = chrono::__from_to_sys_seconds(__continuation.__stdoff, *__rule); ++ date::sys_seconds __rule_begin = ++ __from_to_sys_seconds(__continuation.__stdoff, *__rule); + + // The time sought is very likely inside the current rule. + // When the continuation's UNTIL uses the local clock there are edge cases +@@ -510,7 +565,12 @@ + // + // For now we just walk all the rules TODO TZDB investigate whether a smarter + // algorithm would work. +- auto __next = chrono::__next_rule(__rule_begin, __continuation.__stdoff, __rule->__save.__time, __rules, __rule); ++ auto __next = __next_rule( ++ __rule_begin, ++ __continuation.__stdoff, ++ __rule->__save.__time, ++ __rules, ++ __rule); + + // Ignore small steps, this happens with America/Punta_Arenas for the + // transition +@@ -521,18 +581,29 @@ + // R x 1927 1931 - S 1 0 1 - + // R x 1928 1932 - Ap 1 0 0 - + // +- // America/Punta_Arenas Thu Sep 1 04:42:45 1927 UT = Thu Sep 1 00:42:45 1927 -04 isdst=1 gmtoff=-14400 +- // America/Punta_Arenas Sun Apr 1 03:59:59 1928 UT = Sat Mar 31 23:59:59 1928 -04 isdst=1 gmtoff=-14400 +- // America/Punta_Arenas Sun Apr 1 04:00:00 1928 UT = Sat Mar 31 23:00:00 1928 -05 isdst=0 gmtoff=-18000 ++ // America/Punta_Arenas Thu Sep 1 04:42:45 1927 UT = Thu Sep 1 00:42:45 ++ // 1927 -04 isdst=1 gmtoff=-14400 America/Punta_Arenas Sun Apr 1 03:59:59 ++ // 1928 UT = Sat Mar 31 23:59:59 1928 -04 isdst=1 gmtoff=-14400 ++ // America/Punta_Arenas Sun Apr 1 04:00:00 1928 UT = Sat Mar 31 23:00:00 ++ // 1928 -05 isdst=0 gmtoff=-18000 + // + // Without this there will be a transition + // [1927-09-01 04:42:45, 1927-09-01 05:00:00) -05:00:00 0min -05 + +- if (sys_seconds __begin = __rule->__save.__time != 0s ? __rule_begin : __next.first; __time < __begin) { +- if (__continuation_begin == sys_seconds::min() || __begin - __continuation_begin > 12h) +- return __sys_info{__get_sys_info_before_first_rule( +- __continuation_begin, __rule_begin, __next.first, __continuation, __rules, __rule), +- false}; ++ if (date::sys_seconds __begin = ++ __rule->__save.__time != 0s ? __rule_begin : __next.first; ++ __time < __begin) { ++ if (__continuation_begin == date::sys_seconds::min() || ++ __begin - __continuation_begin > 12h) ++ return __sys_info{ ++ __get_sys_info_before_first_rule( ++ __continuation_begin, ++ __rule_begin, ++ __next.first, ++ __continuation, ++ __rules, ++ __rule), ++ false}; + + // Europe/Berlin + // 1 c CE%sT 1945 May 24 2 (C1) +@@ -544,64 +615,59 @@ + // + // When C2 becomes active the time would be before the first rule R2, + // giving a 1 hour sys_info. +- seconds __save = __rule->__save.__time; ++ std::chrono::seconds __save = __rule->__save.__time; + __named_rule_until __continuation_end{__continuation}; +- sys_seconds __sys_info_end = std::min(__continuation_end(__save), __next.first); ++ date::sys_seconds __sys_info_end = ++ std::min(__continuation_end(__save), __next.first); + + return __sys_info{ +- sys_info{__continuation_begin, +- __sys_info_end, +- __continuation.__stdoff + __save, +- chrono::duration_cast(__save), +- chrono::__format(__continuation, __rule->__letters, __save)}, ++ sys_info{ ++ __continuation_begin, ++ __sys_info_end, ++ __continuation.__stdoff + __save, ++ std::chrono::duration_cast(__save), ++ __format(__continuation, __rule->__letters, __save)}, + __sys_info_end == __continuation_end(__save)}; + } + + // See above for America/Asuncion + if (__rule->__save.__time == 0s && __time < __next.first) { + return __sys_info{ +- sys_info{__continuation_begin, +- __next.first, +- __continuation.__stdoff, +- 0min, +- chrono::__format(__continuation, __rule->__letters, 0s)}, ++ sys_info{ ++ __continuation_begin, ++ __next.first, ++ __continuation.__stdoff, ++ 0min, ++ __format(__continuation, __rule->__letters, 0s)}, + false}; + } + + if (__rule->__save.__time != 0s) { + // another fix for America/Punta_Arenas when not at the start of the + // sys_info object. +- seconds __save = __rule->__save.__time; +- if (__continuation_begin >= __rule_begin - __save && __time < __next.first) { ++ std::chrono::seconds __save = __rule->__save.__time; ++ if (__continuation_begin >= __rule_begin - __save && ++ __time < __next.first) { + return __sys_info{ +- sys_info{__continuation_begin, +- __next.first, +- __continuation.__stdoff + __save, +- chrono::duration_cast(__save), +- chrono::__format(__continuation, __rule->__letters, __save)}, ++ sys_info{ ++ __continuation_begin, ++ __next.first, ++ __continuation.__stdoff + __save, ++ std::chrono::duration_cast(__save), ++ __format(__continuation, __rule->__letters, __save)}, + false}; + } + } + + __named_rule_until __continuation_end{__continuation}; + while (__next.second != __rules.end()) { +-#ifdef PRINT +- std::print( +- stderr, +- "Rule for {}: [{}, {}) off={} save={} duration={}\n", +- __time, +- __rule_begin, +- __next.first, +- __continuation.__stdoff, +- __rule->__save.__time, +- __next.first - __rule_begin); +-#endif +- +- sys_seconds __end = __continuation_end(__rule->__save.__time); ++ date::sys_seconds __end = __continuation_end(__rule->__save.__time); + +- sys_seconds __sys_info_begin = std::max(__continuation_begin, __rule_begin); +- sys_seconds __sys_info_end = std::min(__end, __next.first); +- seconds __diff = chrono::abs(__sys_info_end - __sys_info_begin); ++ date::sys_seconds __sys_info_begin = ++ std::max(__continuation_begin, __rule_begin); ++ date::sys_seconds __sys_info_end = std::min(__end, __next.first); ++ std::chrono::seconds __diff = ++ std::chrono::abs(__sys_info_end - __sys_info_begin); + + if (__diff < 12h) { + // Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31 +@@ -627,70 +693,98 @@ + // Looking at the zdump like output in libc++ this generates jumps in + // the UTC time. + +- __rule = __next.second; +- __next = __next_rule(__next.first, __continuation.__stdoff, __rule->__save.__time, __rules, __rule); +- __end = __continuation_end(__rule->__save.__time); ++ __rule = __next.second; ++ __next = __next_rule( ++ __next.first, ++ __continuation.__stdoff, ++ __rule->__save.__time, ++ __rules, ++ __rule); ++ __end = __continuation_end(__rule->__save.__time); + __sys_info_end = std::min(__end, __next.first); + } + +- if ((__time >= __rule_begin && __time < __next.first) || __next.first >= __end) { ++ if ((__time >= __rule_begin && __time < __next.first) || ++ __next.first >= __end) { + __sys_info_begin = std::max(__continuation_begin, __rule_begin); +- __sys_info_end = std::min(__end, __next.first); ++ __sys_info_end = std::min(__end, __next.first); + + return __sys_info{ +- sys_info{__sys_info_begin, +- __sys_info_end, +- __continuation.__stdoff + __rule->__save.__time, +- chrono::duration_cast(__rule->__save.__time), +- chrono::__format(__continuation, __rule->__letters, __rule->__save.__time)}, ++ sys_info{ ++ __sys_info_begin, ++ __sys_info_end, ++ __continuation.__stdoff + __rule->__save.__time, ++ std::chrono::duration_cast( ++ __rule->__save.__time), ++ __format( ++ __continuation, __rule->__letters, __rule->__save.__time)}, + __sys_info_end == __end}; + } + + __rule_begin = __next.first; +- __rule = __next.second; +- __next = __next_rule(__rule_begin, __continuation.__stdoff, __rule->__save.__time, __rules, __rule); ++ __rule = __next.second; ++ __next = __next_rule( ++ __rule_begin, ++ __continuation.__stdoff, ++ __rule->__save.__time, ++ __rules, ++ __rule); + } + + return __sys_info{ +- sys_info{std::max(__continuation_begin, __rule_begin), +- __continuation_end(__rule->__save.__time), +- __continuation.__stdoff + __rule->__save.__time, +- chrono::duration_cast(__rule->__save.__time), +- chrono::__format(__continuation, __rule->__letters, __rule->__save.__time)}, ++ sys_info{ ++ std::max(__continuation_begin, __rule_begin), ++ __continuation_end(__rule->__save.__time), ++ __continuation.__stdoff + __rule->__save.__time, ++ std::chrono::duration_cast( ++ __rule->__save.__time), ++ __format(__continuation, __rule->__letters, __rule->__save.__time)}, + true}; + } + + [[nodiscard]] static __sys_info_result __get_sys_info_basic( +- sys_seconds __time, sys_seconds __continuation_begin, const __tz::__continuation& __continuation, seconds __save) { +- sys_seconds __continuation_end = chrono::__until_to_sys_seconds(__continuation); ++ date::sys_seconds __time, ++ date::sys_seconds __continuation_begin, ++ const facebook::velox::tzdb::__continuation& __continuation, ++ std::chrono::seconds __save) { ++ date::sys_seconds __continuation_end = __until_to_sys_seconds(__continuation); + return __sys_info{ +- sys_info{__continuation_begin, +- __continuation_end, +- __continuation.__stdoff + __save, +- chrono::duration_cast(__save), +- chrono::__format(__continuation, __continuation.__format, __save)}, ++ sys_info{ ++ __continuation_begin, ++ __continuation_end, ++ __continuation.__stdoff + __save, ++ std::chrono::duration_cast(__save), ++ __format(__continuation, __continuation.__format, __save)}, + true}; + } + +-[[nodiscard]] static __sys_info_result +-__get_sys_info(sys_seconds __time, +- sys_seconds __continuation_begin, +- const __tz::__continuation& __continuation, +- const __tz::__rules_storage_type& __rules_db) { ++[[nodiscard]] static __sys_info_result __get_sys_info( ++ date::sys_seconds __time, ++ date::sys_seconds __continuation_begin, ++ const facebook::velox::tzdb::__continuation& __continuation, ++ const __rules_storage_type& __rules_db) { + return std::visit( + [&](const auto& __value) { +- using _Tp = decay_t; +- if constexpr (same_as<_Tp, std::string>) +- return chrono::__get_sys_info_rule( +- __time, __continuation_begin, __continuation, __get_rules(__rules_db, __value)); +- else if constexpr (same_as<_Tp, monostate>) +- return chrono::__get_sys_info_basic(__time, __continuation_begin, __continuation, chrono::seconds(0)); +- else if constexpr (same_as<_Tp, __tz::__save>) +- return chrono::__get_sys_info_basic(__time, __continuation_begin, __continuation, __value.__time); ++ using _Tp = std::decay_t; ++ if constexpr (std::is_same_v<_Tp, std::string>) ++ return __get_sys_info_rule( ++ __time, ++ __continuation_begin, ++ __continuation, ++ __get_rules(__rules_db, __value)); ++ else if constexpr (std::is_same_v<_Tp, std::monostate>) ++ return __get_sys_info_basic( ++ __time, ++ __continuation_begin, ++ __continuation, ++ std::chrono::seconds(0)); ++ else if constexpr (std::is_same_v<_Tp, facebook::velox::tzdb::__save>) ++ return __get_sys_info_basic( ++ __time, __continuation_begin, __continuation, __value.__time); + else +- static_assert(false); ++ static_assert(False<_Tp>::value); + +- std::__libcpp_unreachable(); ++ throw std::runtime_error("unreachable"); + }, + __continuation.__rules); + } +@@ -711,11 +805,14 @@ + // Iff the "offsets" are the same '__current.__end' is replaced with + // '__next.__end', which effectively merges the two objects in one object. The + // function returns true if a merge occurred. +-[[nodiscard]] bool __merge_continuation(sys_info& __current, const sys_info& __next) { ++[[nodiscard]] bool __merge_continuation( ++ sys_info& __current, ++ const sys_info& __next) { + if (__current.end != __next.begin) + return false; + +- if (__current.offset != __next.offset || __current.abbrev != __next.abbrev || __current.save != __next.save) ++ if (__current.offset != __next.offset || __current.abbrev != __next.abbrev || ++ __current.save != __next.save) + return false; + + __current.end = __next.end; +@@ -726,24 +823,30 @@ + // Public API + //===----------------------------------------------------------------------===// + +-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI time_zone time_zone::__create(unique_ptr&& __p) { +- _LIBCPP_ASSERT_NON_NULL(__p != nullptr, "initialized time_zone without a valid pimpl object"); ++[[nodiscard]] time_zone time_zone::__create( ++ std::unique_ptr&& __p) { ++ if (__p == nullptr) { ++ throw std::runtime_error( ++ "initialized time_zone without a valid pimpl object"); ++ } + time_zone result; + result.__impl_ = std::move(__p); + return result; + } + +-_LIBCPP_EXPORTED_FROM_ABI time_zone::~time_zone() = default; ++time_zone::~time_zone() = default; + +-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view time_zone::__name() const noexcept { return __impl_->__name(); } ++[[nodiscard]] std::string_view time_zone::__name() const noexcept { ++ return __impl_->__name(); ++} + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI sys_info +-time_zone::__get_info(sys_seconds __time) const { +- optional __result; +- bool __valid_result = false; // true iff __result.has_value() is true and +- // __result.begin <= __time < __result.end is true. +- bool __can_merge = false; +- sys_seconds __continuation_begin = sys_seconds::min(); ++[[nodiscard]] sys_info time_zone::__get_info(date::sys_seconds __time) const { ++ std::optional __result; ++ bool __valid_result = ++ false; // true iff __result.has_value() is true and ++ // __result.begin <= __time < __result.end is true. ++ bool __can_merge = false; ++ date::sys_seconds __continuation_begin = date::sys_seconds::min(); + // Iterates over the Zone entry and its continuations. Internally the Zone + // entry is split in a Zone information and the first continuation. The last + // continuation has no UNTIL field. This means the loop should always find a +@@ -788,15 +891,18 @@ + // means the period [1928-10-07 00:00:00, 1967-06-03 12:00:00) should be + // returned in one sys_info object. + +- const auto& __continuations = __impl_->__continuations(); +- const __tz::__rules_storage_type& __rules_db = __impl_->__rules_db(); +- for (auto __it = __continuations.begin(); __it != __continuations.end(); ++__it) { +- const auto& __continuation = *__it; +- __sys_info_result __sys_info = chrono::__get_sys_info(__time, __continuation_begin, __continuation, __rules_db); ++ const auto& __continuations = __impl_->__continuations(); ++ const __rules_storage_type& __rules_db = __impl_->__rules_db(); ++ for (auto __it = __continuations.begin(); __it != __continuations.end(); ++ ++__it) { ++ const auto& __continuation = *__it; ++ __sys_info_result __sys_info = __get_sys_info( ++ __time, __continuation_begin, __continuation, __rules_db); + + if (__sys_info) { +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( +- __sys_info->__info.begin < __sys_info->__info.end, "invalid sys_info range"); ++ if (__sys_info->__info.begin >= __sys_info->__info.end) { ++ throw std::runtime_error("invalid sys_info range"); ++ } + + // Filters out dummy entries + // Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31 +@@ -816,7 +922,8 @@ + // by [C2 & R1, C2 & R2) which is the proper range + // "[2000-03-03 03:00:00, 2007-12-30 03:00:00) -02:00:00 60min -02 + +- if (std::holds_alternative(__continuation.__rules) && __sys_info->__can_merge && ++ if (std::holds_alternative(__continuation.__rules) && ++ __sys_info->__can_merge && + __sys_info->__info.begin + 12h > __sys_info->__info.end) { + __continuation_begin = __sys_info->__info.begin; + continue; +@@ -827,13 +934,14 @@ + __result = __sys_info->__info; + + __valid_result = __time >= __result->begin && __time < __result->end; +- __can_merge = __sys_info->__can_merge; +- } else if (__can_merge && chrono::__merge_continuation(*__result, __sys_info->__info)) { ++ __can_merge = __sys_info->__can_merge; ++ } else if ( ++ __can_merge && __merge_continuation(*__result, __sys_info->__info)) { + // The results are merged, update the result state. This may + // "overwrite" a valid sys_info object with another valid sys_info + // object. + __valid_result = __time >= __result->begin && __time < __result->end; +- __can_merge = __sys_info->__can_merge; ++ __can_merge = __sys_info->__can_merge; + } else { + // Here things get interesting: + // For example, America/Argentina/San_Luis +@@ -861,10 +969,12 @@ + if (__valid_result) { + return *__result; + } else { +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( +- __it != __continuations.begin(), "the first rule should always seed the result"); ++ if (__it == __continuations.begin()) { ++ throw std::runtime_error( ++ "the first rule should always seed the result"); ++ } + const auto& __last = *(__it - 1); +- if (std::holds_alternative(__last.__rules)) { ++ if (std::holds_alternative(__last.__rules)) { + // Europe/Berlin + // 1 c CE%sT 1945 May 24 2 (C1) + // 1 So CE%sT 1946 (C2) +@@ -873,24 +983,25 @@ + // + // R So 1945 o - May 24 2 2 M (R2) + // +- // When C2 becomes active the time would be before the first rule R2, +- // giving a 1 hour sys_info. This is not valid and the results need +- // merging. ++ // When C2 becomes active the time would be before the first rule ++ // R2, giving a 1 hour sys_info. This is not valid and the results ++ // need merging. + + if (__result->end != __sys_info->__info.begin) { + // When the UTC gap between the rules is due to the change of + // offsets adjust the new time to remove the gap. +- sys_seconds __end = __result->end - __result->offset; +- sys_seconds __begin = __sys_info->__info.begin - __sys_info->__info.offset; ++ date::sys_seconds __end = __result->end - __result->offset; ++ date::sys_seconds __begin = ++ __sys_info->__info.begin - __sys_info->__info.offset; + if (__end == __begin) { + __sys_info->__info.begin = __result->end; + } + } + } + +- __result = __sys_info->__info; ++ __result = __sys_info->__info; + __valid_result = __time >= __result->begin && __time < __result->end; +- __can_merge = __sys_info->__can_merge; ++ __can_merge = __sys_info->__can_merge; + } + } + __continuation_begin = __result->end; +@@ -906,20 +1017,28 @@ + + // Is the "__local_time" present in "__first" and "__second". If so the + // local_info has an ambiguous result. +-[[nodiscard]] static bool +-__is_ambiguous(local_seconds __local_time, const sys_info& __first, const sys_info& __second) { +- std::chrono::local_seconds __end_first{__first.end.time_since_epoch() + __first.offset}; +- std::chrono::local_seconds __begin_second{__second.begin.time_since_epoch() + __second.offset}; ++[[nodiscard]] static bool __is_ambiguous( ++ date::local_seconds __local_time, ++ const sys_info& __first, ++ const sys_info& __second) { ++ date::local_seconds __end_first{ ++ __first.end.time_since_epoch() + __first.offset}; ++ date::local_seconds __begin_second{ ++ __second.begin.time_since_epoch() + __second.offset}; + + return __local_time < __end_first && __local_time >= __begin_second; + } + + // Determines the result of the "__local_time". This expects the object + // "__first" to be earlier in time than "__second". +-[[nodiscard]] static local_info +-__get_info(local_seconds __local_time, const sys_info& __first, const sys_info& __second) { +- std::chrono::local_seconds __end_first{__first.end.time_since_epoch() + __first.offset}; +- std::chrono::local_seconds __begin_second{__second.begin.time_since_epoch() + __second.offset}; ++[[nodiscard]] static local_info __get_info( ++ date::local_seconds __local_time, ++ const sys_info& __first, ++ const sys_info& __second) { ++ date::local_seconds __end_first{ ++ __first.end.time_since_epoch() + __first.offset}; ++ date::local_seconds __begin_second{ ++ __second.begin.time_since_epoch() + __second.offset}; + + if (__local_time < __end_first) { + if (__local_time >= __begin_second) +@@ -946,9 +1065,9 @@ + return {local_info::unique, __second, sys_info{}}; + } + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI local_info +-time_zone::__get_info(local_seconds __local_time) const { +- seconds __local_seconds = __local_time.time_since_epoch(); ++[[nodiscard]] local_info time_zone::__get_info( ++ date::local_seconds __local_time) const { ++ std::chrono::seconds __local_seconds = __local_time.time_since_epoch(); + + /* An example of a typical year with a DST switch displayed in local time. + * +@@ -978,7 +1097,7 @@ + // The code needs to determine the system time for the local time. There is no + // information available. Assume the offset between system time and local time + // is 0s. This gives an initial estimate. +- sys_seconds __guess{__local_seconds}; ++ date::sys_seconds __guess{__local_seconds}; + sys_info __info = __get_info(__guess); + + // At this point the offset can be used to determine an estimate for the local +@@ -986,11 +1105,13 @@ + // local time is the range [chrono::local_seconds::min(), + // chrono::local_seconds::max()). + if (__local_seconds < 0s && __info.offset > 0s) +- if (__local_seconds - chrono::local_seconds::min().time_since_epoch() < __info.offset) ++ if (__local_seconds - date::local_seconds::min().time_since_epoch() < ++ __info.offset) + return {-1, __info, {}}; + + if (__local_seconds > 0s && __info.offset < 0s) +- if (chrono::local_seconds::max().time_since_epoch() - __local_seconds < -__info.offset) ++ if (date::local_seconds::max().time_since_epoch() - __local_seconds < ++ -__info.offset) + return {-2, __info, {}}; + + // Based on the information found in the sys_info, the local time can be +@@ -1024,17 +1145,19 @@ + // However the local_info object only has 2 sys_info objects, so this option + // is not tested. + +- sys_seconds __sys_time{__local_seconds - __info.offset}; ++ date::sys_seconds __sys_time{__local_seconds - __info.offset}; + if (__sys_time < __info.begin) + // Case 1 before __info +- return chrono::__get_info(__local_time, __get_info(__info.begin - 1s), __info); ++ return facebook::velox::tzdb::__get_info( ++ __local_time, __get_info(__info.begin - 1s), __info); + + if (__sys_time >= __info.end) + // Case 3 after __info +- return chrono::__get_info(__local_time, __info, __get_info(__info.end)); ++ return facebook::velox::tzdb::__get_info( ++ __local_time, __info, __get_info(__info.end)); + + // Case 2 in __info +- if (__info.begin != sys_seconds::min()) { ++ if (__info.begin != date::sys_seconds::min()) { + // Case 2.1 Not at the beginning, when not ambiguous the result should test + // case 2.3. + sys_info __prev = __get_info(__info.begin - 1s); +@@ -1042,14 +1165,13 @@ + return {local_info::ambiguous, __prev, __info}; + } + +- if (__info.end == sys_seconds::max()) ++ if (__info.end == date::sys_seconds::max()) + // At the end so it's case 2.2 + return {local_info::unique, __info, sys_info{}}; + + // This tests case 2.2 or case 2.3. +- return chrono::__get_info(__local_time, __info, __get_info(__info.end)); ++ return facebook::velox::tzdb::__get_info( ++ __local_time, __info, __get_info(__info.end)); + } + +-} // namespace chrono +- +-_LIBCPP_END_NAMESPACE_STD ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/time_zone.h b/fbcode/velox/external/tzdb/time_zone.h +--- a/fbcode/velox/external/tzdb/time_zone.h ++++ b/fbcode/velox/external/tzdb/time_zone.h +@@ -9,44 +9,26 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP___CHRONO_TIME_ZONE_H +-#define _LIBCPP___CHRONO_TIME_ZONE_H ++#pragma once + +-#include +-// Enable the contents of the header only when libc++ was built with experimental features enabled. +-#if _LIBCPP_HAS_EXPERIMENTAL_TZDB ++#include ++#include ++#include ++#include + +-# include <__chrono/calendar.h> +-# include <__chrono/duration.h> +-# include <__chrono/exception.h> +-# include <__chrono/local_info.h> +-# include <__chrono/sys_info.h> +-# include <__chrono/system_clock.h> +-# include <__compare/strong_order.h> +-# include <__config> +-# include <__memory/unique_ptr.h> +-# include <__type_traits/common_type.h> +-# include ++#include "velox/external/date/date.h" ++#include "velox/external/tzdb/exception.h" ++#include "velox/external/tzdb/local_info.h" ++#include "velox/external/tzdb/sys_info.h" + +-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +-# pragma GCC system_header +-# endif +- +-_LIBCPP_PUSH_MACROS +-# include <__undef_macros> +- +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION +- +-namespace chrono { ++namespace facebook::velox::tzdb { + + enum class choose { earliest, latest }; + +-class _LIBCPP_AVAILABILITY_TZDB time_zone { +- _LIBCPP_HIDE_FROM_ABI time_zone() = default; ++class time_zone { ++ time_zone() = default; + +-public: ++ public: + class __impl; // public so it can be used by make_unique. + + // The "constructor". +@@ -54,132 +36,179 @@ + // The default constructor is private to avoid the constructor from being + // part of the ABI. Instead use an __ugly_named function as an ABI interface, + // since that gives us the ability to change it in the future. +- [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI static time_zone __create(unique_ptr<__impl>&& __p); ++ [[nodiscard]] static time_zone __create(std::unique_ptr<__impl>&& __p); + +- _LIBCPP_EXPORTED_FROM_ABI ~time_zone(); ++ ~time_zone(); + +- _LIBCPP_HIDE_FROM_ABI time_zone(time_zone&&) = default; +- _LIBCPP_HIDE_FROM_ABI time_zone& operator=(time_zone&&) = default; ++ time_zone(time_zone&&) = default; ++ time_zone& operator=(time_zone&&) = default; + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name(); } ++ [[nodiscard]] std::string_view name() const noexcept { ++ return __name(); ++ } + + template +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_info get_info(const sys_time<_Duration>& __time) const { +- return __get_info(chrono::time_point_cast(__time)); ++ [[nodiscard]] sys_info get_info( ++ const date::sys_time<_Duration>& __time) const { ++ return __get_info( ++ std::chrono::time_point_cast(__time)); + } + + template +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI local_info get_info(const local_time<_Duration>& __time) const { +- return __get_info(chrono::time_point_cast(__time)); ++ [[nodiscard]] local_info get_info( ++ const date::local_time<_Duration>& __time) const { ++ return __get_info( ++ std::chrono::time_point_cast(__time)); + } + + // We don't apply nodiscard here since this function throws on many inputs, + // so it could be used as a validation. + template +- _LIBCPP_HIDE_FROM_ABI sys_time> to_sys(const local_time<_Duration>& __time) const { ++ date::sys_time> to_sys( ++ const date::local_time<_Duration>& __time) const { + local_info __info = get_info(__time); + switch (__info.result) { +- case local_info::unique: +- return sys_time>{__time.time_since_epoch() - __info.first.offset}; ++ case local_info::unique: ++ return date::sys_time< ++ std::common_type_t<_Duration, std::chrono::seconds>>{ ++ __time.time_since_epoch() - __info.first.offset}; + +- case local_info::nonexistent: +- chrono::__throw_nonexistent_local_time(__time, __info); ++ case local_info::nonexistent: ++ __throw_nonexistent_local_time(__time, __info); + +- case local_info::ambiguous: +- chrono::__throw_ambiguous_local_time(__time, __info); ++ case local_info::ambiguous: ++ __throw_ambiguous_local_time(__time, __info); + } + + // TODO TZDB The Standard does not specify anything in these cases. +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( +- __info.result != -1, "cannot convert the local time; it would be before the minimum system clock value"); +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( +- __info.result != -2, "cannot convert the local time; it would be after the maximum system clock value"); ++ if (__info.result == -1) { ++ throw std::runtime_error( ++ "cannot convert the local time; it would be before the minimum system clock value"); ++ } ++ if (__info.result == -2) { ++ throw std::runtime_error( ++ "cannot convert the local time; it would be after the maximum system clock value"); ++ } + + return {}; + } + + template +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time> +- to_sys(const local_time<_Duration>& __time, choose __z) const { ++ [[nodiscard]] date::sys_time< ++ std::common_type_t<_Duration, std::chrono::seconds>> ++ to_sys(const date::local_time<_Duration>& __time, choose __z) const { + local_info __info = get_info(__time); + switch (__info.result) { +- case local_info::unique: // first and second are the same +- return sys_time>{__time.time_since_epoch() - __info.first.offset}; +- +- case local_info::nonexistent: +- // first and second are the same +- // All non-existing values are converted to the same time. +- return sys_time>{__info.first.end}; +- +- case local_info::ambiguous: +- switch (__z) { +- case choose::earliest: +- return sys_time>{__time.time_since_epoch() - __info.first.offset}; +- +- case choose::latest: +- return sys_time>{__time.time_since_epoch() - __info.second.offset}; +- +- // Note a value out of bounds is not specified. +- } ++ case local_info::unique: ++ case local_info::nonexistent: // first and second are the same ++ return date::sys_time< ++ std::common_type_t<_Duration, std::chrono::seconds>>{ ++ __time.time_since_epoch() - __info.first.offset}; ++ ++ case local_info::ambiguous: ++ switch (__z) { ++ case choose::earliest: ++ return date::sys_time< ++ std::common_type_t<_Duration, std::chrono::seconds>>{ ++ __time.time_since_epoch() - __info.first.offset}; ++ ++ case choose::latest: ++ return date::sys_time< ++ std::common_type_t<_Duration, std::chrono::seconds>>{ ++ __time.time_since_epoch() - __info.second.offset}; ++ ++ // Note a value out of bounds is not specified. ++ } + } + + // TODO TZDB The standard does not specify anything in these cases. +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( +- __info.result != -1, "cannot convert the local time; it would be before the minimum system clock value"); +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( +- __info.result != -2, "cannot convert the local time; it would be after the maximum system clock value"); ++ if (__info.result == -1) { ++ throw std::runtime_error( ++ "cannot convert the local time; it would be before the minimum system clock value"); ++ } ++ if (__info.result != -2) { ++ throw std::runtime_error( ++ "cannot convert the local time; it would be after the maximum system clock value"); ++ } + + return {}; + } + + template +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI local_time> +- to_local(const sys_time<_Duration>& __time) const { +- using _Dp = common_type_t<_Duration, seconds>; ++ [[nodiscard]] date::local_time< ++ std::common_type_t<_Duration, std::chrono::seconds>> ++ to_local(const date::sys_time<_Duration>& __time) const { ++ using _Dp = std::common_type_t<_Duration, std::chrono::seconds>; + + sys_info __info = get_info(__time); + +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( +- __info.offset >= chrono::seconds{0} || __time.time_since_epoch() >= _Dp::min() - __info.offset, +- "cannot convert the system time; it would be before the minimum local clock value"); ++ if (__info.offset < std::chrono::seconds{0} && ++ __time.time_since_epoch() < _Dp::min() - __info.offset) { ++ throw std::runtime_error( ++ "cannot convert the system time; it would be before the minimum local clock value"); ++ } + +- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN( +- __info.offset <= chrono::seconds{0} || __time.time_since_epoch() <= _Dp::max() - __info.offset, +- "cannot convert the system time; it would be after the maximum local clock value"); ++ if (__info.offset > std::chrono::seconds{0} && ++ __time.time_since_epoch() > _Dp::max() - __info.offset) { ++ throw std::runtime_error( ++ "cannot convert the system time; it would be after the maximum local clock value"); ++ } + +- return local_time<_Dp>{__time.time_since_epoch() + __info.offset}; ++ return date::local_time<_Dp>{__time.time_since_epoch() + __info.offset}; + } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const __impl& __implementation() const noexcept { return *__impl_; } ++ [[nodiscard]] const __impl& __implementation() const noexcept { ++ return *__impl_; ++ } + +-private: +- [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view __name() const noexcept; ++ private: ++ [[nodiscard]] std::string_view __name() const noexcept; + +- [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI sys_info __get_info(sys_seconds __time) const; +- [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI local_info __get_info(local_seconds __time) const; ++ [[nodiscard]] sys_info __get_info(date::sys_seconds __time) const; ++ [[nodiscard]] local_info __get_info(date::local_seconds __time) const; + +- unique_ptr<__impl> __impl_; ++ std::unique_ptr<__impl> __impl_; + }; + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline bool +-operator==(const time_zone& __x, const time_zone& __y) noexcept { ++[[nodiscard]] inline bool operator==( ++ const time_zone& __x, ++ const time_zone& __y) noexcept { + return __x.name() == __y.name(); + } + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline strong_ordering +-operator<=>(const time_zone& __x, const time_zone& __y) noexcept { +- return __x.name() <=> __y.name(); ++[[nodiscard]] inline bool operator!=( ++ const time_zone& __x, ++ const time_zone& __y) noexcept { ++ return !(__x.name() == __y.name()); + } + +-} // namespace chrono ++[[nodiscard]] inline bool operator<( ++ const time_zone& __x, ++ const time_zone& __y) noexcept { ++ return __x.name() < __y.name(); ++} + +-# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && +- // _LIBCPP_HAS_LOCALIZATION ++[[nodiscard]] inline bool operator>( ++ const time_zone& __x, ++ const time_zone& __y) noexcept { ++ return __x.name() > __y.name(); ++} + +-_LIBCPP_END_NAMESPACE_STD ++[[nodiscard]] inline bool operator<=( ++ const time_zone& __x, ++ const time_zone& __y) noexcept { ++ return !(__x.name() > __y.name()); ++} + +-_LIBCPP_POP_MACROS ++[[nodiscard]] inline bool operator>=( ++ const time_zone& __x, ++ const time_zone& __y) noexcept { ++ return !(__x.name() < __y.name()); ++} + +-#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB ++// [[nodiscard]] inline strong_ordering ++// operator<=>(const time_zone& __x, const time_zone& __y) noexcept { ++// return __x.name() <=> __y.name(); ++// } + +-#endif // _LIBCPP___CHRONO_TIME_ZONE_H ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/time_zone_link.h b/fbcode/velox/external/tzdb/time_zone_link.h +--- a/fbcode/velox/external/tzdb/time_zone_link.h ++++ b/fbcode/velox/external/tzdb/time_zone_link.h +@@ -9,71 +9,76 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP___CHRONO_TIME_ZONE_LINK_H +-#define _LIBCPP___CHRONO_TIME_ZONE_LINK_H ++#pragma once + +-#include +-// Enable the contents of the header only when libc++ was built with experimental features enabled. +-#if _LIBCPP_HAS_EXPERIMENTAL_TZDB ++#include ++#include + +-# include <__compare/strong_order.h> +-# include <__config> +-# include <__utility/private_constructor_tag.h> +-# include +-# include +- +-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +-# pragma GCC system_header +-# endif +- +-_LIBCPP_PUSH_MACROS +-# include <__undef_macros> +- +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION +- +-namespace chrono { ++namespace facebook::velox::tzdb { + + class time_zone_link { +-public: ++ public: + [[nodiscard]] +- _LIBCPP_HIDE_FROM_ABI explicit time_zone_link(__private_constructor_tag, string_view __name, string_view __target) ++ explicit time_zone_link(std::string_view __name, std::string_view __target) + : __name_{__name}, __target_{__target} {} + +- _LIBCPP_HIDE_FROM_ABI time_zone_link(time_zone_link&&) = default; +- _LIBCPP_HIDE_FROM_ABI time_zone_link& operator=(time_zone_link&&) = default; ++ time_zone_link(time_zone_link&&) = default; ++ time_zone_link& operator=(time_zone_link&&) = default; + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name_; } +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string_view target() const noexcept { return __target_; } ++ [[nodiscard]] std::string_view name() const noexcept { ++ return __name_; ++ } ++ [[nodiscard]] std::string_view target() const noexcept { ++ return __target_; ++ } + +-private: +- string __name_; ++ private: ++ std::string __name_; + // TODO TZDB instead of the name we can store the pointer to a zone. These + // pointers are immutable. This makes it possible to directly return a + // pointer in the time_zone in the 'locate_zone' function. +- string __target_; ++ std::string __target_; + }; + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline bool +-operator==(const time_zone_link& __x, const time_zone_link& __y) noexcept { ++[[nodiscard]] inline bool operator==( ++ const time_zone_link& __x, ++ const time_zone_link& __y) noexcept { + return __x.name() == __y.name(); + } + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline strong_ordering +-operator<=>(const time_zone_link& __x, const time_zone_link& __y) noexcept { +- return __x.name() <=> __y.name(); ++[[nodiscard]] inline bool operator!=( ++ const time_zone_link& __x, ++ const time_zone_link& __y) noexcept { ++ return !(__x.name() == __y.name()); + } + +-} // namespace chrono ++[[nodiscard]] inline bool operator<( ++ const time_zone_link& __x, ++ const time_zone_link& __y) noexcept { ++ return __x.name() < __y.name(); ++} + +-# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && +- // _LIBCPP_HAS_LOCALIZATION ++[[nodiscard]] inline bool operator>( ++ const time_zone_link& __x, ++ const time_zone_link& __y) noexcept { ++ return __x.name() > __y.name(); ++} + +-_LIBCPP_END_NAMESPACE_STD ++[[nodiscard]] inline bool operator<=( ++ const time_zone_link& __x, ++ const time_zone_link& __y) noexcept { ++ return !(__x.name() > __y.name()); ++} + +-_LIBCPP_POP_MACROS ++[[nodiscard]] inline bool operator>=( ++ const time_zone_link& __x, ++ const time_zone_link& __y) noexcept { ++ return !(__x.name() < __y.name()); ++} + +-#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB ++// [[nodiscard]] inline strong_ordering ++// operator<=>(const time_zone_link& __x, const time_zone_link& __y) noexcept { ++// return __x.name() <=> __y.name(); ++// } + +-#endif // _LIBCPP___CHRONO_TIME_ZONE_LINK_H ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/time_zone_private.h b/fbcode/velox/external/tzdb/time_zone_private.h +--- a/fbcode/velox/external/tzdb/time_zone_private.h ++++ b/fbcode/velox/external/tzdb/time_zone_private.h +@@ -9,49 +9,48 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_PRIVATE_H +-#define _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_PRIVATE_H ++#pragma once + +-#include + #include + #include + +-#include "types_private.h" ++#include "velox/external/tzdb/time_zone.h" ++#include "velox/external/tzdb/types_private.h" + +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-namespace chrono { ++namespace facebook::velox::tzdb { + + class time_zone::__impl { +-public: +- explicit _LIBCPP_HIDE_FROM_ABI __impl(string&& __name, const __tz::__rules_storage_type& __rules_db) ++ public: ++ explicit __impl(std::string&& __name, const __rules_storage_type& __rules_db) + : __name_(std::move(__name)), __rules_db_(__rules_db) {} + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI string_view __name() const noexcept { return __name_; } ++ [[nodiscard]] std::string_view __name() const noexcept { ++ return __name_; ++ } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI vector<__tz::__continuation>& __continuations() { return __continuations_; } +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const vector<__tz::__continuation>& __continuations() const { ++ [[nodiscard]] std::vector& __continuations() { ++ return __continuations_; ++ } ++ [[nodiscard]] const std::vector& __continuations() const { + return __continuations_; + } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const __tz::__rules_storage_type& __rules_db() const { return __rules_db_; } ++ [[nodiscard]] const __rules_storage_type& __rules_db() const { ++ return __rules_db_; ++ } + +-private: +- string __name_; ++ private: ++ std::string __name_; + // Note the first line has a name + __continuation, the other lines + // are just __continuations. So there is always at least one item in + // the vector. +- vector<__tz::__continuation> __continuations_; ++ std::vector __continuations_; + + // Continuations often depend on a set of rules. The rules are stored in + // parallel data structurs in tzdb_list. From the time_zone it's not possible + // to find its associated tzdb entry and thus not possible to find its + // associated rules. Therefore a link to the rules in stored in this class. +- const __tz::__rules_storage_type& __rules_db_; ++ const __rules_storage_type& __rules_db_; + }; + +-} // namespace chrono +- +-_LIBCPP_END_NAMESPACE_STD +- +-#endif // _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_PRIVATE_H ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/types_private.h b/fbcode/velox/external/tzdb/types_private.h +--- a/fbcode/velox/external/tzdb/types_private.h ++++ b/fbcode/velox/external/tzdb/types_private.h +@@ -1,4 +1,3 @@ +- + // -*- C++ -*- + //===----------------------------------------------------------------------===// + // +@@ -10,8 +9,7 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef __LIBCPP_SRC_INCLUDE_TZDB_TYPES_PRIVATE_H +-#define __LIBCPP_SRC_INCLUDE_TZDB_TYPES_PRIVATE_H ++#pragma once + + #include + #include +@@ -19,7 +17,7 @@ + #include + #include + +-_LIBCPP_BEGIN_NAMESPACE_STD ++#include "velox/external/date/date.h" + + // TODO TZDB + // The helper classes in this header have no constructor but are loaded with +@@ -29,14 +27,17 @@ + // should be reconsidered. (For now the design is kept as is, in case this + // header needs to be public for unforseen reasons.) + +-namespace chrono::__tz { ++namespace facebook::velox::tzdb { + + // Sun>=8 first Sunday on or after the eighth + // Sun<=25 last Sunday on or before the 25th + struct __constrained_weekday { +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI year_month_day operator()(year __year, month __month) const { +- auto __result = static_cast(year_month_day{__year, __month, __day}); +- weekday __wd{static_cast(__result)}; ++ [[nodiscard]] date::year_month_day operator()( ++ date::year __year, ++ date::month __month) const { ++ auto __result = static_cast( ++ date::year_month_day{__year, __month, __day}); ++ date::weekday __wd{static_cast(__result)}; + + if (__comparison == __le) + __result -= __wd - __weekday; +@@ -46,9 +47,9 @@ + return __result; + } + +- weekday __weekday; ++ date::weekday __weekday; + enum __comparison_t { __le, __ge } __comparison; +- day __day; ++ date::day __day; + }; + + // The on field has a few alternative presentations +@@ -57,38 +58,41 @@ + // lastMon the last Monday in the month + // Sun>=8 first Sunday on or after the eighth + // Sun<=25 last Sunday on or before the 25th +-using __on = variant; ++using __on = std::variant; + + enum class __clock { __local, __standard, __universal }; + + struct __at { +- seconds __time{0}; +- __tz::__clock __clock{__tz::__clock::__local}; ++ std::chrono::seconds __time{0}; ++ facebook::velox::tzdb::__clock __clock{ ++ facebook::velox::tzdb::__clock::__local}; + }; + + struct __save { +- seconds __time; ++ std::chrono::seconds __time; + bool __is_dst; + }; + + // The names of the fields match the fields of a Rule. + struct __rule { +- year __from; +- year __to; +- month __in; +- __tz::__on __on; +- __tz::__at __at; +- __tz::__save __save; +- string __letters; ++ date::year __from; ++ date::year __to; ++ date::month __in; ++ facebook::velox::tzdb::__on __on; ++ facebook::velox::tzdb::__at __at; ++ facebook::velox::tzdb::__save __save; ++ std::string __letters; + }; + +-using __rules_storage_type = std::vector>>; // TODO TZDB use flat_map; ++using __rules_storage_type = ++ std::vector>>; // TODO TZDB use ++ // flat_map; + + struct __continuation { + // Non-owning link to the RULE entries. +- __tz::__rules_storage_type* __rule_database_; ++ __rules_storage_type* __rule_database_; + +- seconds __stdoff; ++ std::chrono::seconds __stdoff; + + // The RULES is either a SAVE or a NAME. + // The size_t is used as cache. After loading the rules they are +@@ -97,23 +101,23 @@ + // If this field contains - then standard time always + // applies. This is indicated by the monostate. + // TODO TZDB Investigate implantation the size_t based caching. +- using __rules_t = variant; ++ using __rules_t = std::variant< ++ std::monostate, ++ facebook::velox::tzdb::__save, ++ std::string /*, size_t*/>; + + __rules_t __rules; + +- string __format; ++ std::string __format; + // TODO TZDB the until field can contain more than just a year. + // Parts of the UNTIL, the optional parts are default initialized + // optional __until_; +- year __year = chrono::year::min(); +- month __in{January}; +- __tz::__on __on{chrono::day{1}}; +- __tz::__at __at{chrono::seconds{0}, __tz::__clock::__local}; ++ date::year __year = date::year::min(); ++ date::month __in{date::January}; ++ facebook::velox::tzdb::__on __on{date::day{1}}; ++ facebook::velox::tzdb::__at __at{ ++ std::chrono::seconds{0}, ++ facebook::velox::tzdb::__clock::__local}; + }; + +-} // namespace chrono::__tz +- +-_LIBCPP_END_NAMESPACE_STD +- +-#endif // __LIBCPP_SRC_INCLUDE_TZDB_TYPES_PRIVATE_H +- ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/tzdb.cpp b/fbcode/velox/external/tzdb/tzdb.cpp +--- a/fbcode/velox/external/tzdb/tzdb.cpp ++++ b/fbcode/velox/external/tzdb/tzdb.cpp +@@ -1,4 +1,3 @@ +- + //===----------------------------------------------------------------------===// + // + // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +@@ -9,7 +8,8 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#include <__assert> ++#include ++#include + #include + #include + #include +@@ -20,10 +20,11 @@ + #include + #include + +-#include "include/tzdb/time_zone_private.h" +-#include "include/tzdb/types_private.h" +-#include "include/tzdb/tzdb_list_private.h" +-#include "include/tzdb/tzdb_private.h" ++#include "velox/external/date/date.h" ++#include "velox/external/tzdb/time_zone_private.h" ++#include "velox/external/tzdb/types_private.h" ++#include "velox/external/tzdb/tzdb_list_private.h" ++#include "velox/external/tzdb/tzdb_private.h" + + // Contains a parser for the IANA time zone data files. + // +@@ -46,101 +47,141 @@ + // + // TODO TZDB Implement the Windows mapping in tzdb::current_zone + +-_LIBCPP_BEGIN_NAMESPACE_STD ++namespace facebook::velox::tzdb { + +-namespace chrono { ++using namespace std::chrono_literals; + +-// This function is weak so it can be overriden in the tests. The ++// This function is weak so it can be overridden in the tests. The + // declaration is in the test header test/support/test_tzdb.h +-_LIBCPP_WEAK string_view __libcpp_tzdb_directory() { +-#if defined(__linux__) +- return "/usr/share/zoneinfo/"; +-#else +-# error "unknown path to the IANA Time Zone Database" +-#endif ++std::string __libcpp_tzdb_directory() { ++ struct stat sb; ++ using namespace std; ++#if !defined(__APPLE__) ++ CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo"; ++ CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc"; ++ ++ // Check special path which is valid for buildroot with uclibc builds ++ if (stat(tz_dir_buildroot, &sb) == 0 && S_ISDIR(sb.st_mode)) ++ return tz_dir_buildroot; ++ else if (stat(tz_dir_default, &sb) == 0 && S_ISDIR(sb.st_mode)) ++ return tz_dir_default; ++ else ++ throw runtime_error("discover_tz_dir failed to find zoneinfo\n"); ++#else // __APPLE__ ++ CONSTDATA auto timezone = "/etc/localtime"; ++ if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0)) ++ throw runtime_error("discover_tz_dir failed\n"); ++ string result; ++ unique_ptr rp(new char[sb.st_size]); ++ const auto rp_length = readlink(timezone, rp.get(), sb.st_size); ++ if (rp_length > 0) ++ result = string(rp.get(), rp_length); // readlink doesn't null-terminate ++ else ++ throw system_error(errno, system_category(), "readlink() failed"); ++ auto i = result.find("zoneinfo"); ++ if (i == string::npos) ++ throw runtime_error("discover_tz_dir failed to find zoneinfo\n"); ++ i = result.find('/', i); ++ if (i == string::npos) ++ throw runtime_error("discover_tz_dir failed to find '/'\n"); ++ return result.substr(0, i); ++#endif // __APPLE__ + } + + //===----------------------------------------------------------------------===// + // Details + //===----------------------------------------------------------------------===// + +-[[nodiscard]] static bool __is_whitespace(int __c) { return __c == ' ' || __c == '\t'; } ++[[nodiscard]] static bool __is_whitespace(int __c) { ++ return __c == ' ' || __c == '\t'; ++} + +-static void __skip_optional_whitespace(istream& __input) { +- while (chrono::__is_whitespace(__input.peek())) ++static void __skip_optional_whitespace(std::istream& __input) { ++ while (__is_whitespace(__input.peek())) + __input.get(); + } + +-static void __skip_mandatory_whitespace(istream& __input) { +- if (!chrono::__is_whitespace(__input.get())) ++static void __skip_mandatory_whitespace(std::istream& __input) { ++ if (!__is_whitespace(__input.get())) + std::__throw_runtime_error("corrupt tzdb: expected whitespace"); + +- chrono::__skip_optional_whitespace(__input); ++ __skip_optional_whitespace(__input); + } + +-[[nodiscard]] static bool __is_eol(int __c) { return __c == '\n' || __c == std::char_traits::eof(); } ++[[nodiscard]] static bool __is_eol(int __c) { ++ return __c == '\n' || __c == std::char_traits::eof(); ++} + +-static void __skip_line(istream& __input) { +- while (!chrono::__is_eol(__input.peek())) { ++static void __skip_line(std::istream& __input) { ++ while (!__is_eol(__input.peek())) { + __input.get(); + } + __input.get(); + } + +-static void __skip(istream& __input, char __suffix) { ++static void __skip(std::istream& __input, char __suffix) { + if (std::tolower(__input.peek()) == __suffix) + __input.get(); + } + +-static void __skip(istream& __input, string_view __suffix) { ++static void __skip(std::istream& __input, std::string_view __suffix) { + for (auto __c : __suffix) + if (std::tolower(__input.peek()) == __c) + __input.get(); + } + +-static void __matches(istream& __input, char __expected) { +- _LIBCPP_ASSERT_INTERNAL(!std::isalpha(__expected) || std::islower(__expected), "lowercase characters only here!"); ++static void __matches(std::istream& __input, char __expected) { ++ if (std::isalpha(__expected) && !std::islower(__expected)) { ++ throw std::runtime_error("lowercase characters only here!"); ++ } + char __c = __input.get(); + if (std::tolower(__c) != __expected) + std::__throw_runtime_error( +- (string("corrupt tzdb: expected character '") + __expected + "', got '" + __c + "' instead").c_str()); ++ (std::string("corrupt tzdb: expected character '") + __expected + ++ "', got '" + __c + "' instead") ++ .c_str()); + } + +-static void __matches(istream& __input, string_view __expected) { ++static void __matches(std::istream& __input, std::string_view __expected) { + for (auto __c : __expected) { +- _LIBCPP_ASSERT_INTERNAL(!std::isalpha(__c) || std::islower(__c), "lowercase strings only here!"); ++ if (std::isalpha(__c) && !std::islower(__c)) { ++ throw std::runtime_error("lowercase strings only here!"); ++ } + char __actual = __input.get(); + if (std::tolower(__actual) != __c) + std::__throw_runtime_error( +- (string("corrupt tzdb: expected character '") + __c + "' from string '" + string(__expected) + "', got '" + +- __actual + "' instead") ++ (std::string("corrupt tzdb: expected character '") + __c + ++ "' from string '" + std::string(__expected) + "', got '" + __actual + ++ "' instead") + .c_str()); + } + } + +-[[nodiscard]] static string __parse_string(istream& __input) { +- string __result; ++[[nodiscard]] static std::string __parse_string(std::istream& __input) { ++ std::string __result; + while (true) { + int __c = __input.get(); + switch (__c) { +- case ' ': +- case '\t': +- case '\n': +- __input.unget(); +- [[fallthrough]]; +- case istream::traits_type::eof(): +- if (__result.empty()) +- std::__throw_runtime_error("corrupt tzdb: expected a string"); ++ case ' ': ++ case '\t': ++ case '\n': ++ __input.unget(); ++ [[fallthrough]]; ++ case std::istream::traits_type::eof(): ++ if (__result.empty()) ++ std::__throw_runtime_error("corrupt tzdb: expected a string"); + +- return __result; ++ return __result; + +- default: +- __result.push_back(__c); ++ default: ++ __result.push_back(__c); + } + } + } + +-[[nodiscard]] static int64_t __parse_integral(istream& __input, bool __leading_zero_allowed) { ++[[nodiscard]] static int64_t __parse_integral( ++ std::istream& __input, ++ bool __leading_zero_allowed) { + int64_t __result = __input.get(); + if (__leading_zero_allowed) { + if (__result < '0' || __result > '9') +@@ -177,159 +218,161 @@ + // Calendar + //===----------------------------------------------------------------------===// + +-[[nodiscard]] static day __parse_day(istream& __input) { +- unsigned __result = chrono::__parse_integral(__input, false); ++[[nodiscard]] static date::day __parse_day(std::istream& __input) { ++ unsigned __result = __parse_integral(__input, false); + if (__result > 31) + std::__throw_runtime_error("corrupt tzdb day: value too large"); +- return day{__result}; ++ return date::day{__result}; + } + +-[[nodiscard]] static weekday __parse_weekday(istream& __input) { ++[[nodiscard]] static date::weekday __parse_weekday(std::istream& __input) { + // TZDB allows the shortest unique name. + switch (std::tolower(__input.get())) { +- case 'f': +- chrono::__skip(__input, "riday"); +- return Friday; ++ case 'f': ++ __skip(__input, "riday"); ++ return date::Friday; + +- case 'm': +- chrono::__skip(__input, "onday"); +- return Monday; ++ case 'm': ++ __skip(__input, "onday"); ++ return date::Monday; + +- case 's': +- switch (std::tolower(__input.get())) { +- case 'a': +- chrono::__skip(__input, "turday"); +- return Saturday; ++ case 's': ++ switch (std::tolower(__input.get())) { ++ case 'a': ++ __skip(__input, "turday"); ++ return date::Saturday; + +- case 'u': +- chrono::__skip(__input, "nday"); +- return Sunday; +- } +- break; ++ case 'u': ++ __skip(__input, "nday"); ++ return date::Sunday; ++ } ++ break; + +- case 't': +- switch (std::tolower(__input.get())) { +- case 'h': +- chrono::__skip(__input, "ursday"); +- return Thursday; ++ case 't': ++ switch (std::tolower(__input.get())) { ++ case 'h': ++ __skip(__input, "ursday"); ++ return date::Thursday; + +- case 'u': +- chrono::__skip(__input, "esday"); +- return Tuesday; +- } +- break; +- case 'w': +- chrono::__skip(__input, "ednesday"); +- return Wednesday; ++ case 'u': ++ __skip(__input, "esday"); ++ return date::Tuesday; ++ } ++ break; ++ case 'w': ++ __skip(__input, "ednesday"); ++ return date::Wednesday; + } + + std::__throw_runtime_error("corrupt tzdb weekday: invalid name"); + } + +-[[nodiscard]] static month __parse_month(istream& __input) { ++[[nodiscard]] static date::month __parse_month(std::istream& __input) { + // TZDB allows the shortest unique name. + switch (std::tolower(__input.get())) { +- case 'a': +- switch (std::tolower(__input.get())) { +- case 'p': +- chrono::__skip(__input, "ril"); +- return April; +- +- case 'u': +- chrono::__skip(__input, "gust"); +- return August; +- } +- break; ++ case 'a': ++ switch (std::tolower(__input.get())) { ++ case 'p': ++ __skip(__input, "ril"); ++ return date::April; + +- case 'd': +- chrono::__skip(__input, "ecember"); +- return December; ++ case 'u': ++ __skip(__input, "gust"); ++ return date::August; ++ } ++ break; + +- case 'f': +- chrono::__skip(__input, "ebruary"); +- return February; ++ case 'd': ++ __skip(__input, "ecember"); ++ return date::December; + +- case 'j': +- switch (std::tolower(__input.get())) { +- case 'a': +- chrono::__skip(__input, "nuary"); +- return January; ++ case 'f': ++ __skip(__input, "ebruary"); ++ return date::February; + +- case 'u': ++ case 'j': + switch (std::tolower(__input.get())) { +- case 'n': +- chrono::__skip(__input, 'e'); +- return June; +- +- case 'l': +- chrono::__skip(__input, 'y'); +- return July; ++ case 'a': ++ __skip(__input, "nuary"); ++ return date::January; ++ ++ case 'u': ++ switch (std::tolower(__input.get())) { ++ case 'n': ++ __skip(__input, 'e'); ++ return date::June; ++ ++ case 'l': ++ __skip(__input, 'y'); ++ return date::July; ++ } + } +- } +- break; ++ break; + +- case 'm': +- if (std::tolower(__input.get()) == 'a') +- switch (std::tolower(__input.get())) { +- case 'y': +- return May; ++ case 'm': ++ if (std::tolower(__input.get()) == 'a') ++ switch (std::tolower(__input.get())) { ++ case 'y': ++ return date::May; + +- case 'r': +- chrono::__skip(__input, "ch"); +- return March; +- } +- break; ++ case 'r': ++ __skip(__input, "ch"); ++ return date::March; ++ } ++ break; + +- case 'n': +- chrono::__skip(__input, "ovember"); +- return November; ++ case 'n': ++ __skip(__input, "ovember"); ++ return date::November; + +- case 'o': +- chrono::__skip(__input, "ctober"); +- return October; ++ case 'o': ++ __skip(__input, "ctober"); ++ return date::October; + +- case 's': +- chrono::__skip(__input, "eptember"); +- return September; ++ case 's': ++ __skip(__input, "eptember"); ++ return date::September; + } + std::__throw_runtime_error("corrupt tzdb month: invalid name"); + } + +-[[nodiscard]] static year __parse_year_value(istream& __input) { ++[[nodiscard]] static date::year __parse_year_value(std::istream& __input) { + bool __negative = __input.peek() == '-'; + if (__negative) [[unlikely]] + __input.get(); + + int64_t __result = __parse_integral(__input, true); +- if (__result > static_cast(year::max())) { ++ if (__result > static_cast(date::year::max())) { + if (__negative) +- std::__throw_runtime_error("corrupt tzdb year: year is less than the minimum"); ++ std::__throw_runtime_error( ++ "corrupt tzdb year: year is less than the minimum"); + +- std::__throw_runtime_error("corrupt tzdb year: year is greater than the maximum"); ++ std::__throw_runtime_error( ++ "corrupt tzdb year: year is greater than the maximum"); + } + +- return year{static_cast(__negative ? -__result : __result)}; ++ return date::year{static_cast(__negative ? -__result : __result)}; + } + +-[[nodiscard]] static year __parse_year(istream& __input) { ++[[nodiscard]] static date::year __parse_year(std::istream& __input) { + if (std::tolower(__input.peek()) != 'm') [[likely]] +- return chrono::__parse_year_value(__input); ++ return __parse_year_value(__input); + + __input.get(); + switch (std::tolower(__input.peek())) { +- case 'i': +- __input.get(); +- chrono::__skip(__input, 'n'); +- [[fallthrough]]; ++ case 'i': ++ __input.get(); ++ __skip(__input, 'n'); ++ [[fallthrough]]; + +- case ' ': +- // The m is minimum, even when that is ambiguous. +- return year::min(); ++ case ' ': ++ // The m is minimum, even when that is ambiguous. ++ return date::year::min(); + +- case 'a': +- __input.get(); +- chrono::__skip(__input, 'x'); +- return year::max(); ++ case 'a': ++ __input.get(); ++ __skip(__input, 'x'); ++ return date::year::max(); + } + + std::__throw_runtime_error("corrupt tzdb year: expected 'min' or 'max'"); +@@ -339,44 +382,51 @@ + // TZDB fields + //===----------------------------------------------------------------------===// + +-[[nodiscard]] static year __parse_to(istream& __input, year __only) { ++[[nodiscard]] static date::year __parse_to( ++ std::istream& __input, ++ date::year __only) { + if (std::tolower(__input.peek()) != 'o') +- return chrono::__parse_year(__input); ++ return __parse_year(__input); + + __input.get(); +- chrono::__skip(__input, "nly"); ++ __skip(__input, "nly"); + return __only; + } + +-[[nodiscard]] static __tz::__constrained_weekday::__comparison_t __parse_comparison(istream& __input) { ++[[nodiscard]] static __constrained_weekday::__comparison_t __parse_comparison( ++ std::istream& __input) { + switch (__input.get()) { +- case '>': +- chrono::__matches(__input, '='); +- return __tz::__constrained_weekday::__ge; ++ case '>': ++ __matches(__input, '='); ++ return __constrained_weekday::__ge; + +- case '<': +- chrono::__matches(__input, '='); +- return __tz::__constrained_weekday::__le; ++ case '<': ++ __matches(__input, '='); ++ return __constrained_weekday::__le; + } + std::__throw_runtime_error("corrupt tzdb on: expected '>=' or '<='"); + } + +-[[nodiscard]] static __tz::__on __parse_on(istream& __input) { ++[[nodiscard]] static facebook::velox::tzdb::__on __parse_on( ++ std::istream& __input) { + if (std::isdigit(__input.peek())) +- return chrono::__parse_day(__input); ++ return __parse_day(__input); + + if (std::tolower(__input.peek()) == 'l') { +- chrono::__matches(__input, "last"); +- return weekday_last(chrono::__parse_weekday(__input)); ++ __matches(__input, "last"); ++ return date::weekday_last(__parse_weekday(__input)); + } + +- return __tz::__constrained_weekday{ +- chrono::__parse_weekday(__input), chrono::__parse_comparison(__input), chrono::__parse_day(__input)}; ++ return __constrained_weekday{ ++ __parse_weekday(__input), ++ __parse_comparison(__input), ++ __parse_day(__input)}; + } + +-[[nodiscard]] static seconds __parse_duration(istream& __input) { +- seconds __result{0}; +- int __c = __input.peek(); ++[[nodiscard]] static std::chrono::seconds __parse_duration( ++ std::istream& __input) { ++ std::chrono::seconds __result{0}; ++ int __c = __input.peek(); + bool __negative = __c == '-'; + if (__negative) { + __input.get(); +@@ -386,17 +436,17 @@ + return __result; + } + +- __result += hours(__parse_integral(__input, true)); ++ __result += std::chrono::hours(__parse_integral(__input, true)); + if (__input.peek() != ':') + return __negative ? -__result : __result; + + __input.get(); +- __result += minutes(__parse_integral(__input, true)); ++ __result += std::chrono::minutes(__parse_integral(__input, true)); + if (__input.peek() != ':') + return __negative ? -__result : __result; + + __input.get(); +- __result += seconds(__parse_integral(__input, true)); ++ __result += std::chrono::seconds(__parse_integral(__input, true)); + if (__input.peek() != '.') + return __negative ? -__result : __result; + +@@ -406,70 +456,78 @@ + return __negative ? -__result : __result; + } + +-[[nodiscard]] static __tz::__clock __parse_clock(istream& __input) { ++[[nodiscard]] static facebook::velox::tzdb::__clock __parse_clock( ++ std::istream& __input) { + switch (__input.get()) { // case sensitive +- case 'w': +- return __tz::__clock::__local; +- case 's': +- return __tz::__clock::__standard; +- +- case 'u': +- case 'g': +- case 'z': +- return __tz::__clock::__universal; ++ case 'w': ++ return facebook::velox::tzdb::__clock::__local; ++ case 's': ++ return facebook::velox::tzdb::__clock::__standard; ++ ++ case 'u': ++ case 'g': ++ case 'z': ++ return facebook::velox::tzdb::__clock::__universal; + } + + __input.unget(); +- return __tz::__clock::__local; ++ return facebook::velox::tzdb::__clock::__local; + } + +-[[nodiscard]] static bool __parse_dst(istream& __input, seconds __offset) { ++[[nodiscard]] static bool __parse_dst( ++ std::istream& __input, ++ std::chrono::seconds __offset) { + switch (__input.get()) { // case sensitive +- case 's': +- return false; ++ case 's': ++ return false; + +- case 'd': +- return true; ++ case 'd': ++ return true; + } + + __input.unget(); + return __offset != 0s; + } + +-[[nodiscard]] static __tz::__at __parse_at(istream& __input) { ++[[nodiscard]] static facebook::velox::tzdb::__at __parse_at( ++ std::istream& __input) { + return {__parse_duration(__input), __parse_clock(__input)}; + } + +-[[nodiscard]] static __tz::__save __parse_save(istream& __input) { +- seconds __time = chrono::__parse_duration(__input); +- return {__time, chrono::__parse_dst(__input, __time)}; ++[[nodiscard]] static facebook::velox::tzdb::__save __parse_save( ++ std::istream& __input) { ++ std::chrono::seconds __time = __parse_duration(__input); ++ return {__time, __parse_dst(__input, __time)}; + } + +-[[nodiscard]] static string __parse_letters(istream& __input) { +- string __result = __parse_string(__input); ++[[nodiscard]] static std::string __parse_letters(std::istream& __input) { ++ std::string __result = __parse_string(__input); + // Canonicalize "-" to "" since they are equivalent in the specification. + return __result != "-" ? __result : ""; + } + +-[[nodiscard]] static __tz::__continuation::__rules_t __parse_rules(istream& __input) { ++[[nodiscard]] static __continuation::__rules_t __parse_rules( ++ std::istream& __input) { + int __c = __input.peek(); + // A single - is not a SAVE but a special case. + if (__c == '-') { + __input.get(); +- if (chrono::__is_whitespace(__input.peek())) +- return monostate{}; ++ if (__is_whitespace(__input.peek())) ++ return std::monostate{}; + __input.unget(); +- return chrono::__parse_save(__input); ++ return __parse_save(__input); + } + + if (std::isdigit(__c) || __c == '+') +- return chrono::__parse_save(__input); ++ return __parse_save(__input); + +- return chrono::__parse_string(__input); ++ return __parse_string(__input); + } + +-[[nodiscard]] static __tz::__continuation __parse_continuation(__tz::__rules_storage_type& __rules, istream& __input) { +- __tz::__continuation __result; ++[[nodiscard]] static facebook::velox::tzdb::__continuation __parse_continuation( ++ __rules_storage_type& __rules, ++ std::istream& __input) { ++ facebook::velox::tzdb::__continuation __result; + + __result.__rule_database_ = std::addressof(__rules); + +@@ -478,29 +536,29 @@ + // These fields have different suffix letters, these letters seem + // not to be used so do not allow any of them. + +- __result.__stdoff = chrono::__parse_duration(__input); +- chrono::__skip_mandatory_whitespace(__input); +- __result.__rules = chrono::__parse_rules(__input); +- chrono::__skip_mandatory_whitespace(__input); +- __result.__format = chrono::__parse_string(__input); +- chrono::__skip_optional_whitespace(__input); ++ __result.__stdoff = __parse_duration(__input); ++ __skip_mandatory_whitespace(__input); ++ __result.__rules = __parse_rules(__input); ++ __skip_mandatory_whitespace(__input); ++ __result.__format = __parse_string(__input); ++ __skip_optional_whitespace(__input); + +- if (chrono::__is_eol(__input.peek())) ++ if (__is_eol(__input.peek())) + return __result; +- __result.__year = chrono::__parse_year(__input); +- chrono::__skip_optional_whitespace(__input); ++ __result.__year = __parse_year(__input); ++ __skip_optional_whitespace(__input); + +- if (chrono::__is_eol(__input.peek())) ++ if (__is_eol(__input.peek())) + return __result; +- __result.__in = chrono::__parse_month(__input); +- chrono::__skip_optional_whitespace(__input); ++ __result.__in = __parse_month(__input); ++ __skip_optional_whitespace(__input); + +- if (chrono::__is_eol(__input.peek())) ++ if (__is_eol(__input.peek())) + return __result; +- __result.__on = chrono::__parse_on(__input); +- chrono::__skip_optional_whitespace(__input); ++ __result.__on = __parse_on(__input); ++ __skip_optional_whitespace(__input); + +- if (chrono::__is_eol(__input.peek())) ++ if (__is_eol(__input.peek())) + return __result; + __result.__at = __parse_at(__input); + +@@ -511,24 +569,26 @@ + // Time Zone Database entries + //===----------------------------------------------------------------------===// + +-static string __parse_version(istream& __input) { ++static std::string __parse_version(std::istream& __input) { + // The first line in tzdata.zi contains + // # version YYYYw + // The parser expects this pattern + // #\s*version\s*\(.*) + // This part is not documented. +- chrono::__matches(__input, '#'); +- chrono::__skip_optional_whitespace(__input); +- chrono::__matches(__input, "version"); +- chrono::__skip_mandatory_whitespace(__input); +- return chrono::__parse_string(__input); ++ __matches(__input, '#'); ++ __skip_optional_whitespace(__input); ++ __matches(__input, "version"); ++ __skip_mandatory_whitespace(__input); ++ return __parse_string(__input); + } + + [[nodiscard]] +-static __tz::__rule& __create_entry(__tz::__rules_storage_type& __rules, const string& __name) { +- auto __result = [&]() -> __tz::__rule& { +- auto& __rule = __rules.emplace_back(__name, vector<__tz::__rule>{}); +- return __rule.second.emplace_back(); ++static __rule& __create_entry( ++ __rules_storage_type& __rules, ++ const std::string& __name) { ++ auto __result = [&]() -> __rule& { ++ auto& rule = __rules.emplace_back(__name, std::vector<__rule>{}); ++ return rule.second.emplace_back(); + }; + + if (__rules.empty()) +@@ -539,133 +599,155 @@ + if (__rules.back().first == __name) + return __rules.back().second.emplace_back(); + +- if (auto __it = ranges::find(__rules, __name, [](const auto& __r) { return __r.first; }); +- __it != ranges::end(__rules)) ++ if (auto __it = std::find_if( ++ __rules.begin(), ++ __rules.end(), ++ [&__name](const auto& __r) { return __r.first == __name; }); ++ __it != __rules.end()) + return __it->second.emplace_back(); + + return __result(); + } + +-static void __parse_rule(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) { +- chrono::__skip_mandatory_whitespace(__input); +- string __name = chrono::__parse_string(__input); +- +- __tz::__rule& __rule = __create_entry(__rules, __name); +- +- chrono::__skip_mandatory_whitespace(__input); +- __rule.__from = chrono::__parse_year(__input); +- chrono::__skip_mandatory_whitespace(__input); +- __rule.__to = chrono::__parse_to(__input, __rule.__from); +- chrono::__skip_mandatory_whitespace(__input); +- chrono::__matches(__input, '-'); +- chrono::__skip_mandatory_whitespace(__input); +- __rule.__in = chrono::__parse_month(__input); +- chrono::__skip_mandatory_whitespace(__input); +- __rule.__on = chrono::__parse_on(__input); +- chrono::__skip_mandatory_whitespace(__input); ++static void __parse_rule( ++ tzdb& __tzdb, ++ __rules_storage_type& __rules, ++ std::istream& __input) { ++ __skip_mandatory_whitespace(__input); ++ std::string __name = __parse_string(__input); ++ ++ __rule& __rule = __create_entry(__rules, __name); ++ ++ __skip_mandatory_whitespace(__input); ++ __rule.__from = __parse_year(__input); ++ __skip_mandatory_whitespace(__input); ++ __rule.__to = __parse_to(__input, __rule.__from); ++ __skip_mandatory_whitespace(__input); ++ __matches(__input, '-'); ++ __skip_mandatory_whitespace(__input); ++ __rule.__in = __parse_month(__input); ++ __skip_mandatory_whitespace(__input); ++ __rule.__on = __parse_on(__input); ++ __skip_mandatory_whitespace(__input); + __rule.__at = __parse_at(__input); +- chrono::__skip_mandatory_whitespace(__input); ++ __skip_mandatory_whitespace(__input); + __rule.__save = __parse_save(__input); +- chrono::__skip_mandatory_whitespace(__input); +- __rule.__letters = chrono::__parse_letters(__input); +- chrono::__skip_line(__input); ++ __skip_mandatory_whitespace(__input); ++ __rule.__letters = __parse_letters(__input); ++ __skip_line(__input); + } + +-static void __parse_zone(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) { +- chrono::__skip_mandatory_whitespace(__input); +- auto __p = std::make_unique(chrono::__parse_string(__input), __rules); +- vector<__tz::__continuation>& __continuations = __p->__continuations(); +- chrono::__skip_mandatory_whitespace(__input); ++static void __parse_zone( ++ tzdb& __tzdb, ++ __rules_storage_type& __rules, ++ std::istream& __input) { ++ __skip_mandatory_whitespace(__input); ++ auto __p = ++ std::make_unique(__parse_string(__input), __rules); ++ std::vector& __continuations = __p->__continuations(); ++ __skip_mandatory_whitespace(__input); + + do { + // The first line must be valid, continuations are optional. + __continuations.emplace_back(__parse_continuation(__rules, __input)); +- chrono::__skip_line(__input); +- chrono::__skip_optional_whitespace(__input); ++ __skip_line(__input); ++ __skip_optional_whitespace(__input); + } while (std::isdigit(__input.peek()) || __input.peek() == '-'); + + __tzdb.zones.emplace_back(time_zone::__create(std::move(__p))); + } + +-static void __parse_link(tzdb& __tzdb, istream& __input) { +- chrono::__skip_mandatory_whitespace(__input); +- string __target = chrono::__parse_string(__input); +- chrono::__skip_mandatory_whitespace(__input); +- string __name = chrono::__parse_string(__input); +- chrono::__skip_line(__input); ++static void __parse_link(tzdb& __tzdb, std::istream& __input) { ++ __skip_mandatory_whitespace(__input); ++ std::string __target = __parse_string(__input); ++ __skip_mandatory_whitespace(__input); ++ std::string __name = __parse_string(__input); ++ __skip_line(__input); + +- __tzdb.links.emplace_back(std::__private_constructor_tag{}, std::move(__name), std::move(__target)); ++ __tzdb.links.emplace_back(std::move(__name), std::move(__target)); + } + +-static void __parse_tzdata(tzdb& __db, __tz::__rules_storage_type& __rules, istream& __input) { ++static void __parse_tzdata( ++ tzdb& __db, ++ __rules_storage_type& __rules, ++ std::istream& __input) { + while (true) { + int __c = std::tolower(__input.get()); + + switch (__c) { +- case istream::traits_type::eof(): +- return; ++ case std::istream::traits_type::eof(): ++ return; + +- case ' ': +- case '\t': +- case '\n': +- break; ++ case ' ': ++ case '\t': ++ case '\n': ++ break; + +- case '#': +- chrono::__skip_line(__input); +- break; ++ case '#': ++ __skip_line(__input); ++ break; + +- case 'r': +- chrono::__skip(__input, "ule"); +- chrono::__parse_rule(__db, __rules, __input); +- break; ++ case 'r': ++ __skip(__input, "ule"); ++ __parse_rule(__db, __rules, __input); ++ break; + +- case 'z': +- chrono::__skip(__input, "one"); +- chrono::__parse_zone(__db, __rules, __input); +- break; ++ case 'z': ++ __skip(__input, "one"); ++ __parse_zone(__db, __rules, __input); ++ break; + +- case 'l': +- chrono::__skip(__input, "ink"); +- chrono::__parse_link(__db, __input); +- break; ++ case 'l': ++ __skip(__input, "ink"); ++ __parse_link(__db, __input); ++ break; + +- default: +- std::__throw_runtime_error("corrupt tzdb: unexpected input"); ++ default: ++ std::__throw_runtime_error("corrupt tzdb: unexpected input"); + } + } + } + +-static void __parse_leap_seconds(vector& __leap_seconds, istream&& __input) { ++static void __parse_leap_seconds( ++ std::vector& __leap_seconds, ++ std::istream&& __input) { + // The file stores dates since 1 January 1900, 00:00:00, we want + // seconds since 1 January 1970. +- constexpr auto __offset = sys_days{1970y / January / 1} - sys_days{1900y / January / 1}; ++ constexpr auto __offset = ++ date::sys_days{date::year(1970) / date::January / 1} - ++ date::sys_days{date::year(1900) / date::January / 1}; + + struct __entry { +- sys_seconds __timestamp; +- seconds __value; ++ __entry(date::sys_seconds __timestamp, std::chrono::seconds __value) ++ : __timestamp(__timestamp), __value(__value) {} ++ ++ date::sys_seconds __timestamp; ++ std::chrono::seconds __value; + }; +- vector<__entry> __entries; ++ std::vector<__entry> __entries; + [&] { + while (true) { + switch (__input.peek()) { +- case istream::traits_type::eof(): +- return; +- +- case ' ': +- case '\t': +- case '\n': +- __input.get(); +- continue; +- +- case '#': +- chrono::__skip_line(__input); +- continue; ++ case std::istream::traits_type::eof(): ++ return; ++ ++ case ' ': ++ case '\t': ++ case '\n': ++ __input.get(); ++ continue; ++ ++ case '#': ++ __skip_line(__input); ++ continue; + } + +- sys_seconds __date = sys_seconds{seconds{chrono::__parse_integral(__input, false)}} - __offset; +- chrono::__skip_mandatory_whitespace(__input); +- seconds __value{chrono::__parse_integral(__input, false)}; +- chrono::__skip_line(__input); ++ date::sys_seconds __date = date::sys_seconds{std::chrono::seconds{ ++ __parse_integral(__input, false)}} - ++ __offset; ++ __skip_mandatory_whitespace(__input); ++ std::chrono::seconds __value{__parse_integral(__input, false)}; ++ __skip_line(__input); + + __entries.emplace_back(__date, __value); + } +@@ -673,34 +755,48 @@ + // The Standard requires the leap seconds to be sorted. The file + // leap-seconds.list usually provides them in sorted order, but that is not + // guaranteed so we ensure it here. +- ranges::sort(__entries, {}, &__entry::__timestamp); ++ std::sort( ++ __entries.begin(), ++ __entries.end(), ++ [](const auto& left_entry, const auto& right_entry) { ++ return left_entry.__timestamp < right_entry.__timestamp; ++ }); + + // The database should contain the number of seconds inserted by a leap + // second (1 or -1). So the difference between the two elements is stored. + // std::ranges::views::adjacent has not been implemented yet. +- (void)ranges::adjacent_find(__entries, [&](const __entry& __first, const __entry& __second) { +- __leap_seconds.emplace_back( +- std::__private_constructor_tag{}, __second.__timestamp, __second.__value - __first.__value); +- return false; +- }); +-} +- +-void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) { +- filesystem::path __root = chrono::__libcpp_tzdb_directory(); +- ifstream __tzdata{__root / "tzdata.zi"}; +- +- __tzdb.version = chrono::__parse_version(__tzdata); +- chrono::__parse_tzdata(__tzdb, __rules, __tzdata); +- ranges::sort(__tzdb.zones); +- ranges::sort(__tzdb.links); +- ranges::sort(__rules, {}, [](const auto& p) { return p.first; }); ++ (void)std::adjacent_find( ++ __entries.begin(), ++ __entries.end(), ++ [&](const __entry& __first, const __entry& __second) { ++ __leap_seconds.emplace_back( ++ __second.__timestamp, __second.__value - __first.__value); ++ return false; ++ }); ++} ++ ++void __init_tzdb(tzdb& __tzdb, __rules_storage_type& __rules) { ++ std::filesystem::path __root = __libcpp_tzdb_directory(); ++ std::ifstream __tzdata{__root / "tzdata.zi"}; ++ ++ __tzdb.version = __parse_version(__tzdata); ++ __parse_tzdata(__tzdb, __rules, __tzdata); ++ std::sort(__tzdb.zones.begin(), __tzdb.zones.end()); ++ std::sort(__tzdb.links.begin(), __tzdb.links.end()); ++ std::sort( ++ __rules.begin(), ++ __rules.end(), ++ [](const auto& __left, const auto& __right) { ++ return __left.first < __right.first; ++ }); + + // There are two files with the leap second information + // - leapseconds as specified by zic + // - leap-seconds.list the source data + // The latter is much easier to parse, it seems Howard shares that + // opinion. +- chrono::__parse_leap_seconds(__tzdb.leap_seconds, ifstream{__root / "leap-seconds.list"}); ++ __parse_leap_seconds( ++ __tzdb.leap_seconds, std::ifstream{__root / "leap-seconds.list"}); + } + + #ifdef _WIN32 +@@ -708,40 +804,7 @@ + // TODO TZDB Implement this on Windows. + std::__throw_runtime_error("unknown time zone"); + } +-#else // ifdef _WIN32 +- +-[[nodiscard]] static string __current_zone_environment() { +- if (const char* __tz = std::getenv("TZ")) +- return __tz; +- +- return {}; +-} +- +-[[nodiscard]] static string __current_zone_etc_localtime() { +- filesystem::path __path = "/etc/localtime"; +- if (!filesystem::exists(__path) || !filesystem::is_symlink(__path)) +- return {}; +- +- filesystem::path __tz = filesystem::read_symlink(__path); +- // The path may be a relative path, in that case convert it to an absolute +- // path based on the proper initial directory. +- if (__tz.is_relative()) +- __tz = filesystem::canonical("/etc" / __tz); +- +- return filesystem::relative(__tz, "/usr/share/zoneinfo/"); +-} +- +-[[nodiscard]] static string __current_zone_etc_timezone() { +- filesystem::path __path = "/etc/timezone"; +- if (!filesystem::exists(__path)) +- return {}; +- +- ifstream __f(__path); +- string __name; +- std::getline(__f, __name); +- return __name; +-} +- ++#else // ifdef _WIN32 + [[nodiscard]] static const time_zone* __current_zone_posix(const tzdb& tzdb) { + // On POSIX systems there are several ways to configure the time zone. + // In order of priority they are: +@@ -760,33 +823,38 @@ + // + // - The time zone name is the target of the symlink /etc/localtime + // relative to /usr/share/zoneinfo/ +- // +- // - The file /etc/timezone. This text file contains the name of the time +- // zone. +- // +- // On Linux systems it seems /etc/timezone is deprecated and being phased out. +- // This file is used when /etc/localtime does not exist, or when it exists but +- // is not a symlink. For more information and links see +- // https://github.com/llvm/llvm-project/issues/105634 + +- string __name = chrono::__current_zone_environment(); ++ // The algorithm is like this: ++ // - If the environment variable TZ is set and points to a valid ++ // record use this value. ++ // - Else use the name based on the `/etc/localtime` symlink. + +- // Ignore invalid names in the environment. +- if (!__name.empty()) +- if (const time_zone* __result = tzdb.__locate_zone(__name)) ++ if (const char* __tz = getenv("TZ")) ++ if (const time_zone* __result = tzdb.__locate_zone(__tz)) + return __result; + +- __name = chrono::__current_zone_etc_localtime(); +- if (__name.empty()) +- __name = chrono::__current_zone_etc_timezone(); ++ std::filesystem::path __path = "/etc/localtime"; ++ if (!std::filesystem::exists(__path)) ++ std::__throw_runtime_error( ++ "tzdb: the symlink '/etc/localtime' does not exist"); + +- if (__name.empty()) +- std::__throw_runtime_error("tzdb: unable to determine the name of the current time zone"); ++ if (!std::filesystem::is_symlink(__path)) ++ std::__throw_runtime_error( ++ "tzdb: the path '/etc/localtime' is not a symlink"); + ++ std::filesystem::path __tz = std::filesystem::read_symlink(__path); ++ // The path may be a relative path, in that case convert it to an absolute ++ // path based on the proper initial directory. ++ if (__tz.is_relative()) ++ __tz = std::filesystem::canonical("/etc" / __tz); ++ ++ std::string __name = std::filesystem::relative(__tz, "/usr/share/zoneinfo/"); + if (const time_zone* __result = tzdb.__locate_zone(__name)) + return __result; + +- std::__throw_runtime_error(("tzdb: the time zone '" + __name + "' is not found in the database").c_str()); ++ std::__throw_runtime_error( ++ ("tzdb: the time zone '" + __name + "' is not found in the database") ++ .c_str()); + } + #endif // ifdef _WIN32 + +@@ -794,33 +862,30 @@ + // Public API + //===----------------------------------------------------------------------===// + +-_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list() { ++tzdb_list& get_tzdb_list() { + static tzdb_list __result{new tzdb_list::__impl()}; + return __result; + } + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const time_zone* tzdb::__current_zone() const { ++[[nodiscard]] const time_zone* tzdb::__current_zone() const { + #ifdef _WIN32 +- return chrono::__current_zone_windows(*this); ++ return __current_zone_windows(*this); + #else +- return chrono::__current_zone_posix(*this); ++ return __current_zone_posix(*this); + #endif + } + +-_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const tzdb& reload_tzdb() { +- if (chrono::remote_version() == chrono::get_tzdb().version) +- return chrono::get_tzdb(); ++const tzdb& reload_tzdb() { ++ if (remote_version() == get_tzdb().version) ++ return get_tzdb(); + +- return chrono::get_tzdb_list().__implementation().__load(); ++ return get_tzdb_list().__implementation().__load(); + } + +-_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version() { +- filesystem::path __root = chrono::__libcpp_tzdb_directory(); +- ifstream __tzdata{__root / "tzdata.zi"}; +- return chrono::__parse_version(__tzdata); ++std::string remote_version() { ++ std::filesystem::path __root = __libcpp_tzdb_directory(); ++ std::ifstream __tzdata{__root / "tzdata.zi"}; ++ return __parse_version(__tzdata); + } + +-} // namespace chrono +- +-_LIBCPP_END_NAMESPACE_STD +- ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/tzdb.h b/fbcode/velox/external/tzdb/tzdb.h +--- a/fbcode/velox/external/tzdb/tzdb.h ++++ b/fbcode/velox/external/tzdb/tzdb.h +@@ -9,49 +9,37 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP___CHRONO_TZDB_H +-#define _LIBCPP___CHRONO_TZDB_H ++#pragma once + +-#include +-// Enable the contents of the header only when libc++ was built with experimental features enabled. +-#if _LIBCPP_HAS_EXPERIMENTAL_TZDB ++#include ++#include ++#include ++#include ++#include ++#include "velox/external/tzdb/leap_second.h" ++#include "velox/external/tzdb/time_zone.h" ++#include "velox/external/tzdb/time_zone_link.h" + +-# include <__algorithm/ranges_lower_bound.h> +-# include <__chrono/leap_second.h> +-# include <__chrono/time_zone.h> +-# include <__chrono/time_zone_link.h> +-# include <__config> +-# include <__memory/addressof.h> +-# include <__vector/vector.h> +-# include +-# include +-# include +- +-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +-# pragma GCC system_header +-# endif +- +-_LIBCPP_PUSH_MACROS +-# include <__undef_macros> +- +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION +- +-namespace chrono { ++namespace facebook::velox::tzdb { + + struct tzdb { +- string version; +- vector zones; +- vector links; ++ std::string version; ++ std::vector zones; ++ std::vector links; + +- vector leap_seconds; ++ std::vector leap_seconds; + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const time_zone* __locate_zone(string_view __name) const { ++ [[nodiscard]] const time_zone* __locate_zone(std::string_view __name) const { + if (const time_zone* __result = __find_in_zone(__name)) + return __result; + +- if (auto __it = ranges::lower_bound(links, __name, {}, &time_zone_link::name); ++ if (auto __it = std::lower_bound( ++ links.begin(), ++ links.end(), ++ __name, ++ [](const time_zone_link& link, const std::string_view& name) { ++ return link.name() < name; ++ }); + __it != links.end() && __it->name() == __name) + if (const time_zone* __result = __find_in_zone(__it->target())) + return __result; +@@ -59,38 +47,33 @@ + return nullptr; + } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const time_zone* locate_zone(string_view __name) const { ++ [[nodiscard]] const time_zone* locate_zone(std::string_view __name) const { + if (const time_zone* __result = __locate_zone(__name)) + return __result; + + std::__throw_runtime_error("tzdb: requested time zone not found"); + } + +- [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI const time_zone* current_zone() const { ++ [[nodiscard]] const time_zone* current_zone() const { + return __current_zone(); + } + +-private: +- _LIBCPP_HIDE_FROM_ABI const time_zone* __find_in_zone(string_view __name) const noexcept { +- if (auto __it = ranges::lower_bound(zones, __name, {}, &time_zone::name); ++ private: ++ const time_zone* __find_in_zone(std::string_view __name) const noexcept { ++ if (auto __it = std::lower_bound( ++ zones.begin(), ++ zones.end(), ++ __name, ++ [](const time_zone& zone, const std::string_view& name) { ++ return zone.name() < name; ++ }); + __it != zones.end() && __it->name() == __name) + return std::addressof(*__it); + + return nullptr; + } + +- [[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const time_zone* __current_zone() const; ++ [[nodiscard]] const time_zone* __current_zone() const; + }; + +-} // namespace chrono +- +-# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && +- // _LIBCPP_HAS_LOCALIZATION +- +-_LIBCPP_END_NAMESPACE_STD +- +-_LIBCPP_POP_MACROS +- +-#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB +- +-#endif // _LIBCPP___CHRONO_TZDB_H ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/tzdb_list.cpp b/fbcode/velox/external/tzdb/tzdb_list.cpp +--- a/fbcode/velox/external/tzdb/tzdb_list.cpp ++++ b/fbcode/velox/external/tzdb/tzdb_list.cpp +@@ -1,4 +1,3 @@ +- + //===----------------------------------------------------------------------===// + // + // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +@@ -9,37 +8,34 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#include +- +-#include "include/tzdb/tzdb_list_private.h" ++#include "velox/external/tzdb/tzdb_list_private.h" + +-_LIBCPP_BEGIN_NAMESPACE_STD ++namespace facebook::velox::tzdb { + +-namespace chrono { +- +-_LIBCPP_EXPORTED_FROM_ABI tzdb_list::~tzdb_list() { delete __impl_; } ++tzdb_list::~tzdb_list() { ++ delete __impl_; ++} + +-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const tzdb& tzdb_list::__front() const noexcept { return __impl_->__front(); } ++[[nodiscard]] const tzdb& tzdb_list::__front() const noexcept { ++ return __impl_->__front(); ++} + +-_LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::__erase_after(const_iterator __p) { ++tzdb_list::const_iterator tzdb_list::__erase_after(const_iterator __p) { + return __impl_->__erase_after(__p); + } + +-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::__begin() const noexcept { ++[[nodiscard]] tzdb_list::const_iterator tzdb_list::__begin() const noexcept { + return __impl_->__begin(); + } +-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::__end() const noexcept { ++[[nodiscard]] tzdb_list::const_iterator tzdb_list::__end() const noexcept { + return __impl_->__end(); + } + +-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::__cbegin() const noexcept { ++[[nodiscard]] tzdb_list::const_iterator tzdb_list::__cbegin() const noexcept { + return __impl_->__begin(); + } +-[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::__cend() const noexcept { ++[[nodiscard]] tzdb_list::const_iterator tzdb_list::__cend() const noexcept { + return __impl_->__end(); + } + +-} // namespace chrono +- +-_LIBCPP_END_NAMESPACE_STD +- ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/tzdb_list.h b/fbcode/velox/external/tzdb/tzdb_list.h +--- a/fbcode/velox/external/tzdb/tzdb_list.h ++++ b/fbcode/velox/external/tzdb/tzdb_list.h +@@ -1,4 +1,3 @@ +- + // -*- C++ -*- + //===----------------------------------------------------------------------===// + // +@@ -10,29 +9,14 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP___CHRONO_TZDB_LIST_H +-#define _LIBCPP___CHRONO_TZDB_LIST_H +- +-#include +-// Enable the contents of the header only when libc++ was built with experimental features enabled. +-#if _LIBCPP_HAS_EXPERIMENTAL_TZDB +- +-# include <__chrono/time_zone.h> +-# include <__chrono/tzdb.h> +-# include <__config> +-# include <__fwd/string.h> +-# include +-# include +- +-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +-# pragma GCC system_header +-# endif +- +-_LIBCPP_BEGIN_NAMESPACE_STD ++#pragma once + +-# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION ++#include ++#include ++#include "velox/external/tzdb/time_zone.h" ++#include "velox/external/tzdb/tzdb.h" + +-namespace chrono { ++namespace facebook::velox::tzdb { + + // TODO TZDB + // Libc++ recently switched to only export __ugly_names from the dylib. +@@ -40,71 +24,76 @@ + // should be adapted to this new style. The other tzdb headers should be + // evaluated too. + +-class _LIBCPP_AVAILABILITY_TZDB tzdb_list { +-public: ++class tzdb_list { ++ public: + class __impl; // public to allow construction in dylib +- _LIBCPP_HIDE_FROM_ABI explicit tzdb_list(__impl* __p) : __impl_(__p) { +- _LIBCPP_ASSERT_NON_NULL(__impl_ != nullptr, "initialized time_zone without a valid pimpl object"); ++ explicit tzdb_list(__impl* __p) : __impl_(__p) { ++ // _LIBCPP_ASSERT_NON_NULL(__impl_ != nullptr, "initialized time_zone ++ // without a valid pimpl object"); + } +- _LIBCPP_EXPORTED_FROM_ABI ~tzdb_list(); ++ ~tzdb_list(); + +- tzdb_list(const tzdb_list&) = delete; ++ tzdb_list(const tzdb_list&) = delete; + tzdb_list& operator=(const tzdb_list&) = delete; + +- using const_iterator = forward_list::const_iterator; ++ using const_iterator = std::forward_list::const_iterator; + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const tzdb& front() const noexcept { return __front(); } ++ [[nodiscard]] const tzdb& front() const noexcept { ++ return __front(); ++ } + +- _LIBCPP_HIDE_FROM_ABI const_iterator erase_after(const_iterator __p) { return __erase_after(__p); } ++ const_iterator erase_after(const_iterator __p) { ++ return __erase_after(__p); ++ } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator begin() const noexcept { return __begin(); } +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator end() const noexcept { return __end(); } ++ [[nodiscard]] const_iterator begin() const noexcept { ++ return __begin(); ++ } ++ [[nodiscard]] const_iterator end() const noexcept { ++ return __end(); ++ } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator cbegin() const noexcept { return __cbegin(); } +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI const_iterator cend() const noexcept { return __cend(); } ++ [[nodiscard]] const_iterator cbegin() const noexcept { ++ return __cbegin(); ++ } ++ [[nodiscard]] const_iterator cend() const noexcept { ++ return __cend(); ++ } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __impl& __implementation() { return *__impl_; } ++ [[nodiscard]] __impl& __implementation() { ++ return *__impl_; ++ } + +-private: +- [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const tzdb& __front() const noexcept; ++ private: ++ [[nodiscard]] const tzdb& __front() const noexcept; + +- _LIBCPP_EXPORTED_FROM_ABI const_iterator __erase_after(const_iterator __p); ++ const_iterator __erase_after(const_iterator __p); + +- [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator __begin() const noexcept; +- [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator __end() const noexcept; ++ [[nodiscard]] const_iterator __begin() const noexcept; ++ [[nodiscard]] const_iterator __end() const noexcept; + +- [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator __cbegin() const noexcept; +- [[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI const_iterator __cend() const noexcept; ++ [[nodiscard]] const_iterator __cbegin() const noexcept; ++ [[nodiscard]] const_iterator __cend() const noexcept; + + __impl* __impl_; + }; + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list(); ++[[nodiscard]] tzdb_list& get_tzdb_list(); + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline const tzdb& get_tzdb() { ++[[nodiscard]] inline const tzdb& get_tzdb() { + return get_tzdb_list().front(); + } + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline const time_zone* locate_zone(string_view __name) { ++[[nodiscard]] inline const time_zone* locate_zone(std::string_view __name) { + return get_tzdb().locate_zone(__name); + } + +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline const time_zone* current_zone() { ++[[nodiscard]] inline const time_zone* current_zone() { + return get_tzdb().current_zone(); + } + +-_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const tzdb& reload_tzdb(); +- +-[[nodiscard]] _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version(); +- +-} // namespace chrono +- +-# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && +- // _LIBCPP_HAS_LOCALIZATION +- +-_LIBCPP_END_NAMESPACE_STD +- +-#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB ++const tzdb& reload_tzdb(); + +-#endif // _LIBCPP___CHRONO_TZDB_LIST_H ++[[nodiscard]] std::string remote_version(); + ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/tzdb_list_private.h b/fbcode/velox/external/tzdb/tzdb_list_private.h +--- a/fbcode/velox/external/tzdb/tzdb_list_private.h ++++ b/fbcode/velox/external/tzdb/tzdb_list_private.h +@@ -1,4 +1,3 @@ +- + //===----------------------------------------------------------------------===// + // + // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +@@ -9,26 +8,18 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_PRIVATE_H +-#define _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_PRIVATE_H ++#pragma once + +-#include <__mutex/unique_lock.h> + #include ++#include "velox/external/tzdb/types_private.h" ++#include "velox/external/tzdb/tzdb_list.h" ++#include "velox/external/tzdb/tzdb_private.h" + +-// When threads are not available the locking is not required. +-// When threads are available, we use std::mutex over std::shared_mutex +-// due to the increased overhead of std::shared_mutex. +-// See shared_mutex_vs_mutex.bench.cpp + #if _LIBCPP_HAS_THREADS +-# include ++#include + #endif + +-#include "types_private.h" +-#include "tzdb_private.h" +- +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-namespace chrono { ++namespace facebook::velox::tzdb { + + //===----------------------------------------------------------------------===// + // Private API +@@ -45,8 +36,10 @@ + // Since the nodes of a forward_list are stable it's possible to store pointers + // and references to these nodes. + class tzdb_list::__impl { +-public: +- __impl() { __load_no_lock(); } ++ public: ++ __impl() { ++ __load_no_lock(); ++ } + + [[nodiscard]] const tzdb& __load() { + #if _LIBCPP_HAS_THREADS +@@ -70,7 +63,8 @@ + unique_lock __lock{__mutex_}; + #endif + +- __rules_.erase_after(std::next(__rules_.cbegin(), std::distance(__tzdb_.cbegin(), __p))); ++ __rules_.erase_after( ++ std::next(__rules_.cbegin(), std::distance(__tzdb_.cbegin(), __p))); + return __tzdb_.erase_after(__p); + } + +@@ -81,26 +75,24 @@ + return __tzdb_.begin(); + } + const_iterator __end() const noexcept { +- // forward_list::end does not access the list, so no need to take a lock. ++ // forward_list::end does not access the list, so no need to take a ++ // lock. + return __tzdb_.end(); + } + +-private: ++ private: + // Loads the tzdbs + // pre: The caller ensures the locking, if needed, is done. +- void __load_no_lock() { chrono::__init_tzdb(__tzdb_.emplace_front(), __rules_.emplace_front()); } ++ void __load_no_lock() { ++ __init_tzdb(__tzdb_.emplace_front(), __rules_.emplace_front()); ++ } + + #if _LIBCPP_HAS_THREADS + mutable mutex __mutex_; + #endif +- forward_list __tzdb_; ++ std::forward_list __tzdb_; + +- forward_list<__tz::__rules_storage_type> __rules_; ++ std::forward_list<__rules_storage_type> __rules_; + }; + +-} // namespace chrono +- +-_LIBCPP_END_NAMESPACE_STD +- +-#endif // _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_PRIVATE_H +- ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/tzdb_private.h b/fbcode/velox/external/tzdb/tzdb_private.h +--- a/fbcode/velox/external/tzdb/tzdb_private.h ++++ b/fbcode/velox/external/tzdb/tzdb_private.h +@@ -8,21 +8,13 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TZ_PRIVATE_H +-#define _LIBCPP_SRC_INCLUDE_TZDB_TZ_PRIVATE_H ++#pragma once + +-#include ++#include "velox/external/tzdb/types_private.h" ++#include "velox/external/tzdb/tzdb.h" + +-#include "types_private.h" ++namespace facebook::velox::tzdb { + +-_LIBCPP_BEGIN_NAMESPACE_STD ++void __init_tzdb(tzdb& __tzdb, __rules_storage_type& __rules); + +-namespace chrono { +- +-void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules); +- +-} // namespace chrono +- +-_LIBCPP_END_NAMESPACE_STD +- +-#endif // _LIBCPP_SRC_INCLUDE_TZDB_TZ_PRIVATE_H ++} // namespace facebook::velox::tzdb +diff --git a/fbcode/velox/external/tzdb/zoned_time.h b/fbcode/velox/external/tzdb/zoned_time.h +--- a/fbcode/velox/external/tzdb/zoned_time.h ++++ b/fbcode/velox/external/tzdb/zoned_time.h +@@ -9,154 +9,125 @@ + + // For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +-#ifndef _LIBCPP___CHRONO_ZONED_TIME_H +-#define _LIBCPP___CHRONO_ZONED_TIME_H +- +-#include +-// Enable the contents of the header only when libc++ was built with experimental features enabled. +-#if _LIBCPP_HAS_EXPERIMENTAL_TZDB +- +-# include <__chrono/calendar.h> +-# include <__chrono/duration.h> +-# include <__chrono/sys_info.h> +-# include <__chrono/system_clock.h> +-# include <__chrono/time_zone.h> +-# include <__chrono/tzdb_list.h> +-# include <__concepts/constructible.h> +-# include <__config> +-# include <__type_traits/common_type.h> +-# include <__type_traits/conditional.h> +-# include <__type_traits/remove_cvref.h> +-# include <__utility/declval.h> +-# include <__utility/move.h> +-# include +- +-# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +-# pragma GCC system_header +-# endif +- +-_LIBCPP_PUSH_MACROS +-# include <__undef_macros> +- +-_LIBCPP_BEGIN_NAMESPACE_STD +- +-# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION +- +-namespace chrono { ++#pragma once ++#include ++#include ++#include ++#include ++#include ++#include "velox/external/date/date.h" ++#include "velox/external/tzdb/sys_info.h" ++#include "velox/external/tzdb/time_zone.h" ++#include "velox/external/tzdb/tzdb_list.h" ++ ++namespace facebook::velox::tzdb { + + template + struct zoned_traits {}; + + template <> + struct zoned_traits { +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static const time_zone* default_zone() { return chrono::locate_zone("UTC"); } +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static const time_zone* locate_zone(string_view __name) { +- return chrono::locate_zone(__name); ++ [[nodiscard]] static const time_zone* default_zone() { ++ return locate_zone("UTC"); ++ } ++ [[nodiscard]] static const time_zone* locate_zone(std::string_view __name) { ++ return facebook::velox::tzdb::locate_zone(__name); + } + }; + + template + class zoned_time { + // [time.zone.zonedtime.ctor]/2 +- static_assert(__is_duration_v<_Duration>, +- "the program is ill-formed since _Duration is not a specialization of std::chrono::duration"); ++ // static_assert(__is_duration_v<_Duration>, ++ // "the program is ill-formed since _Duration is not a ++ // specialization of std::chrono::duration"); + + // The wording uses the constraints like +- // constructible_from ++ // constructible_from + // Using these constraints in the code causes the compiler to give an + // error that the constraint depends on itself. To avoid that issue use + // the fact it is possible to create this object from a _TimeZonePtr. +- using __traits _LIBCPP_NODEBUG = zoned_traits<_TimeZonePtr>; ++ using __traits = zoned_traits<_TimeZonePtr>; + +-public: +- using duration = common_type_t<_Duration, seconds>; ++ public: ++ using duration = std::common_type_t<_Duration, std::chrono::seconds>; + +- _LIBCPP_HIDE_FROM_ABI zoned_time() +- requires requires { __traits::default_zone(); } +- : __zone_{__traits::default_zone()}, __tp_{} {} ++ zoned_time() : __zone_{__traits::default_zone()}, __tp_{} {} + +- _LIBCPP_HIDE_FROM_ABI zoned_time(const zoned_time&) = default; +- _LIBCPP_HIDE_FROM_ABI zoned_time& operator=(const zoned_time&) = default; ++ zoned_time(const zoned_time&) = default; ++ zoned_time& operator=(const zoned_time&) = default; + +- _LIBCPP_HIDE_FROM_ABI zoned_time(const sys_time<_Duration>& __tp) +- requires requires { __traits::default_zone(); } ++ zoned_time(const date::sys_time<_Duration>& __tp) + : __zone_{__traits::default_zone()}, __tp_{__tp} {} + +- _LIBCPP_HIDE_FROM_ABI explicit zoned_time(_TimeZonePtr __zone) : __zone_{std::move(__zone)}, __tp_{} {} ++ explicit zoned_time(_TimeZonePtr __zone) ++ : __zone_{std::move(__zone)}, __tp_{} {} + +- _LIBCPP_HIDE_FROM_ABI explicit zoned_time(string_view __name) +- requires(requires { __traits::locate_zone(string_view{}); } && +- constructible_from<_TimeZonePtr, decltype(__traits::locate_zone(string_view{}))>) ++ explicit zoned_time(std::string_view __name) + : __zone_{__traits::locate_zone(__name)}, __tp_{} {} + + template +- _LIBCPP_HIDE_FROM_ABI zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& __zt) +- requires is_convertible_v, sys_time<_Duration>> ++ zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& __zt) + : __zone_{__zt.get_time_zone()}, __tp_{__zt.get_sys_time()} {} + +- _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const sys_time<_Duration>& __tp) ++ zoned_time(_TimeZonePtr __zone, const date::sys_time<_Duration>& __tp) + : __zone_{std::move(__zone)}, __tp_{__tp} {} + +- _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const sys_time<_Duration>& __tp) +- requires requires { _TimeZonePtr{__traits::locate_zone(string_view{})}; } ++ zoned_time(std::string_view __name, const date::sys_time<_Duration>& __tp) + : zoned_time{__traits::locate_zone(__name), __tp} {} + +- _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const local_time<_Duration>& __tp) +- requires(is_convertible_v() -> to_sys(local_time<_Duration>{})), +- sys_time>) ++ zoned_time(_TimeZonePtr __zone, const date::local_time<_Duration>& __tp) + : __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp)} {} + +- _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const local_time<_Duration>& __tp) +- requires(requires { +- _TimeZonePtr{__traits::locate_zone(string_view{})}; +- } && is_convertible_v() -> to_sys(local_time<_Duration>{})), +- sys_time>) ++ zoned_time(std::string_view __name, const date::local_time<_Duration>& __tp) + : zoned_time{__traits::locate_zone(__name), __tp} {} + +- _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const local_time<_Duration>& __tp, choose __c) +- requires(is_convertible_v< +- decltype(std::declval<_TimeZonePtr&>() -> to_sys(local_time<_Duration>{}, choose::earliest)), +- sys_time>) ++ zoned_time( ++ _TimeZonePtr __zone, ++ const date::local_time<_Duration>& __tp, ++ choose __c) + : __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp, __c)} {} + +- _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const local_time<_Duration>& __tp, choose __c) +- requires(requires { +- _TimeZonePtr{__traits::locate_zone(string_view{})}; +- } && is_convertible_v() -> to_sys(local_time<_Duration>{}, choose::earliest)), +- sys_time>) ++ zoned_time( ++ std::string_view __name, ++ const date::local_time<_Duration>& __tp, ++ choose __c) + : zoned_time{__traits::locate_zone(__name), __tp, __c} {} + + template +- _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const zoned_time<_Duration2, _TimeZonePtr2>& __zt) +- requires is_convertible_v, sys_time<_Duration>> ++ zoned_time( ++ _TimeZonePtr __zone, ++ const zoned_time<_Duration2, _TimeZonePtr2>& __zt) + : __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {} + + // per wording choose has no effect + template +- _LIBCPP_HIDE_FROM_ABI zoned_time(_TimeZonePtr __zone, const zoned_time<_Duration2, _TimeZonePtr2>& __zt, choose) +- requires is_convertible_v, sys_time<_Duration>> ++ zoned_time( ++ _TimeZonePtr __zone, ++ const zoned_time<_Duration2, _TimeZonePtr2>& __zt, ++ choose) + : __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {} + + template +- _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const zoned_time<_Duration2, _TimeZonePtr2>& __zt) +- requires(requires { +- _TimeZonePtr{__traits::locate_zone(string_view{})}; +- } && is_convertible_v, sys_time<_Duration>>) ++ zoned_time( ++ std::string_view __name, ++ const zoned_time<_Duration2, _TimeZonePtr2>& __zt) + : zoned_time{__traits::locate_zone(__name), __zt} {} + + template +- _LIBCPP_HIDE_FROM_ABI zoned_time(string_view __name, const zoned_time<_Duration2, _TimeZonePtr2>& __zt, choose __c) +- requires(requires { +- _TimeZonePtr{__traits::locate_zone(string_view{})}; +- } && is_convertible_v, sys_time<_Duration>>) ++ zoned_time( ++ std::string_view __name, ++ const zoned_time<_Duration2, _TimeZonePtr2>& __zt, ++ choose __c) + : zoned_time{__traits::locate_zone(__name), __zt, __c} {} + +- _LIBCPP_HIDE_FROM_ABI zoned_time& operator=(const sys_time<_Duration>& __tp) { ++ zoned_time& operator=(const date::sys_time<_Duration>& __tp) { + __tp_ = __tp; + return *this; + } + +- _LIBCPP_HIDE_FROM_ABI zoned_time& operator=(const local_time<_Duration>& __tp) { ++ zoned_time& operator=(const date::local_time<_Duration>& __tp) { + // TODO TZDB This seems wrong. + // Assigning a non-existent or ambiguous time will throw and not satisfy + // the post condition. This seems quite odd; I constructed an object with +@@ -167,62 +138,81 @@ + return *this; + } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI operator sys_time() const { return get_sys_time(); } +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI explicit operator local_time() const { return get_local_time(); } ++ [[nodiscard]] operator date::sys_time() const { ++ return get_sys_time(); ++ } ++ [[nodiscard]] explicit operator date::local_time() const { ++ return get_local_time(); ++ } + +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _TimeZonePtr get_time_zone() const { return __zone_; } +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI local_time get_local_time() const { return __zone_->to_local(__tp_); } +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time get_sys_time() const { return __tp_; } +- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_info get_info() const { return __zone_->get_info(__tp_); } ++ [[nodiscard]] _TimeZonePtr get_time_zone() const { ++ return __zone_; ++ } ++ [[nodiscard]] date::local_time get_local_time() const { ++ return __zone_->to_local(__tp_); ++ } ++ [[nodiscard]] date::sys_time get_sys_time() const { ++ return __tp_; ++ } ++ [[nodiscard]] sys_info get_info() const { ++ return __zone_->get_info(__tp_); ++ } + +-private: ++ private: + _TimeZonePtr __zone_; +- sys_time __tp_; ++ date::sys_time __tp_; + }; + +-zoned_time() -> zoned_time; ++zoned_time() -> zoned_time; + + template +-zoned_time(sys_time<_Duration>) -> zoned_time>; ++zoned_time(date::sys_time<_Duration>) ++ -> zoned_time>; + + template +-using __time_zone_representation _LIBCPP_NODEBUG = +- conditional_t, +- const time_zone*, +- remove_cvref_t<_TimeZonePtrOrName>>; ++using __time_zone_representation = std::conditional_t< ++ std::is_convertible_v<_TimeZonePtrOrName, std::string_view>, ++ const time_zone*, ++ folly::remove_cvref_t<_TimeZonePtrOrName>>; + + template +-zoned_time(_TimeZonePtrOrName&&) -> zoned_time>; ++zoned_time(_TimeZonePtrOrName&&) ++ -> zoned_time< ++ std::chrono::seconds, ++ __time_zone_representation<_TimeZonePtrOrName>>; + + template +-zoned_time(_TimeZonePtrOrName&&, sys_time<_Duration>) +- -> zoned_time, __time_zone_representation<_TimeZonePtrOrName>>; ++zoned_time(_TimeZonePtrOrName&&, date::sys_time<_Duration>) ++ -> zoned_time< ++ std::common_type_t<_Duration, std::chrono::seconds>, ++ __time_zone_representation<_TimeZonePtrOrName>>; + + template +-zoned_time(_TimeZonePtrOrName&&, local_time<_Duration>, choose = choose::earliest) +- -> zoned_time, __time_zone_representation<_TimeZonePtrOrName>>; ++zoned_time( ++ _TimeZonePtrOrName&&, ++ date::local_time<_Duration>, ++ choose = choose::earliest) ++ -> zoned_time< ++ std::common_type_t<_Duration, std::chrono::seconds>, ++ __time_zone_representation<_TimeZonePtrOrName>>; + + template +-zoned_time(_TimeZonePtrOrName&&, zoned_time<_Duration, _TimeZonePtr2>, choose = choose::earliest) +- -> zoned_time, __time_zone_representation<_TimeZonePtrOrName>>; ++zoned_time( ++ _TimeZonePtrOrName&&, ++ zoned_time<_Duration, _TimeZonePtr2>, ++ choose = choose::earliest) ++ -> zoned_time< ++ std::common_type_t<_Duration, std::chrono::seconds>, ++ __time_zone_representation<_TimeZonePtrOrName>>; + +-using zoned_seconds = zoned_time; ++using zoned_seconds = zoned_time; + + template +-_LIBCPP_HIDE_FROM_ABI bool +-operator==(const zoned_time<_Duration1, _TimeZonePtr>& __lhs, const zoned_time<_Duration2, _TimeZonePtr>& __rhs) { +- return __lhs.get_time_zone() == __rhs.get_time_zone() && __lhs.get_sys_time() == __rhs.get_sys_time(); ++bool operator==( ++ const zoned_time<_Duration1, _TimeZonePtr>& __lhs, ++ const zoned_time<_Duration2, _TimeZonePtr>& __rhs) { ++ return __lhs.get_time_zone() == __rhs.get_time_zone() && ++ __lhs.get_sys_time() == __rhs.get_sys_time(); + } + +-} // namespace chrono +- +-# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && +- // _LIBCPP_HAS_LOCALIZATION +- +-_LIBCPP_END_NAMESPACE_STD +- +-_LIBCPP_POP_MACROS +- +-#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB +- +-#endif // _LIBCPP___CHRONO_ZONED_TIME_H ++} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/patches/0002-zoned-time-to-stream-pr-12469.patch b/velox/external/tzdb/patches/0002-zoned-time-to-stream-pr-12469.patch new file mode 100644 index 000000000000..69b4700f5766 --- /dev/null +++ b/velox/external/tzdb/patches/0002-zoned-time-to-stream-pr-12469.patch @@ -0,0 +1,25 @@ +diff --git a/velox/external/tzdb/zoned_time.h b/velox/external/tzdb/zoned_time.h +--- a/velox/external/tzdb/zoned_time.h ++++ b/velox/external/tzdb/zoned_time.h +@@ -215,4 +215,21 @@ + __lhs.get_sys_time() == __rhs.get_sys_time(); + } + ++template ++std::basic_ostream& to_stream( ++ std::basic_ostream& os, ++ const CharT* fmt, ++ const zoned_time& tp) { ++ using duration = typename zoned_time::duration; ++ using LT = date::local_time; ++ auto const st = tp.get_sys_time(); ++ auto const info = tp.get_time_zone()->get_info(st); ++ return to_stream( ++ os, ++ fmt, ++ LT{(st + info.offset).time_since_epoch()}, ++ &info.abbrev, ++ &info.offset); ++} ++ + } // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/patches/0003-add-invalid-time-zone-error-pr-12475.patch b/velox/external/tzdb/patches/0003-add-invalid-time-zone-error-pr-12475.patch new file mode 100644 index 000000000000..1bcb84d14a03 --- /dev/null +++ b/velox/external/tzdb/patches/0003-add-invalid-time-zone-error-pr-12475.patch @@ -0,0 +1,46 @@ +diff --git a/velox/external/tzdb/chrono_exception.cpp b/velox/external/tzdb/chrono_exception.cpp +--- a/velox/external/tzdb/chrono_exception.cpp ++++ b/velox/external/tzdb/chrono_exception.cpp +@@ -14,4 +14,11 @@ + + ambiguous_local_time::~ambiguous_local_time() = default; // key function + ++invalid_time_zone::~invalid_time_zone() = default; // key function ++ ++[[noreturn]] void __throw_invalid_time_zone( ++ [[maybe_unused]] const std::string_view& __tz_name) { ++ throw invalid_time_zone(__tz_name); ++} ++ + } // namespace facebook::velox::tzdb +diff --git a/velox/external/tzdb/exception.h b/velox/external/tzdb/exception.h +--- a/velox/external/tzdb/exception.h ++++ b/velox/external/tzdb/exception.h +@@ -113,4 +113,27 @@ + throw ambiguous_local_time(__time, __info); + } + ++class invalid_time_zone : public std::runtime_error { ++ public: ++ invalid_time_zone( ++ const std::string_view& __tz_name) ++ : runtime_error{__create_message(__tz_name)} {} ++ ++ invalid_time_zone(const invalid_time_zone&) = default; ++ invalid_time_zone& operator=(const invalid_time_zone&) = default; ++ ++ ~invalid_time_zone() override; // exported as key function ++ ++ private: ++ std::string __create_message( ++ const std::string_view& __tz_name) { ++ std::ostringstream os; ++ os << __tz_name << " not found in timezone database"; ++ return os.str(); ++ } ++}; ++ ++[[noreturn]] void __throw_invalid_time_zone( ++ [[maybe_unused]] const std::string_view& __tz_name); ++ + } // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/patches/README b/velox/external/tzdb/patches/README new file mode 100644 index 000000000000..666fd06dbfdb --- /dev/null +++ b/velox/external/tzdb/patches/README @@ -0,0 +1,2 @@ +Contains patches applied to the vendored library, for convenience when +upgrading it. diff --git a/velox/external/tzdb/sys_info.h b/velox/external/tzdb/sys_info.h new file mode 100644 index 000000000000..8da91a291c62 --- /dev/null +++ b/velox/external/tzdb/sys_info.h @@ -0,0 +1,28 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include +#include +#include "velox/external/date/date.h" + +namespace facebook::velox::tzdb { + +struct sys_info { + date::sys_seconds begin; + date::sys_seconds end; + std::chrono::seconds offset; + std::chrono::minutes save; + std::string abbrev; +}; + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/time_zone.cpp b/velox/external/tzdb/time_zone.cpp new file mode 100644 index 000000000000..18a3533e3058 --- /dev/null +++ b/velox/external/tzdb/time_zone.cpp @@ -0,0 +1,1177 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +// TODO TZDB look at optimizations +// +// The current algorithm is correct but not efficient. For example, in a named +// rule based continuation finding the next rule does quite a bit of work, +// returns the next rule and "forgets" its state. This could be better. +// +// It would be possible to cache lookups. If a time for a zone is calculated its +// sys_info could be kept and the next lookup could test whether the time is in +// a "known" sys_info. The wording in the Standard hints at this slowness by +// "suggesting" this could be implemented on the user's side. + +// TODO TZDB look at removing quirks +// +// The code has some special rules to adjust the timing at the continuation +// switches. This works correctly, but some of the places feel odd. It would be +// good to investigate this further and see whether all quirks are needed or +// that there are better fixes. +// +// These quirks often use a 12h interval; this is the scan interval of zdump, +// which implies there are no sys_info objects with a duration of less than 12h. + +#include +#include +#include +#include +#include +#include + +#include "velox/external/date/date.h" +#include "velox/external/tzdb/time_zone_private.h" +#include "velox/external/tzdb/types_private.h" + +namespace facebook::velox::tzdb { + +using namespace std::chrono_literals; + +//===----------------------------------------------------------------------===// +// Details +//===----------------------------------------------------------------------===// + +struct __sys_info { + sys_info __info; + bool __can_merge; // Can the returned sys_info object be merged with +}; + +// Return type for helper function to get a sys_info. +// - The expected result returns the "best" sys_info object. This object can be +// before the requested time. Sometimes sys_info objects from different +// continuations share their offset, save, and abbrev and these objects are +// merged to one sys_info object. The __can_merge flag determines whether the +// current result can be merged with the next result. +// - The unexpected result means no sys_info object was found and the time is +// the time to be used for the next search iteration. +using __sys_info_result = folly::Expected<__sys_info, date::sys_seconds>; + +[[nodiscard]] static auto __binary_find( + const __rules_storage_type& __rules_db, + const std::string& __rule_name) { + auto __end = __rules_db.end(); + auto __ret = std::lower_bound( + __rules_db.begin(), + __rules_db.end(), + __rule_name, + [](const auto& rule, const auto& name) { return rule.first < name; }); + if (__ret == __end) + return __end; + + // When the value does not match the predicate it's equal and a valid result + // was found. + return __rule_name >= __ret->first ? __ret : __end; +} + +// Format based on https://data.iana.org/time-zones/tz-how-to.html +// +// 1 a time zone abbreviation that is a string of three or more characters that +// are either ASCII alphanumerics, "+", or "-" +// 2 the string "%z", in which case the "%z" will be replaced by a numeric time +// zone abbreviation +// 3 a pair of time zone abbreviations separated by a slash ('/'), in which +// case the first string is the abbreviation for the standard time name and +// the second string is the abbreviation for the daylight saving time name +// 4 a string containing "%s", in which case the "%s" will be replaced by the +// text in the appropriate Rule's LETTER column, and the resulting string +// should be a time zone abbreviation +// +// Rule 1 is not strictly validated since America/Barbados uses a two letter +// abbreviation AT. +[[nodiscard]] static std::string __format( + const facebook::velox::tzdb::__continuation& __continuation, + const std::string& __letters, + std::chrono::seconds __save) { + bool __shift = false; + std::string __result; + for (char __c : __continuation.__format) { + if (__shift) { + switch (__c) { + case 's': + std::copy( + __letters.begin(), __letters.end(), std::back_inserter(__result)); + break; + + case 'z': { + if (__continuation.__format.size() != 2) + std::__throw_runtime_error( + fmt::format( + "corrupt tzdb FORMAT field: %z should be the entire contents, instead contains '{}'", + __continuation.__format) + .c_str()); + date::hh_mm_ss __offset{__continuation.__stdoff + __save}; + if (__offset.is_negative()) { + __result += '-'; + __offset = date::hh_mm_ss{-(__continuation.__stdoff + __save)}; + } else + __result += '+'; + + if (__offset.minutes() != 0min) + fmt::format_to( + std::back_inserter(__result), + "{:%H}{:%M}", + __offset.hours(), + __offset.minutes()); + else + fmt::format_to( + std::back_inserter(__result), "{:%H}", __offset.hours()); + } break; + + default: + std::__throw_runtime_error( + fmt::format( + "corrupt tzdb FORMAT field: invalid sequence '%{}' found, expected %s or %z", + __c) + .c_str()); + } + __shift = false; + + } else if (__c == '/') { + if (__save != 0s) + __result.clear(); + else + break; + + } else if (__c == '%') { + __shift = true; + } else if (__c == '+' || __c == '-' || std::isalnum(__c)) { + __result.push_back(__c); + } else { + std::__throw_runtime_error( + fmt::format( + "corrupt tzdb FORMAT field: invalid character '{}' found, expected +, -, or an alphanumeric value", + __c) + .c_str()); + } + } + + if (__shift) + std::__throw_runtime_error( + "corrupt tzdb FORMAT field: input ended with the start of the escape sequence '%'"); + + if (__result.empty()) + std::__throw_runtime_error("corrupt tzdb FORMAT field: result is empty"); + + return __result; +} + +[[nodiscard]] static date::sys_seconds __to_sys_seconds( + date::year_month_day __ymd, + std::chrono::seconds __seconds) { + std::chrono::seconds __result = + static_cast(__ymd).time_since_epoch() + __seconds; + return date::sys_seconds{__result}; +} + +template +struct False : std::bool_constant {}; + +[[nodiscard]] static std::chrono::seconds __at_to_sys_seconds( + const facebook::velox::tzdb::__continuation& __continuation) { + switch (__continuation.__at.__clock) { + case facebook::velox::tzdb::__clock::__local: + return __continuation.__at.__time - __continuation.__stdoff - + std::visit( + [](const auto& __value) { + using _Tp = std::decay_t; + if constexpr (std::is_same_v<_Tp, std::monostate>) + return std::chrono::seconds{0}; + else if constexpr (std::is_same_v< + _Tp, + facebook::velox::tzdb::__save>) + return std::chrono::duration_cast( + __value.__time); + else if constexpr (std::is_same_v<_Tp, std::string>) + // For a named rule based continuation the SAVE depends on + // the RULE active at the end. This should be determined + // separately. + return std::chrono::seconds{0}; + else + static_assert(False<_Tp>::value); + + throw std::runtime_error("unreachable"); + }, + __continuation.__rules); + + case facebook::velox::tzdb::__clock::__universal: + return __continuation.__at.__time; + + case facebook::velox::tzdb::__clock::__standard: + return __continuation.__at.__time - __continuation.__stdoff; + } + throw std::runtime_error("unreachable"); +} + +[[nodiscard]] static date::year_month_day __to_year_month_day( + date::year __year, + date::month __month, + facebook::velox::tzdb::__on __on) { + return std::visit( + [&](const auto& __value) { + using _Tp = std::decay_t; + if constexpr (std::is_same_v<_Tp, date::day>) + return date::year_month_day{__year, __month, __value}; + else if constexpr (std::is_same_v<_Tp, date::weekday_last>) + return date::year_month_day{static_cast( + date::year_month_weekday_last{__year, __month, __value})}; + else if constexpr (std::is_same_v<_Tp, __constrained_weekday>) + return __value(__year, __month); + else + static_assert(False<_Tp>::value); + + throw std::runtime_error("unreachable"); + }, + __on); +} + +[[nodiscard]] static date::sys_seconds __until_to_sys_seconds( + const facebook::velox::tzdb::__continuation& __continuation) { + // Does UNTIL contain the magic value for the last continuation? + if (__continuation.__year == date::year::min()) + return date::sys_seconds::max(); + + date::year_month_day __ymd = __to_year_month_day( + __continuation.__year, __continuation.__in, __continuation.__on); + return __to_sys_seconds(__ymd, __at_to_sys_seconds(__continuation)); +} + +// Holds the UNTIL time for a continuation with a named rule. +// +// Unlike continuations with an fixed SAVE named rules have a variable SAVE. +// This means when the UNTIL uses the local wall time the actual UNTIL value can +// only be determined when the SAVE is known. This class holds that abstraction. +class __named_rule_until { + public: + explicit __named_rule_until(const facebook::velox::tzdb::__continuation& __continuation) + : __until_{__until_to_sys_seconds(__continuation)}, + __needs_adjustment_{ + // The last continuation of a ZONE has no UNTIL which basically is + // until the end of _local_ time. This value is expressed by + // sys_seconds::max(). Subtracting the SAVE leaves large value. + // However SAVE can be negative, which would add a value to maximum + // leading to undefined behaviour. In practice this often results in + // an overflow to a very small value. + __until_ != date::sys_seconds::max() && + __continuation.__at.__clock == + facebook::velox::tzdb::__clock::__local} {} + + // Gives the unadjusted until value, this is useful when the SAVE is not known + // at all. + date::sys_seconds __until() const noexcept { + return __until_; + } + + bool __needs_adjustment() const noexcept { + return __needs_adjustment_; + } + + // Returns the UNTIL adjusted for SAVE. + date::sys_seconds operator()(std::chrono::seconds __save) const noexcept { + return __until_ - __needs_adjustment_ * __save; + } + + private: + date::sys_seconds __until_; + bool __needs_adjustment_; +}; + +[[nodiscard]] static std::chrono::seconds __at_to_seconds( + std::chrono::seconds __stdoff, + const __rule& __rule) { + switch (__rule.__at.__clock) { + case facebook::velox::tzdb::__clock::__local: + // Local time and standard time behave the same. This is not + // correct. Local time needs to adjust for the current saved time. + // To know the saved time the rules need to be known and sorted. + // This needs a time so to avoid the chicken and egg adjust the + // saving of the local time later. + return __rule.__at.__time - __stdoff; + + case facebook::velox::tzdb::__clock::__universal: + return __rule.__at.__time; + + case facebook::velox::tzdb::__clock::__standard: + return __rule.__at.__time - __stdoff; + } + throw std::runtime_error("unreachable"); +} + +[[nodiscard]] static date::sys_seconds __from_to_sys_seconds( + std::chrono::seconds __stdoff, + const __rule& __rule, + date::year __year) { + date::year_month_day __ymd = + __to_year_month_day(__year, __rule.__in, __rule.__on); + + std::chrono::seconds __at = __at_to_seconds(__stdoff, __rule); + return __to_sys_seconds(__ymd, __at); +} + +[[nodiscard]] static date::sys_seconds __from_to_sys_seconds( + std::chrono::seconds __stdoff, + const __rule& __rule) { + return __from_to_sys_seconds(__stdoff, __rule, __rule.__from); +} + +[[nodiscard]] static const std::vector<__rule>& __get_rules( + const __rules_storage_type& __rules_db, + const std::string& __rule_name) { + auto __result = __binary_find(__rules_db, __rule_name); + if (__result == std::end(__rules_db)) + std::__throw_runtime_error( + ("corrupt tzdb: rule '" + __rule_name + " 'does not exist").c_str()); + + return __result->second; +} + +// Returns the letters field for a time before the first rule. +// +// Per https://data.iana.org/time-zones/tz-how-to.html +// One wrinkle, not fully explained in zic.8.txt, is what happens when switching +// to a named rule. To what values should the SAVE and LETTER data be +// initialized? +// +// 1 If at least one transition has happened, use the SAVE and LETTER data from +// the most recent. +// 2 If switching to a named rule before any transition has happened, assume +// standard time (SAVE zero), and use the LETTER data from the earliest +// transition with a SAVE of zero. +// +// This function implements case 2. +[[nodiscard]] static std::string __letters_before_first_rule( + const std::vector<__rule>& __rules) { + for (const auto& __rule : __rules) { + if (__rule.__save.__time != 0s) { + continue; + } + + return __rule.__letters; + } + + std::__throw_runtime_error("corrupt tzdb: rule has zero entries"); +} + +// Determines the information based on the continuation and the rules. +// +// There are several special cases to take into account +// +// === Entries before the first rule becomes active === +// Asia/Hong_Kong +// 9 - JST 1945 N 18 2 // (1) +// 8 HK HK%sT // (2) +// R HK 1946 o - Ap 21 0 1 S // (3) +// There (1) is active until Novemer 18th 1945 at 02:00, after this time +// (2) becomes active. The first rule entry for HK (3) becomes active +// from April 21st 1945 at 01:00. In the period between (2) is active. +// This entry has an offset. +// This entry has no save, letters, or dst flag. So in the period +// after (1) and until (3) no rule entry is associated with the time. + +[[nodiscard]] static sys_info __get_sys_info_before_first_rule( + date::sys_seconds __begin, + date::sys_seconds __end, + const facebook::velox::tzdb::__continuation& __continuation, + const std::vector<__rule>& __rules) { + return sys_info{ + __begin, + __end, + __continuation.__stdoff, + std::chrono::minutes(0), + __format(__continuation, __letters_before_first_rule(__rules), 0s)}; +} + +// Returns the sys_info object for a time before the first rule. +// When this first rule has a SAVE of 0s the sys_info for the time before the +// first rule and for the first rule are identical and will be merged. +[[nodiscard]] static sys_info __get_sys_info_before_first_rule( + date::sys_seconds __begin, + date::sys_seconds __rule_end, // The end used when SAVE != 0s + date::sys_seconds + __next_end, // The end used when SAVE == 0s the times are merged + const facebook::velox::tzdb::__continuation& __continuation, + const std::vector<__rule>& __rules, + std::vector<__rule>::const_iterator __rule) { + if (__rule->__save.__time != 0s) + return __get_sys_info_before_first_rule( + __begin, __rule_end, __continuation, __rules); + + return sys_info{ + __begin, + __next_end, + __continuation.__stdoff, + 0min, + __format(__continuation, __rule->__letters, 0s)}; +} + +[[nodiscard]] static std::chrono::seconds __at_to_seconds( + std::chrono::seconds __stdoff, + std::chrono::seconds __save, + const __rule& __rule) { + switch (__rule.__at.__clock) { + case facebook::velox::tzdb::__clock::__local: + return __rule.__at.__time - __stdoff - __save; + + case facebook::velox::tzdb::__clock::__universal: + return __rule.__at.__time; + + case facebook::velox::tzdb::__clock::__standard: + return __rule.__at.__time - __stdoff; + } + throw std::runtime_error("unreachable"); +} + +[[nodiscard]] static date::sys_seconds __rule_to_sys_seconds( + std::chrono::seconds __stdoff, + std::chrono::seconds __save, + const __rule& __rule, + date::year __year) { + date::year_month_day __ymd = + __to_year_month_day(__year, __rule.__in, __rule.__on); + + std::chrono::seconds __at = __at_to_seconds(__stdoff, __save, __rule); + return __to_sys_seconds(__ymd, __at); +} + +// Returns the first rule after __time. +// Note that a rule can be "active" in multiple years, this may result in an +// infinite loop where the same rule is returned every time, use __current to +// guard against that. +// +// When no next rule exists the returned time will be sys_seconds::max(). This +// can happen in practice. For example, +// +// R So 1945 o - May 24 2 2 M +// R So 1945 o - S 24 3 1 S +// R So 1945 o - N 18 2s 0 - +// +// Has 3 rules that are all only active in 1945. +[[nodiscard]] static std:: + pair::const_iterator> + __next_rule( + date::sys_seconds __time, + std::chrono::seconds __stdoff, + std::chrono::seconds __save, + const std::vector<__rule>& __rules, + std::vector<__rule>::const_iterator __current) { + date::year __year = + date::year_month_day{std::chrono::floor(__time)}.year(); + + // Note it would probably be better to store the pairs in a vector and then + // use min() to get the smallest element + std::map::const_iterator> __candidates; + // Note this evaluates all rules which is a waste of effort; when the entries + // are beyond the current year's "next year" (where "next year" is not always + // year + 1) the algorithm should end. + for (auto __it = __rules.begin(); __it != __rules.end(); ++__it) { + for (date::year __y = __it->__from; __y <= __it->__to; ++__y) { + // Adding the current entry for the current year may lead to infinite + // loops due to the SAVE adjustment. Skip these entries. + if (__y == __year && __it == __current) + continue; + + date::sys_seconds __t = + __rule_to_sys_seconds(__stdoff, __save, *__it, __y); + if (__t <= __time) + continue; + + if (__candidates.count(__t) > 0) { + throw std::runtime_error("duplicated rule"); + } + __candidates[__t] = __it; + break; + } + } + + if (!__candidates.empty()) [[likely]] { + auto __it = __candidates.begin(); + + // When no rule is selected the time before the first rule and the first + // rule should not be merged. + if (__time == date::sys_seconds::min()) + return *__it; + + // There can be two constitutive rules that are the same. For example, + // Hong Kong + // + // R HK 1973 o - D 30 3:30 1 S (R1) + // R HK 1965 1976 - Ap Su>=16 3:30 1 S (R2) + // + // 1973-12-29 19:30:00 R1 becomes active. + // 1974-04-20 18:30:00 R2 becomes active. + // Both rules have a SAVE of 1 hour and LETTERS are S for both of them. + while (__it != __candidates.end()) { + if (__current->__save.__time != __it->second->__save.__time || + __current->__letters != __it->second->__letters) + return *__it; + + ++__it; + } + } + + return {date::sys_seconds::max(), __rules.end()}; +} + +// Returns the first rule of a set of rules. +// This is not always the first of the listed rules. For example +// R Sa 2008 2009 - Mar Su>=8 0 0 - +// R Sa 2007 2008 - O Su>=8 0 1 - +// The transition in October 2007 happens before the transition in March 2008. +[[nodiscard]] static std::vector<__rule>::const_iterator __first_rule( + std::chrono::seconds __stdoff, + const std::vector<__rule>& __rules) { + return __next_rule( + date::sys_seconds::min(), __stdoff, 0s, __rules, __rules.end()) + .second; +} + +[[nodiscard]] static __sys_info_result __get_sys_info_rule( + date::sys_seconds __time, + date::sys_seconds __continuation_begin, + const facebook::velox::tzdb::__continuation& __continuation, + const std::vector<__rule>& __rules) { + auto __rule = __first_rule(__continuation.__stdoff, __rules); + if (__rule == __rules.end()) { + throw std::runtime_error("the set of rules has no first rule"); + } + + // Avoid selecting a time before the start of the continuation + __time = std::max(__time, __continuation_begin); + + date::sys_seconds __rule_begin = + __from_to_sys_seconds(__continuation.__stdoff, *__rule); + + // The time sought is very likely inside the current rule. + // When the continuation's UNTIL uses the local clock there are edge cases + // where this is not true. + // + // Start to walk the rules to find the proper one. + // + // For now we just walk all the rules TODO TZDB investigate whether a smarter + // algorithm would work. + auto __next = __next_rule( + __rule_begin, + __continuation.__stdoff, + __rule->__save.__time, + __rules, + __rule); + + // Ignore small steps, this happens with America/Punta_Arenas for the + // transition + // -4:42:46 - SMT 1927 S + // -5 x -05/-04 1932 S + // ... + // + // R x 1927 1931 - S 1 0 1 - + // R x 1928 1932 - Ap 1 0 0 - + // + // America/Punta_Arenas Thu Sep 1 04:42:45 1927 UT = Thu Sep 1 00:42:45 + // 1927 -04 isdst=1 gmtoff=-14400 America/Punta_Arenas Sun Apr 1 03:59:59 + // 1928 UT = Sat Mar 31 23:59:59 1928 -04 isdst=1 gmtoff=-14400 + // America/Punta_Arenas Sun Apr 1 04:00:00 1928 UT = Sat Mar 31 23:00:00 + // 1928 -05 isdst=0 gmtoff=-18000 + // + // Without this there will be a transition + // [1927-09-01 04:42:45, 1927-09-01 05:00:00) -05:00:00 0min -05 + + if (date::sys_seconds __begin = + __rule->__save.__time != 0s ? __rule_begin : __next.first; + __time < __begin) { + if (__continuation_begin == date::sys_seconds::min() || + __begin - __continuation_begin > 12h) + return __sys_info{ + __get_sys_info_before_first_rule( + __continuation_begin, + __rule_begin, + __next.first, + __continuation, + __rules, + __rule), + false}; + + // Europe/Berlin + // 1 c CE%sT 1945 May 24 2 (C1) + // 1 So CE%sT 1946 (C2) + // + // R c 1944 1945 - Ap M>=1 2s 1 S (R1) + // + // R So 1945 o - May 24 2 2 M (R2) + // + // When C2 becomes active the time would be before the first rule R2, + // giving a 1 hour sys_info. + std::chrono::seconds __save = __rule->__save.__time; + __named_rule_until __continuation_end{__continuation}; + date::sys_seconds __sys_info_end = + std::min(__continuation_end(__save), __next.first); + + return __sys_info{ + sys_info{ + __continuation_begin, + __sys_info_end, + __continuation.__stdoff + __save, + std::chrono::duration_cast(__save), + __format(__continuation, __rule->__letters, __save)}, + __sys_info_end == __continuation_end(__save)}; + } + + // See above for America/Asuncion + if (__rule->__save.__time == 0s && __time < __next.first) { + return __sys_info{ + sys_info{ + __continuation_begin, + __next.first, + __continuation.__stdoff, + 0min, + __format(__continuation, __rule->__letters, 0s)}, + false}; + } + + if (__rule->__save.__time != 0s) { + // another fix for America/Punta_Arenas when not at the start of the + // sys_info object. + std::chrono::seconds __save = __rule->__save.__time; + if (__continuation_begin >= __rule_begin - __save && + __time < __next.first) { + return __sys_info{ + sys_info{ + __continuation_begin, + __next.first, + __continuation.__stdoff + __save, + std::chrono::duration_cast(__save), + __format(__continuation, __rule->__letters, __save)}, + false}; + } + } + + __named_rule_until __continuation_end{__continuation}; + while (__next.second != __rules.end()) { + date::sys_seconds __end = __continuation_end(__rule->__save.__time); + + date::sys_seconds __sys_info_begin = + std::max(__continuation_begin, __rule_begin); + date::sys_seconds __sys_info_end = std::min(__end, __next.first); + std::chrono::seconds __diff = + std::chrono::abs(__sys_info_end - __sys_info_begin); + + if (__diff < 12h) { + // Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31 + // -4:16:48 - CMT 1920 May + // -4 - -04 1930 D + // -4 A -04/-03 1969 O 5 + // -3 A -03/-02 1999 O 3 + // -4 A -04/-03 2000 Mar 3 + // ... + // + // ... + // R A 1989 1992 - O Su>=15 0 1 - + // R A 1999 o - O Su>=1 0 1 - + // R A 2000 o - Mar 3 0 0 - + // R A 2007 o - D 30 0 1 - + // ... + + // The 1999 switch uses the same rule, but with a different stdoff. + // R A 1999 o - O Su>=1 0 1 - + // stdoff -3 -> 1999-10-03 03:00:00 + // stdoff -4 -> 1999-10-03 04:00:00 + // This generates an invalid entry and this is evaluated as a transition. + // Looking at the zdump like output in libc++ this generates jumps in + // the UTC time. + + __rule = __next.second; + __next = __next_rule( + __next.first, + __continuation.__stdoff, + __rule->__save.__time, + __rules, + __rule); + __end = __continuation_end(__rule->__save.__time); + __sys_info_end = std::min(__end, __next.first); + } + + if ((__time >= __rule_begin && __time < __next.first) || + __next.first >= __end) { + __sys_info_begin = std::max(__continuation_begin, __rule_begin); + __sys_info_end = std::min(__end, __next.first); + + return __sys_info{ + sys_info{ + __sys_info_begin, + __sys_info_end, + __continuation.__stdoff + __rule->__save.__time, + std::chrono::duration_cast( + __rule->__save.__time), + __format( + __continuation, __rule->__letters, __rule->__save.__time)}, + __sys_info_end == __end}; + } + + __rule_begin = __next.first; + __rule = __next.second; + __next = __next_rule( + __rule_begin, + __continuation.__stdoff, + __rule->__save.__time, + __rules, + __rule); + } + + return __sys_info{ + sys_info{ + std::max(__continuation_begin, __rule_begin), + __continuation_end(__rule->__save.__time), + __continuation.__stdoff + __rule->__save.__time, + std::chrono::duration_cast( + __rule->__save.__time), + __format(__continuation, __rule->__letters, __rule->__save.__time)}, + true}; +} + +[[nodiscard]] static __sys_info_result __get_sys_info_basic( + date::sys_seconds __time, + date::sys_seconds __continuation_begin, + const facebook::velox::tzdb::__continuation& __continuation, + std::chrono::seconds __save) { + date::sys_seconds __continuation_end = __until_to_sys_seconds(__continuation); + return __sys_info{ + sys_info{ + __continuation_begin, + __continuation_end, + __continuation.__stdoff + __save, + std::chrono::duration_cast(__save), + __format(__continuation, __continuation.__format, __save)}, + true}; +} + +[[nodiscard]] static __sys_info_result __get_sys_info( + date::sys_seconds __time, + date::sys_seconds __continuation_begin, + const facebook::velox::tzdb::__continuation& __continuation, + const __rules_storage_type& __rules_db) { + return std::visit( + [&](const auto& __value) { + using _Tp = std::decay_t; + if constexpr (std::is_same_v<_Tp, std::string>) + return __get_sys_info_rule( + __time, + __continuation_begin, + __continuation, + __get_rules(__rules_db, __value)); + else if constexpr (std::is_same_v<_Tp, std::monostate>) + return __get_sys_info_basic( + __time, + __continuation_begin, + __continuation, + std::chrono::seconds(0)); + else if constexpr (std::is_same_v<_Tp, facebook::velox::tzdb::__save>) + return __get_sys_info_basic( + __time, __continuation_begin, __continuation, __value.__time); + else + static_assert(False<_Tp>::value); + + throw std::runtime_error("unreachable"); + }, + __continuation.__rules); +} + +// The transition from one continuation to the next continuation may result in +// two constitutive continuations with the same "offset" information. +// [time.zone.info.sys]/3 +// The begin and end data members indicate that, for the associated time_zone +// and time_point, the offset and abbrev are in effect in the range +// [begin, end). This information can be used to efficiently iterate the +// transitions of a time_zone. +// +// Note that this does considers a change in the SAVE field not to be a +// different sys_info, zdump does consider this different. +// LWG XXXX The sys_info range should be affected by save +// matches the behaviour of the Standard and zdump. +// +// Iff the "offsets" are the same '__current.__end' is replaced with +// '__next.__end', which effectively merges the two objects in one object. The +// function returns true if a merge occurred. +[[nodiscard]] bool __merge_continuation( + sys_info& __current, + const sys_info& __next) { + if (__current.end != __next.begin) + return false; + + if (__current.offset != __next.offset || __current.abbrev != __next.abbrev || + __current.save != __next.save) + return false; + + __current.end = __next.end; + return true; +} + +//===----------------------------------------------------------------------===// +// Public API +//===----------------------------------------------------------------------===// + +[[nodiscard]] time_zone time_zone::__create( + std::unique_ptr&& __p) { + if (__p == nullptr) { + throw std::runtime_error( + "initialized time_zone without a valid pimpl object"); + } + time_zone result; + result.__impl_ = std::move(__p); + return result; +} + +time_zone::~time_zone() = default; + +[[nodiscard]] std::string_view time_zone::__name() const noexcept { + return __impl_->__name(); +} + +[[nodiscard]] sys_info time_zone::__get_info(date::sys_seconds __time) const { + std::optional __result; + bool __valid_result = + false; // true iff __result.has_value() is true and + // __result.begin <= __time < __result.end is true. + bool __can_merge = false; + date::sys_seconds __continuation_begin = date::sys_seconds::min(); + // Iterates over the Zone entry and its continuations. Internally the Zone + // entry is split in a Zone information and the first continuation. The last + // continuation has no UNTIL field. This means the loop should always find a + // continuation. + // + // For more information on background of zone information please consult the + // following information + // [zic manual](https://www.man7.org/linux/man-pages/man8/zic.8.html) + // [tz source info](https://data.iana.org/time-zones/tz-how-to.html) + // On POSIX systems the zdump tool can be useful: + // zdump -v Asia/Hong_Kong + // Gives all transitions in the Hong Kong time zone. + // + // During iteration the result for the current continuation is returned. If + // no continuation is applicable it will return the end time as "error". When + // two continuations are contiguous and contain the "same" information these + // ranges are merged as one range. + // The merging requires keeping any result that occurs before __time, + // likewise when a valid result is found the algorithm needs to test the next + // continuation to see whether it can be merged. For example, Africa/Ceuta + // Continuations + // 0 s WE%sT 1929 (C1) + // 0 - WET 1967 (C2) + // 0 Sp WE%sT 1984 Mar 16 (C3) + // + // Rules + // R s 1926 1929 - O Sa>=1 24s 0 - (R1) + // + // R Sp 1967 o - Jun 3 12 1 S (R2) + // + // The rule R1 is the last rule used in C1. The rule R2 is the first rule in + // C3. Since R2 is the first rule this means when a continuation uses this + // rule its value prior to R2 will be SAVE 0 LETTERS of the first entry with a + // SAVE of 0, in this case WET. + // This gives the following changes in the information. + // 1928-10-07 00:00:00 C1 R1 becomes active: offset 0 save 0 abbrev WET + // 1929-01-01 00:00:00 C2 becomes active: offset 0 save 0 abbrev WET + // 1967-01-01 00:00:00 C3 becomes active: offset 0 save 0 abbrev WET + // 1967-06-03 12:00:00 C3 R2 becomes active: offset 0 save 1 abbrev WEST + // + // The first 3 entries are contiguous and contain the same information, this + // means the period [1928-10-07 00:00:00, 1967-06-03 12:00:00) should be + // returned in one sys_info object. + + const auto& __continuations = __impl_->__continuations(); + const __rules_storage_type& __rules_db = __impl_->__rules_db(); + for (auto __it = __continuations.begin(); __it != __continuations.end(); + ++__it) { + const auto& __continuation = *__it; + __sys_info_result __sys_info = __get_sys_info( + __time, __continuation_begin, __continuation, __rules_db); + + if (__sys_info) { + if (__sys_info->__info.begin >= __sys_info->__info.end) { + throw std::runtime_error("invalid sys_info range"); + } + + // Filters out dummy entries + // Z America/Argentina/Buenos_Aires -3:53:48 - LMT 1894 O 31 + // ... + // -4 A -04/-03 2000 Mar 3 (C1) + // -3 A -03/-02 (C2) + // + // ... + // R A 2000 o - Mar 3 0 0 - + // R A 2007 o - D 30 0 1 - + // ... + // + // This results in an entry + // [2000-03-03 03:00:00, 2000-03-03 04:00:00) -10800s 60min -03 + // for [C1 & R1, C1, R2) which due to the end of the continuation is an + // one hour "sys_info". Instead the entry should be ignored and replaced + // by [C2 & R1, C2 & R2) which is the proper range + // "[2000-03-03 03:00:00, 2007-12-30 03:00:00) -02:00:00 60min -02 + + if (std::holds_alternative(__continuation.__rules) && + __sys_info->__can_merge && + __sys_info->__info.begin + 12h > __sys_info->__info.end) { + __continuation_begin = __sys_info->__info.begin; + continue; + } + + if (!__result) { + // First entry found, always keep it. + __result = __sys_info->__info; + + __valid_result = __time >= __result->begin && __time < __result->end; + __can_merge = __sys_info->__can_merge; + } else if ( + __can_merge && __merge_continuation(*__result, __sys_info->__info)) { + // The results are merged, update the result state. This may + // "overwrite" a valid sys_info object with another valid sys_info + // object. + __valid_result = __time >= __result->begin && __time < __result->end; + __can_merge = __sys_info->__can_merge; + } else { + // Here things get interesting: + // For example, America/Argentina/San_Luis + // + // -3 A -03/-02 2008 Ja 21 (C1) + // -4 Sa -04/-03 2009 O 11 (C2) + // + // R A 2007 o - D 30 0 1 - (R1) + // + // R Sa 2007 2008 - O Su>=8 0 1 - (R2) + // + // Based on C1 & R1 the end time of C1 is 2008-01-21 03:00:00 + // Based on C2 & R2 the end time of C1 is 2008-01-21 02:00:00 + // In this case the earlier time is the real time of the transition. + // However the algorithm used gives 2008-01-21 03:00:00. + // + // So we need to calculate the previous UNTIL in the current context and + // see whether it's earlier. + + // The results could not be merged. + // - When we have a valid result that result is the final result. + // - Otherwise the result we had is before __time and the result we got + // is at a later time (possibly valid). This result is always better + // than the previous result. + if (__valid_result) { + return *__result; + } else { + if (__it == __continuations.begin()) { + throw std::runtime_error( + "the first rule should always seed the result"); + } + const auto& __last = *(__it - 1); + if (std::holds_alternative(__last.__rules)) { + // Europe/Berlin + // 1 c CE%sT 1945 May 24 2 (C1) + // 1 So CE%sT 1946 (C2) + // + // R c 1944 1945 - Ap M>=1 2s 1 S (R1) + // + // R So 1945 o - May 24 2 2 M (R2) + // + // When C2 becomes active the time would be before the first rule + // R2, giving a 1 hour sys_info. This is not valid and the results + // need merging. + + if (__result->end != __sys_info->__info.begin) { + // When the UTC gap between the rules is due to the change of + // offsets adjust the new time to remove the gap. + date::sys_seconds __end = __result->end - __result->offset; + date::sys_seconds __begin = + __sys_info->__info.begin - __sys_info->__info.offset; + if (__end == __begin) { + __sys_info->__info.begin = __result->end; + } + } + } + + __result = __sys_info->__info; + __valid_result = __time >= __result->begin && __time < __result->end; + __can_merge = __sys_info->__can_merge; + } + } + __continuation_begin = __result->end; + } else { + __continuation_begin = __sys_info.error(); + } + } + if (__valid_result) + return *__result; + + std::__throw_runtime_error("tzdb: corrupt db"); +} + +// Is the "__local_time" present in "__first" and "__second". If so the +// local_info has an ambiguous result. +[[nodiscard]] static bool __is_ambiguous( + date::local_seconds __local_time, + const sys_info& __first, + const sys_info& __second) { + date::local_seconds __end_first{ + __first.end.time_since_epoch() + __first.offset}; + date::local_seconds __begin_second{ + __second.begin.time_since_epoch() + __second.offset}; + + return __local_time < __end_first && __local_time >= __begin_second; +} + +// Determines the result of the "__local_time". This expects the object +// "__first" to be earlier in time than "__second". +[[nodiscard]] static local_info __get_info( + date::local_seconds __local_time, + const sys_info& __first, + const sys_info& __second) { + date::local_seconds __end_first{ + __first.end.time_since_epoch() + __first.offset}; + date::local_seconds __begin_second{ + __second.begin.time_since_epoch() + __second.offset}; + + if (__local_time < __end_first) { + if (__local_time >= __begin_second) + // |--------| + // |------| + // ^ + return {local_info::ambiguous, __first, __second}; + + // |--------| + // |------| + // ^ + return {local_info::unique, __first, sys_info{}}; + } + + if (__local_time < __begin_second) + // |--------| + // |------| + // ^ + return {local_info::nonexistent, __first, __second}; + + // |--------| + // |------| + // ^ + return {local_info::unique, __second, sys_info{}}; +} + +[[nodiscard]] local_info time_zone::__get_info( + date::local_seconds __local_time) const { + std::chrono::seconds __local_seconds = __local_time.time_since_epoch(); + + /* An example of a typical year with a DST switch displayed in local time. + * + * At the first of April the time goes forward one hour. This means the + * time marked with ~~ is not a valid local time. This is represented by the + * nonexistent value in local_info.result. + * + * At the first of November the time goes backward one hour. This means the + * time marked with ^^ happens twice. This is represented by the ambiguous + * value in local_info.result. + * + * 2020.11.01 2021.04.01 2021.11.01 + * offset +05 offset +05 offset +05 + * save 0s save 1h save 0s + * |------------//----------| + * |---------//--------------| + * |------------- + * ~~ ^^ + * + * These shifts can happen due to changes in the current time zone for a + * location. For example, Indian/Kerguelen switched only once. In 1950 from an + * offset of 0 hours to an offset of +05 hours. + * + * During all these shifts the UTC time will not have gaps. + */ + + // The code needs to determine the system time for the local time. There is no + // information available. Assume the offset between system time and local time + // is 0s. This gives an initial estimate. + date::sys_seconds __guess{__local_seconds}; + sys_info __info = __get_info(__guess); + + // At this point the offset can be used to determine an estimate for the local + // time. Before doing that, determine the offset and validate whether the + // local time is the range [chrono::local_seconds::min(), + // chrono::local_seconds::max()). + if (__local_seconds < 0s && __info.offset > 0s) + if (__local_seconds - date::local_seconds::min().time_since_epoch() < + __info.offset) + return {-1, __info, {}}; + + if (__local_seconds > 0s && __info.offset < 0s) + if (date::local_seconds::max().time_since_epoch() - __local_seconds < + -__info.offset) + return {-2, __info, {}}; + + // Based on the information found in the sys_info, the local time can be + // converted to a system time. This resulting time can be in the following + // locations of the sys_info: + // + // |---------//--------------| + // 1 2.1 2.2 2.3 3 + // + // 1. The estimate is before the returned sys_info object. + // The result is either non-existent or unique in the previous sys_info. + // 2. The estimate is in the sys_info object + // - If the sys_info begin is not sys_seconds::min(), then it might be at + // 2.1 and could be ambiguous with the previous or unique. + // - If sys_info end is not sys_seconds::max(), then it might be at 2.3 + // and could be ambiguous with the next or unique. + // - Else it is at 2.2 and always unique. This case happens when a + // time zone has no transitions. For example, UTC or GMT+1. + // 3. The estimate is after the returned sys_info object. + // The result is either non-existent or unique in the next sys_info. + // + // There is no specification where the "middle" starts. Similar issues can + // happen when sys_info objects are "short", then "unique in the next" could + // become "ambiguous in the next and the one following". Theoretically there + // is the option of the following time-line + // + // |------------| + // |----| + // |-----------------| + // + // However the local_info object only has 2 sys_info objects, so this option + // is not tested. + + date::sys_seconds __sys_time{__local_seconds - __info.offset}; + if (__sys_time < __info.begin) + // Case 1 before __info + return facebook::velox::tzdb::__get_info( + __local_time, __get_info(__info.begin - 1s), __info); + + if (__sys_time >= __info.end) + // Case 3 after __info + return facebook::velox::tzdb::__get_info( + __local_time, __info, __get_info(__info.end)); + + // Case 2 in __info + if (__info.begin != date::sys_seconds::min()) { + // Case 2.1 Not at the beginning, when not ambiguous the result should test + // case 2.3. + sys_info __prev = __get_info(__info.begin - 1s); + if (__is_ambiguous(__local_time, __prev, __info)) + return {local_info::ambiguous, __prev, __info}; + } + + if (__info.end == date::sys_seconds::max()) + // At the end so it's case 2.2 + return {local_info::unique, __info, sys_info{}}; + + // This tests case 2.2 or case 2.3. + return facebook::velox::tzdb::__get_info( + __local_time, __info, __get_info(__info.end)); +} + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/time_zone.h b/velox/external/tzdb/time_zone.h new file mode 100644 index 000000000000..c4d0de48955f --- /dev/null +++ b/velox/external/tzdb/time_zone.h @@ -0,0 +1,214 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include +#include +#include +#include + +#include "velox/external/date/date.h" +#include "velox/external/tzdb/exception.h" +#include "velox/external/tzdb/local_info.h" +#include "velox/external/tzdb/sys_info.h" + +namespace facebook::velox::tzdb { + +enum class choose { earliest, latest }; + +class time_zone { + time_zone() = default; + + public: + class __impl; // public so it can be used by make_unique. + + // The "constructor". + // + // The default constructor is private to avoid the constructor from being + // part of the ABI. Instead use an __ugly_named function as an ABI interface, + // since that gives us the ability to change it in the future. + [[nodiscard]] static time_zone __create(std::unique_ptr<__impl>&& __p); + + ~time_zone(); + + time_zone(time_zone&&) = default; + time_zone& operator=(time_zone&&) = default; + + [[nodiscard]] std::string_view name() const noexcept { + return __name(); + } + + template + [[nodiscard]] sys_info get_info( + const date::sys_time<_Duration>& __time) const { + return __get_info( + std::chrono::time_point_cast(__time)); + } + + template + [[nodiscard]] local_info get_info( + const date::local_time<_Duration>& __time) const { + return __get_info( + std::chrono::time_point_cast(__time)); + } + + // We don't apply nodiscard here since this function throws on many inputs, + // so it could be used as a validation. + template + date::sys_time> to_sys( + const date::local_time<_Duration>& __time) const { + local_info __info = get_info(__time); + switch (__info.result) { + case local_info::unique: + return date::sys_time< + std::common_type_t<_Duration, std::chrono::seconds>>{ + __time.time_since_epoch() - __info.first.offset}; + + case local_info::nonexistent: + __throw_nonexistent_local_time(__time, __info); + + case local_info::ambiguous: + __throw_ambiguous_local_time(__time, __info); + } + + // TODO TZDB The Standard does not specify anything in these cases. + if (__info.result == -1) { + throw std::runtime_error( + "cannot convert the local time; it would be before the minimum system clock value"); + } + if (__info.result == -2) { + throw std::runtime_error( + "cannot convert the local time; it would be after the maximum system clock value"); + } + + return {}; + } + + template + [[nodiscard]] date::sys_time< + std::common_type_t<_Duration, std::chrono::seconds>> + to_sys(const date::local_time<_Duration>& __time, choose __z) const { + local_info __info = get_info(__time); + switch (__info.result) { + case local_info::unique: + case local_info::nonexistent: // first and second are the same + return date::sys_time< + std::common_type_t<_Duration, std::chrono::seconds>>{ + __time.time_since_epoch() - __info.first.offset}; + + case local_info::ambiguous: + switch (__z) { + case choose::earliest: + return date::sys_time< + std::common_type_t<_Duration, std::chrono::seconds>>{ + __time.time_since_epoch() - __info.first.offset}; + + case choose::latest: + return date::sys_time< + std::common_type_t<_Duration, std::chrono::seconds>>{ + __time.time_since_epoch() - __info.second.offset}; + + // Note a value out of bounds is not specified. + } + } + + // TODO TZDB The standard does not specify anything in these cases. + if (__info.result == -1) { + throw std::runtime_error( + "cannot convert the local time; it would be before the minimum system clock value"); + } + if (__info.result != -2) { + throw std::runtime_error( + "cannot convert the local time; it would be after the maximum system clock value"); + } + + return {}; + } + + template + [[nodiscard]] date::local_time< + std::common_type_t<_Duration, std::chrono::seconds>> + to_local(const date::sys_time<_Duration>& __time) const { + using _Dp = std::common_type_t<_Duration, std::chrono::seconds>; + + sys_info __info = get_info(__time); + + if (__info.offset < std::chrono::seconds{0} && + __time.time_since_epoch() < _Dp::min() - __info.offset) { + throw std::runtime_error( + "cannot convert the system time; it would be before the minimum local clock value"); + } + + if (__info.offset > std::chrono::seconds{0} && + __time.time_since_epoch() > _Dp::max() - __info.offset) { + throw std::runtime_error( + "cannot convert the system time; it would be after the maximum local clock value"); + } + + return date::local_time<_Dp>{__time.time_since_epoch() + __info.offset}; + } + + [[nodiscard]] const __impl& __implementation() const noexcept { + return *__impl_; + } + + private: + [[nodiscard]] std::string_view __name() const noexcept; + + [[nodiscard]] sys_info __get_info(date::sys_seconds __time) const; + [[nodiscard]] local_info __get_info(date::local_seconds __time) const; + + std::unique_ptr<__impl> __impl_; +}; + +[[nodiscard]] inline bool operator==( + const time_zone& __x, + const time_zone& __y) noexcept { + return __x.name() == __y.name(); +} + +[[nodiscard]] inline bool operator!=( + const time_zone& __x, + const time_zone& __y) noexcept { + return !(__x.name() == __y.name()); +} + +[[nodiscard]] inline bool operator<( + const time_zone& __x, + const time_zone& __y) noexcept { + return __x.name() < __y.name(); +} + +[[nodiscard]] inline bool operator>( + const time_zone& __x, + const time_zone& __y) noexcept { + return __x.name() > __y.name(); +} + +[[nodiscard]] inline bool operator<=( + const time_zone& __x, + const time_zone& __y) noexcept { + return !(__x.name() > __y.name()); +} + +[[nodiscard]] inline bool operator>=( + const time_zone& __x, + const time_zone& __y) noexcept { + return !(__x.name() < __y.name()); +} + +// [[nodiscard]] inline strong_ordering +// operator<=>(const time_zone& __x, const time_zone& __y) noexcept { +// return __x.name() <=> __y.name(); +// } + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/time_zone_link.h b/velox/external/tzdb/time_zone_link.h new file mode 100644 index 000000000000..973dfaf94a3f --- /dev/null +++ b/velox/external/tzdb/time_zone_link.h @@ -0,0 +1,84 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include +#include + +namespace facebook::velox::tzdb { + +class time_zone_link { + public: + [[nodiscard]] + explicit time_zone_link(std::string_view __name, std::string_view __target) + : __name_{__name}, __target_{__target} {} + + time_zone_link(time_zone_link&&) = default; + time_zone_link& operator=(time_zone_link&&) = default; + + [[nodiscard]] std::string_view name() const noexcept { + return __name_; + } + [[nodiscard]] std::string_view target() const noexcept { + return __target_; + } + + private: + std::string __name_; + // TODO TZDB instead of the name we can store the pointer to a zone. These + // pointers are immutable. This makes it possible to directly return a + // pointer in the time_zone in the 'locate_zone' function. + std::string __target_; +}; + +[[nodiscard]] inline bool operator==( + const time_zone_link& __x, + const time_zone_link& __y) noexcept { + return __x.name() == __y.name(); +} + +[[nodiscard]] inline bool operator!=( + const time_zone_link& __x, + const time_zone_link& __y) noexcept { + return !(__x.name() == __y.name()); +} + +[[nodiscard]] inline bool operator<( + const time_zone_link& __x, + const time_zone_link& __y) noexcept { + return __x.name() < __y.name(); +} + +[[nodiscard]] inline bool operator>( + const time_zone_link& __x, + const time_zone_link& __y) noexcept { + return __x.name() > __y.name(); +} + +[[nodiscard]] inline bool operator<=( + const time_zone_link& __x, + const time_zone_link& __y) noexcept { + return !(__x.name() > __y.name()); +} + +[[nodiscard]] inline bool operator>=( + const time_zone_link& __x, + const time_zone_link& __y) noexcept { + return !(__x.name() < __y.name()); +} + +// [[nodiscard]] inline strong_ordering +// operator<=>(const time_zone_link& __x, const time_zone_link& __y) noexcept { +// return __x.name() <=> __y.name(); +// } + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/time_zone_private.h b/velox/external/tzdb/time_zone_private.h new file mode 100644 index 000000000000..f56bcfaaa450 --- /dev/null +++ b/velox/external/tzdb/time_zone_private.h @@ -0,0 +1,56 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include +#include + +#include "velox/external/tzdb/time_zone.h" +#include "velox/external/tzdb/types_private.h" + +namespace facebook::velox::tzdb { + +class time_zone::__impl { + public: + explicit __impl(std::string&& __name, const __rules_storage_type& __rules_db) + : __name_(std::move(__name)), __rules_db_(__rules_db) {} + + [[nodiscard]] std::string_view __name() const noexcept { + return __name_; + } + + [[nodiscard]] std::vector& __continuations() { + return __continuations_; + } + [[nodiscard]] const std::vector& __continuations() const { + return __continuations_; + } + + [[nodiscard]] const __rules_storage_type& __rules_db() const { + return __rules_db_; + } + + private: + std::string __name_; + // Note the first line has a name + __continuation, the other lines + // are just __continuations. So there is always at least one item in + // the vector. + std::vector __continuations_; + + // Continuations often depend on a set of rules. The rules are stored in + // parallel data structurs in tzdb_list. From the time_zone it's not possible + // to find its associated tzdb entry and thus not possible to find its + // associated rules. Therefore a link to the rules in stored in this class. + const __rules_storage_type& __rules_db_; +}; + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/types_private.h b/velox/external/tzdb/types_private.h new file mode 100644 index 000000000000..5477673e7be6 --- /dev/null +++ b/velox/external/tzdb/types_private.h @@ -0,0 +1,123 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include +#include +#include +#include +#include + +#include "velox/external/date/date.h" + +// TODO TZDB +// The helper classes in this header have no constructor but are loaded with +// dedicated parse functions. In the original design this header was public and +// the parsing was done in the dylib. In that design having constructors would +// expand the ABI interface. Since this header is now in the dylib that design +// should be reconsidered. (For now the design is kept as is, in case this +// header needs to be public for unforseen reasons.) + +namespace facebook::velox::tzdb { + +// Sun>=8 first Sunday on or after the eighth +// Sun<=25 last Sunday on or before the 25th +struct __constrained_weekday { + [[nodiscard]] date::year_month_day operator()( + date::year __year, + date::month __month) const { + auto __result = static_cast( + date::year_month_day{__year, __month, __day}); + date::weekday __wd{static_cast(__result)}; + + if (__comparison == __le) + __result -= __wd - __weekday; + else + __result += __weekday - __wd; + + return __result; + } + + date::weekday __weekday; + enum __comparison_t { __le, __ge } __comparison; + date::day __day; +}; + +// The on field has a few alternative presentations +// 5 the fifth of the month +// lastSun the last Sunday in the month +// lastMon the last Monday in the month +// Sun>=8 first Sunday on or after the eighth +// Sun<=25 last Sunday on or before the 25th +using __on = std::variant; + +enum class __clock { __local, __standard, __universal }; + +struct __at { + std::chrono::seconds __time{0}; + facebook::velox::tzdb::__clock __clock{ + facebook::velox::tzdb::__clock::__local}; +}; + +struct __save { + std::chrono::seconds __time; + bool __is_dst; +}; + +// The names of the fields match the fields of a Rule. +struct __rule { + date::year __from; + date::year __to; + date::month __in; + facebook::velox::tzdb::__on __on; + facebook::velox::tzdb::__at __at; + facebook::velox::tzdb::__save __save; + std::string __letters; +}; + +using __rules_storage_type = + std::vector>>; // TODO TZDB use + // flat_map; + +struct __continuation { + // Non-owning link to the RULE entries. + __rules_storage_type* __rule_database_; + + std::chrono::seconds __stdoff; + + // The RULES is either a SAVE or a NAME. + // The size_t is used as cache. After loading the rules they are + // sorted and remain stable, then an index in the vector can be + // used. + // If this field contains - then standard time always + // applies. This is indicated by the monostate. + // TODO TZDB Investigate implantation the size_t based caching. + using __rules_t = std::variant< + std::monostate, + facebook::velox::tzdb::__save, + std::string /*, size_t*/>; + + __rules_t __rules; + + std::string __format; + // TODO TZDB the until field can contain more than just a year. + // Parts of the UNTIL, the optional parts are default initialized + // optional __until_; + date::year __year = date::year::min(); + date::month __in{date::January}; + facebook::velox::tzdb::__on __on{date::day{1}}; + facebook::velox::tzdb::__at __at{ + std::chrono::seconds{0}, + facebook::velox::tzdb::__clock::__local}; +}; + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/tzdb.cpp b/velox/external/tzdb/tzdb.cpp new file mode 100644 index 000000000000..06c399b3e9f4 --- /dev/null +++ b/velox/external/tzdb/tzdb.cpp @@ -0,0 +1,891 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "velox/external/date/date.h" +#include "velox/external/tzdb/time_zone_private.h" +#include "velox/external/tzdb/types_private.h" +#include "velox/external/tzdb/tzdb_list_private.h" +#include "velox/external/tzdb/tzdb_private.h" + +// Contains a parser for the IANA time zone data files. +// +// These files can be found at https://data.iana.org/time-zones/ and are in the +// public domain. Information regarding the input can be found at +// https://data.iana.org/time-zones/tz-how-to.html and +// https://man7.org/linux/man-pages/man8/zic.8.html. +// +// As indicated at https://howardhinnant.github.io/date/tz.html#Installation +// For Windows another file seems to be required +// https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml +// This file seems to contain the mapping of Windows time zone name to IANA +// time zone names. +// +// However this article mentions another way to do the mapping on Windows +// https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255 +// This requires Windows 10 Version 1903, which was released in May of 2019 +// and considered end of life in December 2020 +// https://learn.microsoft.com/en-us/lifecycle/announcements/windows-10-1903-end-of-servicing +// +// TODO TZDB Implement the Windows mapping in tzdb::current_zone + +namespace facebook::velox::tzdb { + +using namespace std::chrono_literals; + +// This function is weak so it can be overridden in the tests. The +// declaration is in the test header test/support/test_tzdb.h +std::string __libcpp_tzdb_directory() { + struct stat sb; + using namespace std; +#if !defined(__APPLE__) + CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo"; + CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc"; + + // Check special path which is valid for buildroot with uclibc builds + if (stat(tz_dir_buildroot, &sb) == 0 && S_ISDIR(sb.st_mode)) + return tz_dir_buildroot; + else if (stat(tz_dir_default, &sb) == 0 && S_ISDIR(sb.st_mode)) + return tz_dir_default; + else + throw runtime_error("discover_tz_dir failed to find zoneinfo\n"); +#else // __APPLE__ + CONSTDATA auto timezone = "/etc/localtime"; + if (!(lstat(timezone, &sb) == 0 && S_ISLNK(sb.st_mode) && sb.st_size > 0)) + throw runtime_error("discover_tz_dir failed\n"); + string result; + unique_ptr rp(new char[sb.st_size]); + const auto rp_length = readlink(timezone, rp.get(), sb.st_size); + if (rp_length > 0) + result = string(rp.get(), rp_length); // readlink doesn't null-terminate + else + throw system_error(errno, system_category(), "readlink() failed"); + auto i = result.find("zoneinfo"); + if (i == string::npos) + throw runtime_error("discover_tz_dir failed to find zoneinfo\n"); + i = result.find('/', i); + if (i == string::npos) + throw runtime_error("discover_tz_dir failed to find '/'\n"); + return result.substr(0, i); +#endif // __APPLE__ +} + +//===----------------------------------------------------------------------===// +// Details +//===----------------------------------------------------------------------===// + +[[nodiscard]] static bool __is_whitespace(int __c) { + return __c == ' ' || __c == '\t'; +} + +static void __skip_optional_whitespace(std::istream& __input) { + while (__is_whitespace(__input.peek())) + __input.get(); +} + +static void __skip_mandatory_whitespace(std::istream& __input) { + if (!__is_whitespace(__input.get())) + std::__throw_runtime_error("corrupt tzdb: expected whitespace"); + + __skip_optional_whitespace(__input); +} + +[[nodiscard]] static bool __is_eol(int __c) { + return __c == '\n' || __c == std::char_traits::eof(); +} + +static void __skip_line(std::istream& __input) { + while (!__is_eol(__input.peek())) { + __input.get(); + } + __input.get(); +} + +static void __skip(std::istream& __input, char __suffix) { + if (std::tolower(__input.peek()) == __suffix) + __input.get(); +} + +static void __skip(std::istream& __input, std::string_view __suffix) { + for (auto __c : __suffix) + if (std::tolower(__input.peek()) == __c) + __input.get(); +} + +static void __matches(std::istream& __input, char __expected) { + if (std::isalpha(__expected) && !std::islower(__expected)) { + throw std::runtime_error("lowercase characters only here!"); + } + char __c = __input.get(); + if (std::tolower(__c) != __expected) + std::__throw_runtime_error( + (std::string("corrupt tzdb: expected character '") + __expected + + "', got '" + __c + "' instead") + .c_str()); +} + +static void __matches(std::istream& __input, std::string_view __expected) { + for (auto __c : __expected) { + if (std::isalpha(__c) && !std::islower(__c)) { + throw std::runtime_error("lowercase strings only here!"); + } + char __actual = __input.get(); + if (std::tolower(__actual) != __c) + std::__throw_runtime_error( + (std::string("corrupt tzdb: expected character '") + __c + + "' from string '" + std::string(__expected) + "', got '" + __actual + + "' instead") + .c_str()); + } +} + +[[nodiscard]] static std::string __parse_string(std::istream& __input) { + std::string __result; + while (true) { + int __c = __input.get(); + switch (__c) { + case ' ': + case '\t': + case '\n': + __input.unget(); + [[fallthrough]]; + case std::istream::traits_type::eof(): + if (__result.empty()) + std::__throw_runtime_error("corrupt tzdb: expected a string"); + + return __result; + + default: + __result.push_back(__c); + } + } +} + +[[nodiscard]] static int64_t __parse_integral( + std::istream& __input, + bool __leading_zero_allowed) { + int64_t __result = __input.get(); + if (__leading_zero_allowed) { + if (__result < '0' || __result > '9') + std::__throw_runtime_error("corrupt tzdb: expected a digit"); + } else { + if (__result < '1' || __result > '9') + std::__throw_runtime_error("corrupt tzdb: expected a non-zero digit"); + } + __result -= '0'; + while (true) { + if (__input.peek() < '0' || __input.peek() > '9') + return __result; + + // In order to avoid possible overflows we limit the accepted range. + // Most values parsed are expected to be very small: + // - 8784 hours in a year + // - 31 days in a month + // - year no real maximum, these values are expected to be less than + // the range of the year type. + // + // However the leapseconds use a seconds after epoch value. Using an + // int would run into an overflow in 2038. By using a 64-bit value + // the range is large enough for the bilions of years. Limiting that + // range slightly to make the code easier is not an issue. + if (__result > (std::numeric_limits::max() / 16)) + std::__throw_runtime_error("corrupt tzdb: integral too large"); + + __result *= 10; + __result += __input.get() - '0'; + } +} + +//===----------------------------------------------------------------------===// +// Calendar +//===----------------------------------------------------------------------===// + +[[nodiscard]] static date::day __parse_day(std::istream& __input) { + unsigned __result = __parse_integral(__input, false); + if (__result > 31) + std::__throw_runtime_error("corrupt tzdb day: value too large"); + return date::day{__result}; +} + +[[nodiscard]] static date::weekday __parse_weekday(std::istream& __input) { + // TZDB allows the shortest unique name. + switch (std::tolower(__input.get())) { + case 'f': + __skip(__input, "riday"); + return date::Friday; + + case 'm': + __skip(__input, "onday"); + return date::Monday; + + case 's': + switch (std::tolower(__input.get())) { + case 'a': + __skip(__input, "turday"); + return date::Saturday; + + case 'u': + __skip(__input, "nday"); + return date::Sunday; + } + break; + + case 't': + switch (std::tolower(__input.get())) { + case 'h': + __skip(__input, "ursday"); + return date::Thursday; + + case 'u': + __skip(__input, "esday"); + return date::Tuesday; + } + break; + case 'w': + __skip(__input, "ednesday"); + return date::Wednesday; + } + + std::__throw_runtime_error("corrupt tzdb weekday: invalid name"); +} + +[[nodiscard]] static date::month __parse_month(std::istream& __input) { + // TZDB allows the shortest unique name. + switch (std::tolower(__input.get())) { + case 'a': + switch (std::tolower(__input.get())) { + case 'p': + __skip(__input, "ril"); + return date::April; + + case 'u': + __skip(__input, "gust"); + return date::August; + } + break; + + case 'd': + __skip(__input, "ecember"); + return date::December; + + case 'f': + __skip(__input, "ebruary"); + return date::February; + + case 'j': + switch (std::tolower(__input.get())) { + case 'a': + __skip(__input, "nuary"); + return date::January; + + case 'u': + switch (std::tolower(__input.get())) { + case 'n': + __skip(__input, 'e'); + return date::June; + + case 'l': + __skip(__input, 'y'); + return date::July; + } + } + break; + + case 'm': + if (std::tolower(__input.get()) == 'a') + switch (std::tolower(__input.get())) { + case 'y': + return date::May; + + case 'r': + __skip(__input, "ch"); + return date::March; + } + break; + + case 'n': + __skip(__input, "ovember"); + return date::November; + + case 'o': + __skip(__input, "ctober"); + return date::October; + + case 's': + __skip(__input, "eptember"); + return date::September; + } + std::__throw_runtime_error("corrupt tzdb month: invalid name"); +} + +[[nodiscard]] static date::year __parse_year_value(std::istream& __input) { + bool __negative = __input.peek() == '-'; + if (__negative) [[unlikely]] + __input.get(); + + int64_t __result = __parse_integral(__input, true); + if (__result > static_cast(date::year::max())) { + if (__negative) + std::__throw_runtime_error( + "corrupt tzdb year: year is less than the minimum"); + + std::__throw_runtime_error( + "corrupt tzdb year: year is greater than the maximum"); + } + + return date::year{static_cast(__negative ? -__result : __result)}; +} + +[[nodiscard]] static date::year __parse_year(std::istream& __input) { + if (std::tolower(__input.peek()) != 'm') [[likely]] + return __parse_year_value(__input); + + __input.get(); + switch (std::tolower(__input.peek())) { + case 'i': + __input.get(); + __skip(__input, 'n'); + [[fallthrough]]; + + case ' ': + // The m is minimum, even when that is ambiguous. + return date::year::min(); + + case 'a': + __input.get(); + __skip(__input, 'x'); + return date::year::max(); + } + + std::__throw_runtime_error("corrupt tzdb year: expected 'min' or 'max'"); +} + +//===----------------------------------------------------------------------===// +// TZDB fields +//===----------------------------------------------------------------------===// + +[[nodiscard]] static date::year __parse_to( + std::istream& __input, + date::year __only) { + if (std::tolower(__input.peek()) != 'o') + return __parse_year(__input); + + __input.get(); + __skip(__input, "nly"); + return __only; +} + +[[nodiscard]] static __constrained_weekday::__comparison_t __parse_comparison( + std::istream& __input) { + switch (__input.get()) { + case '>': + __matches(__input, '='); + return __constrained_weekday::__ge; + + case '<': + __matches(__input, '='); + return __constrained_weekday::__le; + } + std::__throw_runtime_error("corrupt tzdb on: expected '>=' or '<='"); +} + +[[nodiscard]] static facebook::velox::tzdb::__on __parse_on( + std::istream& __input) { + if (std::isdigit(__input.peek())) + return __parse_day(__input); + + if (std::tolower(__input.peek()) == 'l') { + __matches(__input, "last"); + return date::weekday_last(__parse_weekday(__input)); + } + + return __constrained_weekday{ + __parse_weekday(__input), + __parse_comparison(__input), + __parse_day(__input)}; +} + +[[nodiscard]] static std::chrono::seconds __parse_duration( + std::istream& __input) { + std::chrono::seconds __result{0}; + int __c = __input.peek(); + bool __negative = __c == '-'; + if (__negative) { + __input.get(); + // Negative is either a negative value or a single -. + // The latter means 0 and the parsing is complete. + if (!std::isdigit(__input.peek())) + return __result; + } + + __result += std::chrono::hours(__parse_integral(__input, true)); + if (__input.peek() != ':') + return __negative ? -__result : __result; + + __input.get(); + __result += std::chrono::minutes(__parse_integral(__input, true)); + if (__input.peek() != ':') + return __negative ? -__result : __result; + + __input.get(); + __result += std::chrono::seconds(__parse_integral(__input, true)); + if (__input.peek() != '.') + return __negative ? -__result : __result; + + __input.get(); + (void)__parse_integral(__input, true); // Truncate the digits. + + return __negative ? -__result : __result; +} + +[[nodiscard]] static facebook::velox::tzdb::__clock __parse_clock( + std::istream& __input) { + switch (__input.get()) { // case sensitive + case 'w': + return facebook::velox::tzdb::__clock::__local; + case 's': + return facebook::velox::tzdb::__clock::__standard; + + case 'u': + case 'g': + case 'z': + return facebook::velox::tzdb::__clock::__universal; + } + + __input.unget(); + return facebook::velox::tzdb::__clock::__local; +} + +[[nodiscard]] static bool __parse_dst( + std::istream& __input, + std::chrono::seconds __offset) { + switch (__input.get()) { // case sensitive + case 's': + return false; + + case 'd': + return true; + } + + __input.unget(); + return __offset != 0s; +} + +[[nodiscard]] static facebook::velox::tzdb::__at __parse_at( + std::istream& __input) { + return {__parse_duration(__input), __parse_clock(__input)}; +} + +[[nodiscard]] static facebook::velox::tzdb::__save __parse_save( + std::istream& __input) { + std::chrono::seconds __time = __parse_duration(__input); + return {__time, __parse_dst(__input, __time)}; +} + +[[nodiscard]] static std::string __parse_letters(std::istream& __input) { + std::string __result = __parse_string(__input); + // Canonicalize "-" to "" since they are equivalent in the specification. + return __result != "-" ? __result : ""; +} + +[[nodiscard]] static __continuation::__rules_t __parse_rules( + std::istream& __input) { + int __c = __input.peek(); + // A single - is not a SAVE but a special case. + if (__c == '-') { + __input.get(); + if (__is_whitespace(__input.peek())) + return std::monostate{}; + __input.unget(); + return __parse_save(__input); + } + + if (std::isdigit(__c) || __c == '+') + return __parse_save(__input); + + return __parse_string(__input); +} + +[[nodiscard]] static facebook::velox::tzdb::__continuation __parse_continuation( + __rules_storage_type& __rules, + std::istream& __input) { + facebook::velox::tzdb::__continuation __result; + + __result.__rule_database_ = std::addressof(__rules); + + // Note STDOFF is specified as + // This field has the same format as the AT and SAVE fields of rule lines; + // These fields have different suffix letters, these letters seem + // not to be used so do not allow any of them. + + __result.__stdoff = __parse_duration(__input); + __skip_mandatory_whitespace(__input); + __result.__rules = __parse_rules(__input); + __skip_mandatory_whitespace(__input); + __result.__format = __parse_string(__input); + __skip_optional_whitespace(__input); + + if (__is_eol(__input.peek())) + return __result; + __result.__year = __parse_year(__input); + __skip_optional_whitespace(__input); + + if (__is_eol(__input.peek())) + return __result; + __result.__in = __parse_month(__input); + __skip_optional_whitespace(__input); + + if (__is_eol(__input.peek())) + return __result; + __result.__on = __parse_on(__input); + __skip_optional_whitespace(__input); + + if (__is_eol(__input.peek())) + return __result; + __result.__at = __parse_at(__input); + + return __result; +} + +//===----------------------------------------------------------------------===// +// Time Zone Database entries +//===----------------------------------------------------------------------===// + +static std::string __parse_version(std::istream& __input) { + // The first line in tzdata.zi contains + // # version YYYYw + // The parser expects this pattern + // #\s*version\s*\(.*) + // This part is not documented. + __matches(__input, '#'); + __skip_optional_whitespace(__input); + __matches(__input, "version"); + __skip_mandatory_whitespace(__input); + return __parse_string(__input); +} + +[[nodiscard]] +static __rule& __create_entry( + __rules_storage_type& __rules, + const std::string& __name) { + auto __result = [&]() -> __rule& { + auto& rule = __rules.emplace_back(__name, std::vector<__rule>{}); + return rule.second.emplace_back(); + }; + + if (__rules.empty()) + return __result(); + + // Typically rules are in contiguous order in the database. + // But there are exceptions, some rules are interleaved. + if (__rules.back().first == __name) + return __rules.back().second.emplace_back(); + + if (auto __it = std::find_if( + __rules.begin(), + __rules.end(), + [&__name](const auto& __r) { return __r.first == __name; }); + __it != __rules.end()) + return __it->second.emplace_back(); + + return __result(); +} + +static void __parse_rule( + tzdb& __tzdb, + __rules_storage_type& __rules, + std::istream& __input) { + __skip_mandatory_whitespace(__input); + std::string __name = __parse_string(__input); + + __rule& __rule = __create_entry(__rules, __name); + + __skip_mandatory_whitespace(__input); + __rule.__from = __parse_year(__input); + __skip_mandatory_whitespace(__input); + __rule.__to = __parse_to(__input, __rule.__from); + __skip_mandatory_whitespace(__input); + __matches(__input, '-'); + __skip_mandatory_whitespace(__input); + __rule.__in = __parse_month(__input); + __skip_mandatory_whitespace(__input); + __rule.__on = __parse_on(__input); + __skip_mandatory_whitespace(__input); + __rule.__at = __parse_at(__input); + __skip_mandatory_whitespace(__input); + __rule.__save = __parse_save(__input); + __skip_mandatory_whitespace(__input); + __rule.__letters = __parse_letters(__input); + __skip_line(__input); +} + +static void __parse_zone( + tzdb& __tzdb, + __rules_storage_type& __rules, + std::istream& __input) { + __skip_mandatory_whitespace(__input); + auto __p = + std::make_unique(__parse_string(__input), __rules); + std::vector& __continuations = __p->__continuations(); + __skip_mandatory_whitespace(__input); + + do { + // The first line must be valid, continuations are optional. + __continuations.emplace_back(__parse_continuation(__rules, __input)); + __skip_line(__input); + __skip_optional_whitespace(__input); + } while (std::isdigit(__input.peek()) || __input.peek() == '-'); + + __tzdb.zones.emplace_back(time_zone::__create(std::move(__p))); +} + +static void __parse_link(tzdb& __tzdb, std::istream& __input) { + __skip_mandatory_whitespace(__input); + std::string __target = __parse_string(__input); + __skip_mandatory_whitespace(__input); + std::string __name = __parse_string(__input); + __skip_line(__input); + + __tzdb.links.emplace_back(std::move(__name), std::move(__target)); +} + +static void __parse_tzdata( + tzdb& __db, + __rules_storage_type& __rules, + std::istream& __input) { + while (true) { + int __c = std::tolower(__input.get()); + + switch (__c) { + case std::istream::traits_type::eof(): + return; + + case ' ': + case '\t': + case '\n': + break; + + case '#': + __skip_line(__input); + break; + + case 'r': + __skip(__input, "ule"); + __parse_rule(__db, __rules, __input); + break; + + case 'z': + __skip(__input, "one"); + __parse_zone(__db, __rules, __input); + break; + + case 'l': + __skip(__input, "ink"); + __parse_link(__db, __input); + break; + + default: + std::__throw_runtime_error("corrupt tzdb: unexpected input"); + } + } +} + +static void __parse_leap_seconds( + std::vector& __leap_seconds, + std::istream&& __input) { + // The file stores dates since 1 January 1900, 00:00:00, we want + // seconds since 1 January 1970. + constexpr auto __offset = + date::sys_days{date::year(1970) / date::January / 1} - + date::sys_days{date::year(1900) / date::January / 1}; + + struct __entry { + __entry(date::sys_seconds __timestamp, std::chrono::seconds __value) + : __timestamp(__timestamp), __value(__value) {} + + date::sys_seconds __timestamp; + std::chrono::seconds __value; + }; + std::vector<__entry> __entries; + [&] { + while (true) { + switch (__input.peek()) { + case std::istream::traits_type::eof(): + return; + + case ' ': + case '\t': + case '\n': + __input.get(); + continue; + + case '#': + __skip_line(__input); + continue; + } + + date::sys_seconds __date = date::sys_seconds{std::chrono::seconds{ + __parse_integral(__input, false)}} - + __offset; + __skip_mandatory_whitespace(__input); + std::chrono::seconds __value{__parse_integral(__input, false)}; + __skip_line(__input); + + __entries.emplace_back(__date, __value); + } + }(); + // The Standard requires the leap seconds to be sorted. The file + // leap-seconds.list usually provides them in sorted order, but that is not + // guaranteed so we ensure it here. + std::sort( + __entries.begin(), + __entries.end(), + [](const auto& left_entry, const auto& right_entry) { + return left_entry.__timestamp < right_entry.__timestamp; + }); + + // The database should contain the number of seconds inserted by a leap + // second (1 or -1). So the difference between the two elements is stored. + // std::ranges::views::adjacent has not been implemented yet. + (void)std::adjacent_find( + __entries.begin(), + __entries.end(), + [&](const __entry& __first, const __entry& __second) { + __leap_seconds.emplace_back( + __second.__timestamp, __second.__value - __first.__value); + return false; + }); +} + +void __init_tzdb(tzdb& __tzdb, __rules_storage_type& __rules) { + std::filesystem::path __root = __libcpp_tzdb_directory(); + std::ifstream __tzdata{__root / "tzdata.zi"}; + + __tzdb.version = __parse_version(__tzdata); + __parse_tzdata(__tzdb, __rules, __tzdata); + std::sort(__tzdb.zones.begin(), __tzdb.zones.end()); + std::sort(__tzdb.links.begin(), __tzdb.links.end()); + std::sort( + __rules.begin(), + __rules.end(), + [](const auto& __left, const auto& __right) { + return __left.first < __right.first; + }); + + // There are two files with the leap second information + // - leapseconds as specified by zic + // - leap-seconds.list the source data + // The latter is much easier to parse, it seems Howard shares that + // opinion. + __parse_leap_seconds( + __tzdb.leap_seconds, std::ifstream{__root / "leap-seconds.list"}); +} + +#ifdef _WIN32 +[[nodiscard]] static const time_zone* __current_zone_windows(const tzdb& tzdb) { + // TODO TZDB Implement this on Windows. + std::__throw_runtime_error("unknown time zone"); +} +#else // ifdef _WIN32 +[[nodiscard]] static const time_zone* __current_zone_posix(const tzdb& tzdb) { + // On POSIX systems there are several ways to configure the time zone. + // In order of priority they are: + // - TZ environment variable + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08 + // The documentation is unclear whether or not it's allowed to + // change time zone information. For example the TZ string + // MST7MDT + // this is an entry in tzdata.zi. The value + // MST + // is also an entry. Is it allowed to use the following? + // MST-3 + // Even when this is valid there is no time_zone record in the + // database. Since the library would need to return a valid pointer, + // this means the library needs to allocate and leak a pointer. + // + // - The time zone name is the target of the symlink /etc/localtime + // relative to /usr/share/zoneinfo/ + + // The algorithm is like this: + // - If the environment variable TZ is set and points to a valid + // record use this value. + // - Else use the name based on the `/etc/localtime` symlink. + + if (const char* __tz = getenv("TZ")) + if (const time_zone* __result = tzdb.__locate_zone(__tz)) + return __result; + + std::filesystem::path __path = "/etc/localtime"; + if (!std::filesystem::exists(__path)) + std::__throw_runtime_error( + "tzdb: the symlink '/etc/localtime' does not exist"); + + if (!std::filesystem::is_symlink(__path)) + std::__throw_runtime_error( + "tzdb: the path '/etc/localtime' is not a symlink"); + + std::filesystem::path __tz = std::filesystem::read_symlink(__path); + // The path may be a relative path, in that case convert it to an absolute + // path based on the proper initial directory. + if (__tz.is_relative()) + __tz = std::filesystem::canonical("/etc" / __tz); + + std::string __name = std::filesystem::relative(__tz, "/usr/share/zoneinfo/"); + if (const time_zone* __result = tzdb.__locate_zone(__name)) + return __result; + + std::__throw_runtime_error( + ("tzdb: the time zone '" + __name + "' is not found in the database") + .c_str()); +} +#endif // ifdef _WIN32 + +//===----------------------------------------------------------------------===// +// Public API +//===----------------------------------------------------------------------===// + +tzdb_list& get_tzdb_list() { + static tzdb_list __result{new tzdb_list::__impl()}; + return __result; +} + +[[nodiscard]] const time_zone* tzdb::__current_zone() const { +#ifdef _WIN32 + return __current_zone_windows(*this); +#else + return __current_zone_posix(*this); +#endif +} + +const tzdb& reload_tzdb() { + if (remote_version() == get_tzdb().version) + return get_tzdb(); + + return get_tzdb_list().__implementation().__load(); +} + +std::string remote_version() { + std::filesystem::path __root = __libcpp_tzdb_directory(); + std::ifstream __tzdata{__root / "tzdata.zi"}; + return __parse_version(__tzdata); +} + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/tzdb.h b/velox/external/tzdb/tzdb.h new file mode 100644 index 000000000000..efdf2b7f1af3 --- /dev/null +++ b/velox/external/tzdb/tzdb.h @@ -0,0 +1,79 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include +#include +#include +#include +#include +#include "velox/external/tzdb/leap_second.h" +#include "velox/external/tzdb/time_zone.h" +#include "velox/external/tzdb/time_zone_link.h" + +namespace facebook::velox::tzdb { + +struct tzdb { + std::string version; + std::vector zones; + std::vector links; + + std::vector leap_seconds; + + [[nodiscard]] const time_zone* __locate_zone(std::string_view __name) const { + if (const time_zone* __result = __find_in_zone(__name)) + return __result; + + if (auto __it = std::lower_bound( + links.begin(), + links.end(), + __name, + [](const time_zone_link& link, const std::string_view& name) { + return link.name() < name; + }); + __it != links.end() && __it->name() == __name) + if (const time_zone* __result = __find_in_zone(__it->target())) + return __result; + + return nullptr; + } + + [[nodiscard]] const time_zone* locate_zone(std::string_view __name) const { + if (const time_zone* __result = __locate_zone(__name)) + return __result; + + std::__throw_runtime_error("tzdb: requested time zone not found"); + } + + [[nodiscard]] const time_zone* current_zone() const { + return __current_zone(); + } + + private: + const time_zone* __find_in_zone(std::string_view __name) const noexcept { + if (auto __it = std::lower_bound( + zones.begin(), + zones.end(), + __name, + [](const time_zone& zone, const std::string_view& name) { + return zone.name() < name; + }); + __it != zones.end() && __it->name() == __name) + return std::addressof(*__it); + + return nullptr; + } + + [[nodiscard]] const time_zone* __current_zone() const; +}; + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/tzdb_list.cpp b/velox/external/tzdb/tzdb_list.cpp new file mode 100644 index 000000000000..b98eecca28f7 --- /dev/null +++ b/velox/external/tzdb/tzdb_list.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#include "velox/external/tzdb/tzdb_list_private.h" + +namespace facebook::velox::tzdb { + +tzdb_list::~tzdb_list() { + delete __impl_; +} + +[[nodiscard]] const tzdb& tzdb_list::__front() const noexcept { + return __impl_->__front(); +} + +tzdb_list::const_iterator tzdb_list::__erase_after(const_iterator __p) { + return __impl_->__erase_after(__p); +} + +[[nodiscard]] tzdb_list::const_iterator tzdb_list::__begin() const noexcept { + return __impl_->__begin(); +} +[[nodiscard]] tzdb_list::const_iterator tzdb_list::__end() const noexcept { + return __impl_->__end(); +} + +[[nodiscard]] tzdb_list::const_iterator tzdb_list::__cbegin() const noexcept { + return __impl_->__begin(); +} +[[nodiscard]] tzdb_list::const_iterator tzdb_list::__cend() const noexcept { + return __impl_->__end(); +} + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/tzdb_list.h b/velox/external/tzdb/tzdb_list.h new file mode 100644 index 000000000000..ff45a64e4430 --- /dev/null +++ b/velox/external/tzdb/tzdb_list.h @@ -0,0 +1,99 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include +#include +#include "velox/external/tzdb/time_zone.h" +#include "velox/external/tzdb/tzdb.h" + +namespace facebook::velox::tzdb { + +// TODO TZDB +// Libc++ recently switched to only export __ugly_names from the dylib. +// Since the library is still experimental the functions in this header +// should be adapted to this new style. The other tzdb headers should be +// evaluated too. + +class tzdb_list { + public: + class __impl; // public to allow construction in dylib + explicit tzdb_list(__impl* __p) : __impl_(__p) { + // _LIBCPP_ASSERT_NON_NULL(__impl_ != nullptr, "initialized time_zone + // without a valid pimpl object"); + } + ~tzdb_list(); + + tzdb_list(const tzdb_list&) = delete; + tzdb_list& operator=(const tzdb_list&) = delete; + + using const_iterator = std::forward_list::const_iterator; + + [[nodiscard]] const tzdb& front() const noexcept { + return __front(); + } + + const_iterator erase_after(const_iterator __p) { + return __erase_after(__p); + } + + [[nodiscard]] const_iterator begin() const noexcept { + return __begin(); + } + [[nodiscard]] const_iterator end() const noexcept { + return __end(); + } + + [[nodiscard]] const_iterator cbegin() const noexcept { + return __cbegin(); + } + [[nodiscard]] const_iterator cend() const noexcept { + return __cend(); + } + + [[nodiscard]] __impl& __implementation() { + return *__impl_; + } + + private: + [[nodiscard]] const tzdb& __front() const noexcept; + + const_iterator __erase_after(const_iterator __p); + + [[nodiscard]] const_iterator __begin() const noexcept; + [[nodiscard]] const_iterator __end() const noexcept; + + [[nodiscard]] const_iterator __cbegin() const noexcept; + [[nodiscard]] const_iterator __cend() const noexcept; + + __impl* __impl_; +}; + +[[nodiscard]] tzdb_list& get_tzdb_list(); + +[[nodiscard]] inline const tzdb& get_tzdb() { + return get_tzdb_list().front(); +} + +[[nodiscard]] inline const time_zone* locate_zone(std::string_view __name) { + return get_tzdb().locate_zone(__name); +} + +[[nodiscard]] inline const time_zone* current_zone() { + return get_tzdb().current_zone(); +} + +const tzdb& reload_tzdb(); + +[[nodiscard]] std::string remote_version(); + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/tzdb_list_private.h b/velox/external/tzdb/tzdb_list_private.h new file mode 100644 index 000000000000..9fa8dbab56b8 --- /dev/null +++ b/velox/external/tzdb/tzdb_list_private.h @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include +#include "velox/external/tzdb/types_private.h" +#include "velox/external/tzdb/tzdb_list.h" +#include "velox/external/tzdb/tzdb_private.h" + +#if _LIBCPP_HAS_THREADS +#include +#endif + +namespace facebook::velox::tzdb { + +//===----------------------------------------------------------------------===// +// Private API +//===----------------------------------------------------------------------===// + +// The tzdb_list stores a list of "tzdb" entries. +// +// The public tzdb database does not store the RULE entries of the IANA +// database. These entries are considered an implementation detail. Since most +// of the tzdb_list interface is exposed as "a list of tzdb entries" it's not +// possible to use a helper struct that stores a tzdb and the RULE database. +// Instead this class stores these in parallel forward lists. +// +// Since the nodes of a forward_list are stable it's possible to store pointers +// and references to these nodes. +class tzdb_list::__impl { + public: + __impl() { + __load_no_lock(); + } + + [[nodiscard]] const tzdb& __load() { +#if _LIBCPP_HAS_THREADS + unique_lock __lock{__mutex_}; +#endif + __load_no_lock(); + return __tzdb_.front(); + } + + using const_iterator = tzdb_list::const_iterator; + + const tzdb& __front() const noexcept { +#if _LIBCPP_HAS_THREADS + unique_lock __lock{__mutex_}; +#endif + return __tzdb_.front(); + } + + const_iterator __erase_after(const_iterator __p) { +#if _LIBCPP_HAS_THREADS + unique_lock __lock{__mutex_}; +#endif + + __rules_.erase_after( + std::next(__rules_.cbegin(), std::distance(__tzdb_.cbegin(), __p))); + return __tzdb_.erase_after(__p); + } + + const_iterator __begin() const noexcept { +#if _LIBCPP_HAS_THREADS + unique_lock __lock{__mutex_}; +#endif + return __tzdb_.begin(); + } + const_iterator __end() const noexcept { + // forward_list::end does not access the list, so no need to take a + // lock. + return __tzdb_.end(); + } + + private: + // Loads the tzdbs + // pre: The caller ensures the locking, if needed, is done. + void __load_no_lock() { + __init_tzdb(__tzdb_.emplace_front(), __rules_.emplace_front()); + } + +#if _LIBCPP_HAS_THREADS + mutable mutex __mutex_; +#endif + std::forward_list __tzdb_; + + std::forward_list<__rules_storage_type> __rules_; +}; + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/tzdb_private.h b/velox/external/tzdb/tzdb_private.h new file mode 100644 index 000000000000..77715d3d7a91 --- /dev/null +++ b/velox/external/tzdb/tzdb_private.h @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once + +#include "velox/external/tzdb/types_private.h" +#include "velox/external/tzdb/tzdb.h" + +namespace facebook::velox::tzdb { + +void __init_tzdb(tzdb& __tzdb, __rules_storage_type& __rules); + +} // namespace facebook::velox::tzdb diff --git a/velox/external/tzdb/zoned_time.h b/velox/external/tzdb/zoned_time.h new file mode 100644 index 000000000000..3d95d94ee11a --- /dev/null +++ b/velox/external/tzdb/zoned_time.h @@ -0,0 +1,235 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#pragma once +#include +#include +#include +#include +#include +#include "velox/external/date/date.h" +#include "velox/external/tzdb/sys_info.h" +#include "velox/external/tzdb/time_zone.h" +#include "velox/external/tzdb/tzdb_list.h" + +namespace facebook::velox::tzdb { + +template +struct zoned_traits {}; + +template <> +struct zoned_traits { + [[nodiscard]] static const time_zone* default_zone() { + return locate_zone("UTC"); + } + [[nodiscard]] static const time_zone* locate_zone(std::string_view __name) { + return facebook::velox::tzdb::locate_zone(__name); + } +}; + +template +class zoned_time { + // [time.zone.zonedtime.ctor]/2 + // static_assert(__is_duration_v<_Duration>, + // "the program is ill-formed since _Duration is not a + // specialization of std::chrono::duration"); + + // The wording uses the constraints like + // constructible_from + // Using these constraints in the code causes the compiler to give an + // error that the constraint depends on itself. To avoid that issue use + // the fact it is possible to create this object from a _TimeZonePtr. + using __traits = zoned_traits<_TimeZonePtr>; + + public: + using duration = std::common_type_t<_Duration, std::chrono::seconds>; + + zoned_time() : __zone_{__traits::default_zone()}, __tp_{} {} + + zoned_time(const zoned_time&) = default; + zoned_time& operator=(const zoned_time&) = default; + + zoned_time(const date::sys_time<_Duration>& __tp) + : __zone_{__traits::default_zone()}, __tp_{__tp} {} + + explicit zoned_time(_TimeZonePtr __zone) + : __zone_{std::move(__zone)}, __tp_{} {} + + explicit zoned_time(std::string_view __name) + : __zone_{__traits::locate_zone(__name)}, __tp_{} {} + + template + zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& __zt) + : __zone_{__zt.get_time_zone()}, __tp_{__zt.get_sys_time()} {} + + zoned_time(_TimeZonePtr __zone, const date::sys_time<_Duration>& __tp) + : __zone_{std::move(__zone)}, __tp_{__tp} {} + + zoned_time(std::string_view __name, const date::sys_time<_Duration>& __tp) + : zoned_time{__traits::locate_zone(__name), __tp} {} + + zoned_time(_TimeZonePtr __zone, const date::local_time<_Duration>& __tp) + : __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp)} {} + + zoned_time(std::string_view __name, const date::local_time<_Duration>& __tp) + : zoned_time{__traits::locate_zone(__name), __tp} {} + + zoned_time( + _TimeZonePtr __zone, + const date::local_time<_Duration>& __tp, + choose __c) + : __zone_{std::move(__zone)}, __tp_{__zone_->to_sys(__tp, __c)} {} + + zoned_time( + std::string_view __name, + const date::local_time<_Duration>& __tp, + choose __c) + : zoned_time{__traits::locate_zone(__name), __tp, __c} {} + + template + zoned_time( + _TimeZonePtr __zone, + const zoned_time<_Duration2, _TimeZonePtr2>& __zt) + : __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {} + + // per wording choose has no effect + template + zoned_time( + _TimeZonePtr __zone, + const zoned_time<_Duration2, _TimeZonePtr2>& __zt, + choose) + : __zone_{std::move(__zone)}, __tp_{__zt.get_sys_time()} {} + + template + zoned_time( + std::string_view __name, + const zoned_time<_Duration2, _TimeZonePtr2>& __zt) + : zoned_time{__traits::locate_zone(__name), __zt} {} + + template + zoned_time( + std::string_view __name, + const zoned_time<_Duration2, _TimeZonePtr2>& __zt, + choose __c) + : zoned_time{__traits::locate_zone(__name), __zt, __c} {} + + zoned_time& operator=(const date::sys_time<_Duration>& __tp) { + __tp_ = __tp; + return *this; + } + + zoned_time& operator=(const date::local_time<_Duration>& __tp) { + // TODO TZDB This seems wrong. + // Assigning a non-existent or ambiguous time will throw and not satisfy + // the post condition. This seems quite odd; I constructed an object with + // choose::earliest and that choice is not respected. + // what did LEWG do with this. + // MSVC STL and libstdc++ behave the same + __tp_ = __zone_->to_sys(__tp); + return *this; + } + + [[nodiscard]] operator date::sys_time() const { + return get_sys_time(); + } + [[nodiscard]] explicit operator date::local_time() const { + return get_local_time(); + } + + [[nodiscard]] _TimeZonePtr get_time_zone() const { + return __zone_; + } + [[nodiscard]] date::local_time get_local_time() const { + return __zone_->to_local(__tp_); + } + [[nodiscard]] date::sys_time get_sys_time() const { + return __tp_; + } + [[nodiscard]] sys_info get_info() const { + return __zone_->get_info(__tp_); + } + + private: + _TimeZonePtr __zone_; + date::sys_time __tp_; +}; + +zoned_time() -> zoned_time; + +template +zoned_time(date::sys_time<_Duration>) + -> zoned_time>; + +template +using __time_zone_representation = std::conditional_t< + std::is_convertible_v<_TimeZonePtrOrName, std::string_view>, + const time_zone*, + folly::remove_cvref_t<_TimeZonePtrOrName>>; + +template +zoned_time(_TimeZonePtrOrName&&) + -> zoned_time< + std::chrono::seconds, + __time_zone_representation<_TimeZonePtrOrName>>; + +template +zoned_time(_TimeZonePtrOrName&&, date::sys_time<_Duration>) + -> zoned_time< + std::common_type_t<_Duration, std::chrono::seconds>, + __time_zone_representation<_TimeZonePtrOrName>>; + +template +zoned_time( + _TimeZonePtrOrName&&, + date::local_time<_Duration>, + choose = choose::earliest) + -> zoned_time< + std::common_type_t<_Duration, std::chrono::seconds>, + __time_zone_representation<_TimeZonePtrOrName>>; + +template +zoned_time( + _TimeZonePtrOrName&&, + zoned_time<_Duration, _TimeZonePtr2>, + choose = choose::earliest) + -> zoned_time< + std::common_type_t<_Duration, std::chrono::seconds>, + __time_zone_representation<_TimeZonePtrOrName>>; + +using zoned_seconds = zoned_time; + +template +bool operator==( + const zoned_time<_Duration1, _TimeZonePtr>& __lhs, + const zoned_time<_Duration2, _TimeZonePtr>& __rhs) { + return __lhs.get_time_zone() == __rhs.get_time_zone() && + __lhs.get_sys_time() == __rhs.get_sys_time(); +} + +template +std::basic_ostream& to_stream( + std::basic_ostream& os, + const CharT* fmt, + const zoned_time& tp) { + using duration = typename zoned_time::duration; + using LT = date::local_time; + auto const st = tp.get_sys_time(); + auto const info = tp.get_time_zone()->get_info(st); + return to_stream( + os, + fmt, + LT{(st + info.offset).time_since_epoch()}, + &info.abbrev, + &info.offset); +} + +} // namespace facebook::velox::tzdb diff --git a/velox/functions/lib/DateTimeFormatter.cpp b/velox/functions/lib/DateTimeFormatter.cpp index 8971873b42cb..8542e3c216e7 100644 --- a/velox/functions/lib/DateTimeFormatter.cpp +++ b/velox/functions/lib/DateTimeFormatter.cpp @@ -18,11 +18,10 @@ #include #include #include -#include #include "velox/common/base/CountBits.h" #include "velox/external/date/date.h" #include "velox/external/date/iso_week.h" -#include "velox/external/date/tz.h" +#include "velox/external/tzdb/tzdb_list.h" #include "velox/functions/lib/DateTimeFormatterBuilder.h" #include "velox/type/TimestampConversion.h" #include "velox/type/tz/TimeZoneMap.h" @@ -373,11 +372,15 @@ struct TimeZoneNameMappings { }; TimeZoneNameMappings getTimeZoneNameMappings() { - // Here we use get_time_zone_names instead of calling get_tzdb and - // constructing the list ourselves because there is some unknown issue with - // the tz library where the time_zone objects after the first one in the tzdb - // will be invalid (contain nullptrs) after the get_tzdb function returns. - const std::vector timeZoneNames = date::get_time_zone_names(); + std::vector timeZoneNames; + const tzdb::tzdb& tzdb = tzdb::get_tzdb(); + timeZoneNames.reserve(tzdb.zones.size() + tzdb.links.size()); + for (const auto& zone : tzdb.zones) { + timeZoneNames.emplace_back(zone.name()); + } + for (const auto& link : tzdb.links) { + timeZoneNames.emplace_back(link.name()); + } TimeZoneNameMappings result; for (size_t i = 0; i < timeZoneNames.size(); i++) { diff --git a/velox/functions/lib/JsonArrayLength.h b/velox/functions/lib/JsonArrayLength.h deleted file mode 100644 index 898c14bd416a..000000000000 --- a/velox/functions/lib/JsonArrayLength.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * 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. - */ - -#pragma once - -#include "velox/functions/Macros.h" -#include "velox/functions/prestosql/json/JsonPathTokenizer.h" -#include "velox/functions/prestosql/json/SIMDJsonExtractor.h" -#include "velox/functions/prestosql/json/SIMDJsonWrapper.h" -#include "velox/functions/prestosql/types/JsonType.h" - -namespace facebook::velox::functions { - -/// json_array_length(jsonString) -> length -/// -/// Returns the number of elements in the outermost JSON array from jsonString. -/// If jsonString is not a valid JSON array or NULL, the function returns null. -/// Presto: -/// https://prestodb.io/docs/current/functions/json.html#json_array_length-json-bigint -/// SparkSQL: -/// https://spark.apache.org/docs/latest/api/sql/index.html#json_array_length -template -struct JsonArrayLengthFunction { - VELOX_DEFINE_FUNCTION_TYPES(T); - - template - FOLLY_ALWAYS_INLINE bool call(TOutput& len, const arg_type& json) { - simdjson::ondemand::document jsonDoc; - - simdjson::padded_string paddedJson(json.data(), json.size()); - if (simdjsonParse(paddedJson).get(jsonDoc)) { - return false; - } - if (jsonDoc.type().error()) { - return false; - } - - if (jsonDoc.type() != simdjson::ondemand::json_type::array) { - return false; - } - - size_t numElements; - if (jsonDoc.count_elements().get(numElements)) { - return false; - } - - VELOX_USER_CHECK_LE( - numElements, - std::numeric_limits::max(), - "The json array length {} is bigger than the max value of output type {}.", - numElements, - std::numeric_limits::max()); - - len = numElements; - return true; - } -}; -} // namespace facebook::velox::functions diff --git a/velox/functions/prestosql/JsonFunctions.h b/velox/functions/prestosql/JsonFunctions.h index 2ea18a56cc57..be951943d798 100644 --- a/velox/functions/prestosql/JsonFunctions.h +++ b/velox/functions/prestosql/JsonFunctions.h @@ -379,4 +379,49 @@ struct JsonSizeFunction { } }; +/// json_array_length(jsonString) -> length +/// +/// Returns the number of elements in the outermost JSON array from jsonString. +/// If jsonString is not a valid JSON array or NULL, the function returns null. +/// Presto: +/// https://prestodb.io/docs/current/functions/json.html#json_array_length-json-bigint +/// SparkSQL: +/// https://spark.apache.org/docs/latest/api/sql/index.html#json_array_length +template +struct JsonArrayLengthFunction { + VELOX_DEFINE_FUNCTION_TYPES(T); + + template + FOLLY_ALWAYS_INLINE bool call(TOutput& len, const arg_type& json) { + simdjson::ondemand::document jsonDoc; + + simdjson::padded_string paddedJson(json.data(), json.size()); + if (simdjsonParse(paddedJson).get(jsonDoc)) { + return false; + } + if (jsonDoc.type().error()) { + return false; + } + + if (jsonDoc.type() != simdjson::ondemand::json_type::array) { + return false; + } + + size_t numElements; + if (jsonDoc.count_elements().get(numElements)) { + return false; + } + + VELOX_USER_CHECK_LE( + numElements, + std::numeric_limits::max(), + "The json array length {} is bigger than the max value of output type {}.", + numElements, + std::numeric_limits::max()); + + len = numElements; + return true; + } +}; + } // namespace facebook::velox::functions diff --git a/velox/functions/prestosql/benchmarks/JsonExprBenchmark.cpp b/velox/functions/prestosql/benchmarks/JsonExprBenchmark.cpp index 9dfc8de8ce15..f05b2456a099 100644 --- a/velox/functions/prestosql/benchmarks/JsonExprBenchmark.cpp +++ b/velox/functions/prestosql/benchmarks/JsonExprBenchmark.cpp @@ -20,7 +20,6 @@ #include #include #include "velox/functions/Registerer.h" -#include "velox/functions/lib/JsonArrayLength.h" #include "velox/functions/lib/benchmarks/FunctionBenchmarkBase.h" #include "velox/functions/prestosql/JsonFunctions.h" #include "velox/functions/prestosql/json/JsonExtractor.h" diff --git a/velox/functions/prestosql/registration/JsonFunctionsRegistration.cpp b/velox/functions/prestosql/registration/JsonFunctionsRegistration.cpp index faf439658d03..23ba1b1d195f 100644 --- a/velox/functions/prestosql/registration/JsonFunctionsRegistration.cpp +++ b/velox/functions/prestosql/registration/JsonFunctionsRegistration.cpp @@ -15,7 +15,6 @@ */ #include "velox/functions/Registerer.h" -#include "velox/functions/lib/JsonArrayLength.h" #include "velox/functions/prestosql/JsonFunctions.h" #include "velox/functions/prestosql/types/JsonRegistration.h" diff --git a/velox/functions/prestosql/tests/DateTimeFunctionsTest.cpp b/velox/functions/prestosql/tests/DateTimeFunctionsTest.cpp index 0fb850b3b8b2..54022fe586ac 100644 --- a/velox/functions/prestosql/tests/DateTimeFunctionsTest.cpp +++ b/velox/functions/prestosql/tests/DateTimeFunctionsTest.cpp @@ -18,7 +18,7 @@ #include #include "velox/common/base/tests/GTestUtils.h" -#include "velox/external/date/tz.h" +#include "velox/external/tzdb/zoned_time.h" #include "velox/functions/prestosql/tests/utils/FunctionBaseTest.h" #include "velox/functions/prestosql/types/TimestampWithTimeZoneType.h" #include "velox/type/tz/TimeZoneMap.h" @@ -150,9 +150,9 @@ class DateTimeFunctionsTest : public functions::test::FunctionBaseTest { return parseDate(date::format( "%Y-%m-%d", timeZone.has_value() - ? date::make_zoned( + ? tzdb::zoned_time( timeZone.value(), std::chrono::system_clock::now()) - : std::chrono::system_clock::now())); + : tzdb::zoned_time(std::chrono::system_clock::now()))); } }; @@ -4766,18 +4766,9 @@ TEST_F(DateTimeFunctionsTest, castDateForDateFunction) { castDateTest(Timestamp( -18297 * kSecondsInDay + kSecondsInDay - 1, kNanosInSecond - 1))); - // Trying to convert a very large timestamp should fail as velox/external/date - // can't convert past year 2037. Note that the correct result here should be - // 376358 ('3000-06-08'), and not 376357 ('3000-06-07'). - VELOX_ASSERT_THROW( - castDateTest(Timestamp(32517359891, 0)), "Unable to convert timezone"); - - // Ensure timezone conversion failures leak through try(). - const auto tryTest = [&](std::optional timestamp) { - return evaluateOnce("try(cast(c0 as date))", timestamp); - }; - VELOX_ASSERT_RUNTIME_THROW( - tryTest(Timestamp(32517359891, 0)), "Unable to convert timezone"); + // Timestamps in the distant future in different DST time zones. + EXPECT_EQ(376358, castDateTest(Timestamp(32517359891, 0))); + EXPECT_EQ(376231, castDateTest(Timestamp(32506387200, 0))); } TEST_F(DateTimeFunctionsTest, currentDateWithTimezone) { diff --git a/velox/functions/sparksql/registration/RegisterJson.cpp b/velox/functions/sparksql/registration/RegisterJson.cpp index 340cb8a86eb7..93df64818d3d 100644 --- a/velox/functions/sparksql/registration/RegisterJson.cpp +++ b/velox/functions/sparksql/registration/RegisterJson.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "velox/functions/lib/JsonArrayLength.h" #include "velox/functions/lib/RegistrationHelpers.h" +#include "velox/functions/prestosql/JsonFunctions.h" #include "velox/functions/sparksql/GetJsonObject.h" #include "velox/functions/sparksql/JsonObjectKeys.h" diff --git a/velox/type/Timestamp.cpp b/velox/type/Timestamp.cpp index ef928791c5d4..e159596cab99 100644 --- a/velox/type/Timestamp.cpp +++ b/velox/type/Timestamp.cpp @@ -17,7 +17,7 @@ #include #include #include "velox/common/base/CountBits.h" -#include "velox/external/date/tz.h" +#include "velox/external/tzdb/exception.h" #include "velox/type/tz/TimeZoneMap.h" namespace facebook::velox { @@ -54,12 +54,12 @@ void Timestamp::toGMT(const tz::TimeZone& zone) { try { sysSeconds = zone.to_sys(std::chrono::seconds(seconds_)); - } catch (const date::ambiguous_local_time&) { + } catch (const tzdb::ambiguous_local_time&) { // If the time is ambiguous, pick the earlier possibility to be consistent // with Presto. sysSeconds = zone.to_sys( std::chrono::seconds(seconds_), tz::TimeZone::TChoose::kEarliest); - } catch (const date::nonexistent_local_time& error) { + } catch (const tzdb::nonexistent_local_time& error) { // If the time does not exist, fail the conversion. VELOX_USER_FAIL(error.what()); } catch (const std::invalid_argument& e) { @@ -95,7 +95,7 @@ const tz::TimeZone& Timestamp::defaultTimezone() { static const tz::TimeZone* kDefault = ({ // TODO: We are hard-coding PST/PDT here to be aligned with the current // behavior in DWRF reader/writer. Once they are fixed, we can use - // date::current_zone() here. + // tzdb::current_zone() here. // // See https://github.com/facebookincubator/velox/issues/8127 auto* tz = tz::locateZone("America/Los_Angeles"); diff --git a/velox/type/tests/TimestampTest.cpp b/velox/type/tests/TimestampTest.cpp index 4f76bc74a0c6..b5f93c2a4816 100644 --- a/velox/type/tests/TimestampTest.cpp +++ b/velox/type/tests/TimestampTest.cpp @@ -383,9 +383,7 @@ TEST(TimestampTest, decreaseOperator) { } TEST(TimestampTest, outOfRange) { - // There are two ranges for timezone conversion. - // - // #1. external/date cannot handle years larger than 32k (date::year::max()). + // external/date cannot handle years larger than 32k (date::year::max()). // Any conversions exceeding that threshold will fail right away. auto* timezone = tz::locateZone("GMT"); Timestamp t1(-3217830796800, 0); @@ -396,16 +394,6 @@ TEST(TimestampTest, outOfRange) { timezone = tz::locateZone("America/Los_Angeles"); VELOX_ASSERT_THROW(t1.toGMT(*timezone), expected); - - // #2. external/date doesn't understand OS_TZDB repetition rules. Therefore, - // for timezones with pre-defined repetition rules for daylight savings, for - // example, it will throw for anything larger than 2037 (which is what is - // currently materialized in OS_TZDBs). America/Los_Angeles is an example of - // such timezone. - Timestamp t2(32517359891, 0); - VELOX_ASSERT_THROW( - t2.toTimezone(*timezone), - "Unable to convert timezone 'America/Los_Angeles' past"); } // In debug mode, Timestamp constructor will throw exception if range check diff --git a/velox/type/tz/CMakeLists.txt b/velox/type/tz/CMakeLists.txt index 2ca06d9543c9..9f474a9cddc3 100644 --- a/velox/type/tz/CMakeLists.txt +++ b/velox/type/tz/CMakeLists.txt @@ -26,7 +26,7 @@ velox_add_library( velox_link_libraries( velox_type_tz velox_exception - velox_external_date + velox_external_tzdb Boost::regex fmt::fmt Folly::folly) diff --git a/velox/type/tz/TimeZoneMap.cpp b/velox/type/tz/TimeZoneMap.cpp index e7935a308310..dfdf24464b54 100644 --- a/velox/type/tz/TimeZoneMap.cpp +++ b/velox/type/tz/TimeZoneMap.cpp @@ -23,7 +23,8 @@ #include "velox/common/base/Exceptions.h" #include "velox/common/testutil/TestValue.h" -#include "velox/external/date/tz.h" +#include "velox/external/tzdb/tzdb_list.h" +#include "velox/external/tzdb/zoned_time.h" #include "velox/type/tz/TimeZoneNames.h" using facebook::velox::common::testutil::TestValue; @@ -44,9 +45,9 @@ inline std::chrono::minutes getTimeZoneOffset(int16_t tzID) { return std::chrono::minutes{(tzID <= 840) ? (tzID - 841) : (tzID - 840)}; } -const date::time_zone* locateZoneImpl(std::string_view tz_name) { +const tzdb::time_zone* locateZoneImpl(std::string_view tz_name) { TestValue::adjust("facebook::velox::tz::locateZoneImpl", &tz_name); - const date::time_zone* zone = date::locate_zone(tz_name); + const tzdb::time_zone* zone = tzdb::locate_zone(tz_name); return zone; } @@ -73,10 +74,10 @@ TTimeZoneDatabase buildTimeZoneDatabase( // Every single other time zone entry (outside of offsets) needs to be // available in external/date or this will throw. else { - const date::time_zone* zone; + const tzdb::time_zone* zone; try { zone = locateZoneImpl(entry.second); - } catch (date::invalid_timezone& err) { + } catch (tzdb::invalid_time_zone& err) { // When this exception is thrown, it typically means the time zone name // we are trying to locate cannot be found from OS's time zone database. // Thus, we just command a "continue;" to skip the creation of the @@ -240,8 +241,8 @@ std::string normalizeTimeZone(const std::string& originalZoneId) { template void validateRangeImpl(time_point timePoint) { using namespace velox::date; - static constexpr auto kMinYear = date::year::min(); - static constexpr auto kMaxYear = date::year::max(); + static constexpr auto kMinYear = year::min(); + static constexpr auto kMaxYear = year::max(); auto year = year_month_day(floor(timePoint)).year(); @@ -257,26 +258,26 @@ void validateRangeImpl(time_point timePoint) { } template -date::zoned_time getZonedTime( - const date::time_zone* tz, +tzdb::zoned_time getZonedTime( + const tzdb::time_zone* tz, date::local_time timestamp, TimeZone::TChoose choose) { if (choose == TimeZone::TChoose::kFail) { // By default, throws. - return date::zoned_time{tz, timestamp}; + return tzdb::zoned_time{tz, timestamp}; } auto dateChoose = (choose == TimeZone::TChoose::kEarliest) - ? date::choose::earliest - : date::choose::latest; - return date::zoned_time{tz, timestamp, dateChoose}; + ? tzdb::choose::earliest + : tzdb::choose::latest; + return tzdb::zoned_time{tz, timestamp, dateChoose}; } template TDuration toSysImpl( const TDuration& timestamp, const TimeZone::TChoose choose, - const date::time_zone* tz, + const tzdb::time_zone* tz, const std::chrono::minutes offset) { date::local_time timePoint{timestamp}; validateRange(date::sys_time{timestamp}); @@ -292,7 +293,7 @@ TDuration toSysImpl( template TDuration toLocalImpl( const TDuration& timestamp, - const date::time_zone* tz, + const tzdb::time_zone* tz, const std::chrono::minutes offset) { date::sys_time timePoint{timestamp}; validateRange(timePoint); @@ -301,14 +302,14 @@ TDuration toLocalImpl( if (tz == nullptr) { return (timePoint + offset).time_since_epoch(); } - return date::zoned_time{tz, timePoint}.get_local_time().time_since_epoch(); + return tzdb::zoned_time{tz, timePoint}.get_local_time().time_since_epoch(); } template std::string getName( TimeZone::milliseconds timestamp, TimeZone::TChoose choose, - const date::time_zone* tz, + const tzdb::time_zone* tz, const std::string& timeZoneName) { validateRange(date::sys_time(timestamp)); @@ -454,7 +455,7 @@ TimeZone::seconds TimeZone::correct_nonexistent_time( const auto localInfo = tz_->get_info(date::local_time{timestamp}); - if (localInfo.result != date::local_info::nonexistent) { + if (localInfo.result != tzdb::local_info::nonexistent) { return timestamp; } diff --git a/velox/type/tz/TimeZoneMap.h b/velox/type/tz/TimeZoneMap.h index 627fdc133355..ea760c05677f 100644 --- a/velox/type/tz/TimeZoneMap.h +++ b/velox/type/tz/TimeZoneMap.h @@ -19,7 +19,7 @@ #include #include -namespace facebook::velox::date { +namespace facebook::velox::tzdb { class time_zone; } @@ -28,13 +28,13 @@ namespace facebook::velox::tz { /// This library provides time zone management primitives. It maintains an /// internal static database which is contructed lazily based on the first /// access, based on TimeZoneDatabase.cpp and the local tzdata installed in your -/// system (through velox/external/date). +/// system (through velox/external/tzdata). /// /// It provides functions for one to lookup TimeZone pointers based on time zone /// name or ID, and to performance timestamp conversion across time zones. /// /// This library provides a layer of functionality on top of -/// velox/external/date, so do not use the external library directly for +/// velox/external/tzdata, so do not use the external library directly for /// time zone routines. class TimeZone; @@ -88,7 +88,7 @@ class TimeZone { TimeZone( std::string_view timeZoneName, int16_t timeZoneID, - const date::time_zone* tz) + const tzdb::time_zone* tz) : tz_(tz), offset_(0), timeZoneName_(timeZoneName), @@ -121,8 +121,8 @@ class TimeZone { /// /// Conversions from local time to GMT are non-linear and may be ambiguous /// during day light savings transitions, or non existent. By default (kFail), - /// `to_sys()` will throw `date::ambiguous_local_time` and - /// `date::nonexistent_local_time` in these cases. + /// `to_sys()` will throw `tzdb::ambiguous_local_time` and + /// `tzdb::nonexistent_local_time` in these cases. /// /// You can overwrite the behavior in ambiguous conversions by setting the /// TChoose flag, but it will still throws in case of nonexistent conversions. @@ -159,7 +159,7 @@ class TimeZone { return timeZoneID_; } - const date::time_zone* tz() const { + const tzdb::time_zone* tz() const { return tz_; } @@ -180,7 +180,7 @@ class TimeZone { TChoose choose = TChoose::kFail) const; private: - const date::time_zone* tz_{nullptr}; + const tzdb::time_zone* tz_{nullptr}; const std::chrono::minutes offset_{0}; const std::string timeZoneName_; const int16_t timeZoneID_; diff --git a/velox/type/tz/tests/TimeZoneMapExternalInvalidTest.cpp b/velox/type/tz/tests/TimeZoneMapExternalInvalidTest.cpp index b5668f929d72..c18512921e1f 100644 --- a/velox/type/tz/tests/TimeZoneMapExternalInvalidTest.cpp +++ b/velox/type/tz/tests/TimeZoneMapExternalInvalidTest.cpp @@ -16,10 +16,9 @@ #include -#include "velox/common/base/Exceptions.h" #include "velox/common/base/tests/GTestUtils.h" #include "velox/common/testutil/TestValue.h" -#include "velox/external/date/tz.h" +#include "velox/external/tzdb/exception.h" #include "velox/type/tz/TimeZoneMap.h" namespace facebook::velox::tz { @@ -36,10 +35,10 @@ DEBUG_ONLY_TEST(TimeZoneMapExternalInvalidTest, externalInvalid) { std::function( [&](std::string_view* tz_name) -> void { if (*tz_name == testZone) { - // Emulates an invalid_timezone error thrown from external API - // date::locate_zone. In real scenarios, this could happen when + // Emulates an invalid_time_zone error thrown from external API + // tzdb::locate_zone. In real scenarios, this could happen when // the timezone is not found in operating system's timezone list. - throw date::invalid_timezone(std::string(*tz_name)); + throw tzdb::invalid_time_zone(std::string(*tz_name)); } }));