Add 'third_party/abseil_cpp/' from commit '768eb2ca28'
git-subtree-dir: third_party/abseil_cpp git-subtree-mainline:ffb2ae54begit-subtree-split:768eb2ca28
This commit is contained in:
commit
fc8dc48020
1276 changed files with 208196 additions and 0 deletions
124
third_party/abseil_cpp/absl/time/BUILD.bazel
vendored
Normal file
124
third_party/abseil_cpp/absl/time/BUILD.bazel
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#
|
||||
# Copyright 2017 The Abseil Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
|
||||
load(
|
||||
"//absl:copts/configure_copts.bzl",
|
||||
"ABSL_DEFAULT_COPTS",
|
||||
"ABSL_DEFAULT_LINKOPTS",
|
||||
"ABSL_TEST_COPTS",
|
||||
)
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
cc_library(
|
||||
name = "time",
|
||||
srcs = [
|
||||
"civil_time.cc",
|
||||
"clock.cc",
|
||||
"duration.cc",
|
||||
"format.cc",
|
||||
"internal/get_current_time_chrono.inc",
|
||||
"internal/get_current_time_posix.inc",
|
||||
"time.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"civil_time.h",
|
||||
"clock.h",
|
||||
"time.h",
|
||||
],
|
||||
copts = ABSL_DEFAULT_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
deps = [
|
||||
"//absl/base",
|
||||
"//absl/base:core_headers",
|
||||
"//absl/base:raw_logging_internal",
|
||||
"//absl/numeric:int128",
|
||||
"//absl/strings",
|
||||
"//absl/time/internal/cctz:civil_time",
|
||||
"//absl/time/internal/cctz:time_zone",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "test_util",
|
||||
testonly = 1,
|
||||
srcs = [
|
||||
"internal/test_util.cc",
|
||||
"internal/zoneinfo.inc",
|
||||
],
|
||||
hdrs = ["internal/test_util.h"],
|
||||
copts = ABSL_DEFAULT_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
visibility = [
|
||||
"//absl/time:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
":time",
|
||||
"//absl/base:raw_logging_internal",
|
||||
"//absl/time/internal/cctz:time_zone",
|
||||
"@com_google_googletest//:gtest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "time_test",
|
||||
srcs = [
|
||||
"civil_time_test.cc",
|
||||
"clock_test.cc",
|
||||
"duration_test.cc",
|
||||
"format_test.cc",
|
||||
"time_test.cc",
|
||||
"time_zone_test.cc",
|
||||
],
|
||||
copts = ABSL_TEST_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
deps = [
|
||||
":test_util",
|
||||
":time",
|
||||
"//absl/base:config",
|
||||
"//absl/base:core_headers",
|
||||
"//absl/numeric:int128",
|
||||
"//absl/time/internal/cctz:time_zone",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "time_benchmark",
|
||||
srcs = [
|
||||
"civil_time_benchmark.cc",
|
||||
"clock_benchmark.cc",
|
||||
"duration_benchmark.cc",
|
||||
"format_benchmark.cc",
|
||||
"time_benchmark.cc",
|
||||
],
|
||||
copts = ABSL_TEST_COPTS,
|
||||
linkopts = ABSL_DEFAULT_LINKOPTS,
|
||||
tags = [
|
||||
"benchmark",
|
||||
],
|
||||
deps = [
|
||||
":test_util",
|
||||
":time",
|
||||
"//absl/base",
|
||||
"//absl/base:core_headers",
|
||||
"//absl/hash",
|
||||
"@com_github_google_benchmark//:benchmark_main",
|
||||
],
|
||||
)
|
||||
127
third_party/abseil_cpp/absl/time/CMakeLists.txt
vendored
Normal file
127
third_party/abseil_cpp/absl/time/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#
|
||||
# Copyright 2017 The Abseil Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
time
|
||||
HDRS
|
||||
"civil_time.h"
|
||||
"clock.h"
|
||||
"time.h"
|
||||
SRCS
|
||||
"civil_time.cc"
|
||||
"clock.cc"
|
||||
"duration.cc"
|
||||
"format.cc"
|
||||
"internal/get_current_time_chrono.inc"
|
||||
"internal/get_current_time_posix.inc"
|
||||
"time.cc"
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
DEPS
|
||||
absl::base
|
||||
absl::civil_time
|
||||
absl::core_headers
|
||||
absl::int128
|
||||
absl::raw_logging_internal
|
||||
absl::strings
|
||||
absl::time_zone
|
||||
PUBLIC
|
||||
)
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
civil_time
|
||||
HDRS
|
||||
"internal/cctz/include/cctz/civil_time.h"
|
||||
"internal/cctz/include/cctz/civil_time_detail.h"
|
||||
SRCS
|
||||
"internal/cctz/src/civil_time_detail.cc"
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
find_library(CoreFoundation CoreFoundation)
|
||||
endif()
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
time_zone
|
||||
HDRS
|
||||
"internal/cctz/include/cctz/time_zone.h"
|
||||
"internal/cctz/include/cctz/zone_info_source.h"
|
||||
SRCS
|
||||
"internal/cctz/src/time_zone_fixed.cc"
|
||||
"internal/cctz/src/time_zone_fixed.h"
|
||||
"internal/cctz/src/time_zone_format.cc"
|
||||
"internal/cctz/src/time_zone_if.cc"
|
||||
"internal/cctz/src/time_zone_if.h"
|
||||
"internal/cctz/src/time_zone_impl.cc"
|
||||
"internal/cctz/src/time_zone_impl.h"
|
||||
"internal/cctz/src/time_zone_info.cc"
|
||||
"internal/cctz/src/time_zone_info.h"
|
||||
"internal/cctz/src/time_zone_libc.cc"
|
||||
"internal/cctz/src/time_zone_libc.h"
|
||||
"internal/cctz/src/time_zone_lookup.cc"
|
||||
"internal/cctz/src/time_zone_posix.cc"
|
||||
"internal/cctz/src/time_zone_posix.h"
|
||||
"internal/cctz/src/tzfile.h"
|
||||
"internal/cctz/src/zone_info_source.cc"
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
DEPS
|
||||
$<$<PLATFORM_ID:Darwin>:${CoreFoundation}>
|
||||
)
|
||||
|
||||
absl_cc_library(
|
||||
NAME
|
||||
time_internal_test_util
|
||||
HDRS
|
||||
"internal/test_util.h"
|
||||
SRCS
|
||||
"internal/test_util.cc"
|
||||
"internal/zoneinfo.inc"
|
||||
COPTS
|
||||
${ABSL_DEFAULT_COPTS}
|
||||
DEPS
|
||||
absl::time
|
||||
absl::raw_logging_internal
|
||||
absl::time_zone
|
||||
gmock
|
||||
TESTONLY
|
||||
)
|
||||
|
||||
absl_cc_test(
|
||||
NAME
|
||||
time_test
|
||||
SRCS
|
||||
"civil_time_test.cc"
|
||||
"clock_test.cc"
|
||||
"duration_test.cc"
|
||||
"format_test.cc"
|
||||
"time_test.cc"
|
||||
"time_zone_test.cc"
|
||||
COPTS
|
||||
${ABSL_TEST_COPTS}
|
||||
DEPS
|
||||
absl::time_internal_test_util
|
||||
absl::time
|
||||
absl::config
|
||||
absl::core_headers
|
||||
absl::time_zone
|
||||
gmock_main
|
||||
)
|
||||
175
third_party/abseil_cpp/absl/time/civil_time.cc
vendored
Normal file
175
third_party/abseil_cpp/absl/time/civil_time.cc
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/time/civil_time.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
// Since a civil time has a larger year range than absl::Time (64-bit years vs
|
||||
// 64-bit seconds, respectively) we normalize years to roughly +/- 400 years
|
||||
// around the year 2400, which will produce an equivalent year in a range that
|
||||
// absl::Time can handle.
|
||||
inline civil_year_t NormalizeYear(civil_year_t year) {
|
||||
return 2400 + year % 400;
|
||||
}
|
||||
|
||||
// Formats the given CivilSecond according to the given format.
|
||||
std::string FormatYearAnd(string_view fmt, CivilSecond cs) {
|
||||
const CivilSecond ncs(NormalizeYear(cs.year()), cs.month(), cs.day(),
|
||||
cs.hour(), cs.minute(), cs.second());
|
||||
const TimeZone utc = UTCTimeZone();
|
||||
// TODO(absl-team): Avoid conversion of fmt string.
|
||||
return StrCat(cs.year(),
|
||||
FormatTime(std::string(fmt), FromCivil(ncs, utc), utc));
|
||||
}
|
||||
|
||||
template <typename CivilT>
|
||||
bool ParseYearAnd(string_view fmt, string_view s, CivilT* c) {
|
||||
// Civil times support a larger year range than absl::Time, so we need to
|
||||
// parse the year separately, normalize it, then use absl::ParseTime on the
|
||||
// normalized string.
|
||||
const std::string ss = std::string(s); // TODO(absl-team): Avoid conversion.
|
||||
const char* const np = ss.c_str();
|
||||
char* endp;
|
||||
errno = 0;
|
||||
const civil_year_t y =
|
||||
std::strtoll(np, &endp, 10); // NOLINT(runtime/deprecated_fn)
|
||||
if (endp == np || errno == ERANGE) return false;
|
||||
const std::string norm = StrCat(NormalizeYear(y), endp);
|
||||
|
||||
const TimeZone utc = UTCTimeZone();
|
||||
Time t;
|
||||
if (ParseTime(StrCat("%Y", fmt), norm, utc, &t, nullptr)) {
|
||||
const auto cs = ToCivilSecond(t, utc);
|
||||
*c = CivilT(y, cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tries to parse the type as a CivilT1, but then assigns the result to the
|
||||
// argument of type CivilT2.
|
||||
template <typename CivilT1, typename CivilT2>
|
||||
bool ParseAs(string_view s, CivilT2* c) {
|
||||
CivilT1 t1;
|
||||
if (ParseCivilTime(s, &t1)) {
|
||||
*c = CivilT2(t1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename CivilT>
|
||||
bool ParseLenient(string_view s, CivilT* c) {
|
||||
// A fastpath for when the given string data parses exactly into the given
|
||||
// type T (e.g., s="YYYY-MM-DD" and CivilT=CivilDay).
|
||||
if (ParseCivilTime(s, c)) return true;
|
||||
// Try parsing as each of the 6 types, trying the most common types first
|
||||
// (based on csearch results).
|
||||
if (ParseAs<CivilDay>(s, c)) return true;
|
||||
if (ParseAs<CivilSecond>(s, c)) return true;
|
||||
if (ParseAs<CivilHour>(s, c)) return true;
|
||||
if (ParseAs<CivilMonth>(s, c)) return true;
|
||||
if (ParseAs<CivilMinute>(s, c)) return true;
|
||||
if (ParseAs<CivilYear>(s, c)) return true;
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string FormatCivilTime(CivilSecond c) {
|
||||
return FormatYearAnd("-%m-%dT%H:%M:%S", c);
|
||||
}
|
||||
std::string FormatCivilTime(CivilMinute c) {
|
||||
return FormatYearAnd("-%m-%dT%H:%M", c);
|
||||
}
|
||||
std::string FormatCivilTime(CivilHour c) {
|
||||
return FormatYearAnd("-%m-%dT%H", c);
|
||||
}
|
||||
std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); }
|
||||
std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); }
|
||||
std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); }
|
||||
|
||||
bool ParseCivilTime(string_view s, CivilSecond* c) {
|
||||
return ParseYearAnd("-%m-%dT%H:%M:%S", s, c);
|
||||
}
|
||||
bool ParseCivilTime(string_view s, CivilMinute* c) {
|
||||
return ParseYearAnd("-%m-%dT%H:%M", s, c);
|
||||
}
|
||||
bool ParseCivilTime(string_view s, CivilHour* c) {
|
||||
return ParseYearAnd("-%m-%dT%H", s, c);
|
||||
}
|
||||
bool ParseCivilTime(string_view s, CivilDay* c) {
|
||||
return ParseYearAnd("-%m-%d", s, c);
|
||||
}
|
||||
bool ParseCivilTime(string_view s, CivilMonth* c) {
|
||||
return ParseYearAnd("-%m", s, c);
|
||||
}
|
||||
bool ParseCivilTime(string_view s, CivilYear* c) {
|
||||
return ParseYearAnd("", s, c);
|
||||
}
|
||||
|
||||
bool ParseLenientCivilTime(string_view s, CivilSecond* c) {
|
||||
return ParseLenient(s, c);
|
||||
}
|
||||
bool ParseLenientCivilTime(string_view s, CivilMinute* c) {
|
||||
return ParseLenient(s, c);
|
||||
}
|
||||
bool ParseLenientCivilTime(string_view s, CivilHour* c) {
|
||||
return ParseLenient(s, c);
|
||||
}
|
||||
bool ParseLenientCivilTime(string_view s, CivilDay* c) {
|
||||
return ParseLenient(s, c);
|
||||
}
|
||||
bool ParseLenientCivilTime(string_view s, CivilMonth* c) {
|
||||
return ParseLenient(s, c);
|
||||
}
|
||||
bool ParseLenientCivilTime(string_view s, CivilYear* c) {
|
||||
return ParseLenient(s, c);
|
||||
}
|
||||
|
||||
namespace time_internal {
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, CivilYear y) {
|
||||
return os << FormatCivilTime(y);
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, CivilMonth m) {
|
||||
return os << FormatCivilTime(m);
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, CivilDay d) {
|
||||
return os << FormatCivilTime(d);
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, CivilHour h) {
|
||||
return os << FormatCivilTime(h);
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, CivilMinute m) {
|
||||
return os << FormatCivilTime(m);
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, CivilSecond s) {
|
||||
return os << FormatCivilTime(s);
|
||||
}
|
||||
|
||||
} // namespace time_internal
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
538
third_party/abseil_cpp/absl/time/civil_time.h
vendored
Normal file
538
third_party/abseil_cpp/absl/time/civil_time.h
vendored
Normal file
|
|
@ -0,0 +1,538 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: civil_time.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header file defines abstractions for computing with "civil time".
|
||||
// The term "civil time" refers to the legally recognized human-scale time
|
||||
// that is represented by the six fields `YYYY-MM-DD hh:mm:ss`. A "date"
|
||||
// is perhaps the most common example of a civil time (represented here as
|
||||
// an `absl::CivilDay`).
|
||||
//
|
||||
// Modern-day civil time follows the Gregorian Calendar and is a
|
||||
// time-zone-independent concept: a civil time of "2015-06-01 12:00:00", for
|
||||
// example, is not tied to a time zone. Put another way, a civil time does not
|
||||
// map to a unique point in time; a civil time must be mapped to an absolute
|
||||
// time *through* a time zone.
|
||||
//
|
||||
// Because a civil time is what most people think of as "time," it is common to
|
||||
// map absolute times to civil times to present to users.
|
||||
//
|
||||
// Time zones define the relationship between absolute and civil times. Given an
|
||||
// absolute or civil time and a time zone, you can compute the other time:
|
||||
//
|
||||
// Civil Time = F(Absolute Time, Time Zone)
|
||||
// Absolute Time = G(Civil Time, Time Zone)
|
||||
//
|
||||
// The Abseil time library allows you to construct such civil times from
|
||||
// absolute times; consult time.h for such functionality.
|
||||
//
|
||||
// This library provides six classes for constructing civil-time objects, and
|
||||
// provides several helper functions for rounding, iterating, and performing
|
||||
// arithmetic on civil-time objects, while avoiding complications like
|
||||
// daylight-saving time (DST):
|
||||
//
|
||||
// * `absl::CivilSecond`
|
||||
// * `absl::CivilMinute`
|
||||
// * `absl::CivilHour`
|
||||
// * `absl::CivilDay`
|
||||
// * `absl::CivilMonth`
|
||||
// * `absl::CivilYear`
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Construct a civil-time object for a specific day
|
||||
// const absl::CivilDay cd(1969, 07, 20);
|
||||
//
|
||||
// // Construct a civil-time object for a specific second
|
||||
// const absl::CivilSecond cd(2018, 8, 1, 12, 0, 1);
|
||||
//
|
||||
// Note: In C++14 and later, this library is usable in a constexpr context.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// // Valid in C++14
|
||||
// constexpr absl::CivilDay cd(1969, 07, 20);
|
||||
|
||||
#ifndef ABSL_TIME_CIVIL_TIME_H_
|
||||
#define ABSL_TIME_CIVIL_TIME_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
namespace time_internal {
|
||||
struct second_tag : cctz::detail::second_tag {};
|
||||
struct minute_tag : second_tag, cctz::detail::minute_tag {};
|
||||
struct hour_tag : minute_tag, cctz::detail::hour_tag {};
|
||||
struct day_tag : hour_tag, cctz::detail::day_tag {};
|
||||
struct month_tag : day_tag, cctz::detail::month_tag {};
|
||||
struct year_tag : month_tag, cctz::detail::year_tag {};
|
||||
} // namespace time_internal
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// CivilSecond, CivilMinute, CivilHour, CivilDay, CivilMonth, CivilYear
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Each of these civil-time types is a simple value type with the same
|
||||
// interface for construction and the same six accessors for each of the civil
|
||||
// time fields (year, month, day, hour, minute, and second, aka YMDHMS). These
|
||||
// classes differ only in their alignment, which is indicated by the type name
|
||||
// and specifies the field on which arithmetic operates.
|
||||
//
|
||||
// CONSTRUCTION
|
||||
//
|
||||
// Each of the civil-time types can be constructed in two ways: by directly
|
||||
// passing to the constructor up to six integers representing the YMDHMS fields,
|
||||
// or by copying the YMDHMS fields from a differently aligned civil-time type.
|
||||
// Omitted fields are assigned their minimum valid value. Hours, minutes, and
|
||||
// seconds will be set to 0, month and day will be set to 1. Since there is no
|
||||
// minimum year, the default is 1970.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// absl::CivilDay default_value; // 1970-01-01 00:00:00
|
||||
//
|
||||
// absl::CivilDay a(2015, 2, 3); // 2015-02-03 00:00:00
|
||||
// absl::CivilDay b(2015, 2, 3, 4, 5, 6); // 2015-02-03 00:00:00
|
||||
// absl::CivilDay c(2015); // 2015-01-01 00:00:00
|
||||
//
|
||||
// absl::CivilSecond ss(2015, 2, 3, 4, 5, 6); // 2015-02-03 04:05:06
|
||||
// absl::CivilMinute mm(ss); // 2015-02-03 04:05:00
|
||||
// absl::CivilHour hh(mm); // 2015-02-03 04:00:00
|
||||
// absl::CivilDay d(hh); // 2015-02-03 00:00:00
|
||||
// absl::CivilMonth m(d); // 2015-02-01 00:00:00
|
||||
// absl::CivilYear y(m); // 2015-01-01 00:00:00
|
||||
//
|
||||
// m = absl::CivilMonth(y); // 2015-01-01 00:00:00
|
||||
// d = absl::CivilDay(m); // 2015-01-01 00:00:00
|
||||
// hh = absl::CivilHour(d); // 2015-01-01 00:00:00
|
||||
// mm = absl::CivilMinute(hh); // 2015-01-01 00:00:00
|
||||
// ss = absl::CivilSecond(mm); // 2015-01-01 00:00:00
|
||||
//
|
||||
// Each civil-time class is aligned to the civil-time field indicated in the
|
||||
// class's name after normalization. Alignment is performed by setting all the
|
||||
// inferior fields to their minimum valid value (as described above). The
|
||||
// following are examples of how each of the six types would align the fields
|
||||
// representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the
|
||||
// string format used here is not important; it's just a shorthand way of
|
||||
// showing the six YMDHMS fields.)
|
||||
//
|
||||
// absl::CivilSecond : 2015-11-22 12:34:56
|
||||
// absl::CivilMinute : 2015-11-22 12:34:00
|
||||
// absl::CivilHour : 2015-11-22 12:00:00
|
||||
// absl::CivilDay : 2015-11-22 00:00:00
|
||||
// absl::CivilMonth : 2015-11-01 00:00:00
|
||||
// absl::CivilYear : 2015-01-01 00:00:00
|
||||
//
|
||||
// Each civil-time type performs arithmetic on the field to which it is
|
||||
// aligned. This means that adding 1 to an absl::CivilDay increments the day
|
||||
// field (normalizing as necessary), and subtracting 7 from an absl::CivilMonth
|
||||
// operates on the month field (normalizing as necessary). All arithmetic
|
||||
// produces a valid civil time. Difference requires two similarly aligned
|
||||
// civil-time objects and returns the scalar answer in units of the objects'
|
||||
// alignment. For example, the difference between two absl::CivilHour objects
|
||||
// will give an answer in units of civil hours.
|
||||
//
|
||||
// ALIGNMENT CONVERSION
|
||||
//
|
||||
// The alignment of a civil-time object cannot change, but the object may be
|
||||
// used to construct a new object with a different alignment. This is referred
|
||||
// to as "realigning". When realigning to a type with the same or more
|
||||
// precision (e.g., absl::CivilDay -> absl::CivilSecond), the conversion may be
|
||||
// performed implicitly since no information is lost. However, if information
|
||||
// could be discarded (e.g., CivilSecond -> CivilDay), the conversion must
|
||||
// be explicit at the call site.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// void UseDay(absl::CivilDay day);
|
||||
//
|
||||
// absl::CivilSecond cs;
|
||||
// UseDay(cs); // Won't compile because data may be discarded
|
||||
// UseDay(absl::CivilDay(cs)); // OK: explicit conversion
|
||||
//
|
||||
// absl::CivilDay cd;
|
||||
// UseDay(cd); // OK: no conversion needed
|
||||
//
|
||||
// absl::CivilMonth cm;
|
||||
// UseDay(cm); // OK: implicit conversion to absl::CivilDay
|
||||
//
|
||||
// NORMALIZATION
|
||||
//
|
||||
// Normalization takes invalid values and adjusts them to produce valid values.
|
||||
// Within the civil-time library, integer arguments passed to the Civil*
|
||||
// constructors may be out-of-range, in which case they are normalized by
|
||||
// carrying overflow into a field of courser granularity to produce valid
|
||||
// civil-time objects. This normalization enables natural arithmetic on
|
||||
// constructor arguments without worrying about the field's range.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// // Out-of-range; normalized to 2016-11-01
|
||||
// absl::CivilDay d(2016, 10, 32);
|
||||
// // Out-of-range, negative: normalized to 2016-10-30T23
|
||||
// absl::CivilHour h1(2016, 10, 31, -1);
|
||||
// // Normalization is cumulative: normalized to 2016-10-30T23
|
||||
// absl::CivilHour h2(2016, 10, 32, -25);
|
||||
//
|
||||
// Note: If normalization is undesired, you can signal an error by comparing
|
||||
// the constructor arguments to the normalized values returned by the YMDHMS
|
||||
// properties.
|
||||
//
|
||||
// COMPARISON
|
||||
//
|
||||
// Comparison between civil-time objects considers all six YMDHMS fields,
|
||||
// regardless of the type's alignment. Comparison between differently aligned
|
||||
// civil-time types is allowed.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// absl::CivilDay feb_3(2015, 2, 3); // 2015-02-03 00:00:00
|
||||
// absl::CivilDay mar_4(2015, 3, 4); // 2015-03-04 00:00:00
|
||||
// // feb_3 < mar_4
|
||||
// // absl::CivilYear(feb_3) == absl::CivilYear(mar_4)
|
||||
//
|
||||
// absl::CivilSecond feb_3_noon(2015, 2, 3, 12, 0, 0); // 2015-02-03 12:00:00
|
||||
// // feb_3 < feb_3_noon
|
||||
// // feb_3 == absl::CivilDay(feb_3_noon)
|
||||
//
|
||||
// // Iterates all the days of February 2015.
|
||||
// for (absl::CivilDay d(2015, 2, 1); d < absl::CivilMonth(2015, 3); ++d) {
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// ARITHMETIC
|
||||
//
|
||||
// Civil-time types support natural arithmetic operators such as addition,
|
||||
// subtraction, and difference. Arithmetic operates on the civil-time field
|
||||
// indicated in the type's name. Difference operators require arguments with
|
||||
// the same alignment and return the answer in units of the alignment.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::CivilDay a(2015, 2, 3);
|
||||
// ++a; // 2015-02-04 00:00:00
|
||||
// --a; // 2015-02-03 00:00:00
|
||||
// absl::CivilDay b = a + 1; // 2015-02-04 00:00:00
|
||||
// absl::CivilDay c = 1 + b; // 2015-02-05 00:00:00
|
||||
// int n = c - a; // n = 2 (civil days)
|
||||
// int m = c - absl::CivilMonth(c); // Won't compile: different types.
|
||||
//
|
||||
// ACCESSORS
|
||||
//
|
||||
// Each civil-time type has accessors for all six of the civil-time fields:
|
||||
// year, month, day, hour, minute, and second.
|
||||
//
|
||||
// civil_year_t year()
|
||||
// int month()
|
||||
// int day()
|
||||
// int hour()
|
||||
// int minute()
|
||||
// int second()
|
||||
//
|
||||
// Recall that fields inferior to the type's alignment will be set to their
|
||||
// minimum valid value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::CivilDay d(2015, 6, 28);
|
||||
// // d.year() == 2015
|
||||
// // d.month() == 6
|
||||
// // d.day() == 28
|
||||
// // d.hour() == 0
|
||||
// // d.minute() == 0
|
||||
// // d.second() == 0
|
||||
//
|
||||
// CASE STUDY: Adding a month to January 31.
|
||||
//
|
||||
// One of the classic questions that arises when considering a civil time
|
||||
// library (or a date library or a date/time library) is this:
|
||||
// "What is the result of adding a month to January 31?"
|
||||
// This is an interesting question because it is unclear what is meant by a
|
||||
// "month", and several different answers are possible, depending on context:
|
||||
//
|
||||
// 1. March 3 (or 2 if a leap year), if "add a month" means to add a month to
|
||||
// the current month, and adjust the date to overflow the extra days into
|
||||
// March. In this case the result of "February 31" would be normalized as
|
||||
// within the civil-time library.
|
||||
// 2. February 28 (or 29 if a leap year), if "add a month" means to add a
|
||||
// month, and adjust the date while holding the resulting month constant.
|
||||
// In this case, the result of "February 31" would be truncated to the last
|
||||
// day in February.
|
||||
// 3. An error. The caller may get some error, an exception, an invalid date
|
||||
// object, or perhaps return `false`. This may make sense because there is
|
||||
// no single unambiguously correct answer to the question.
|
||||
//
|
||||
// Practically speaking, any answer that is not what the programmer intended
|
||||
// is the wrong answer.
|
||||
//
|
||||
// The Abseil time library avoids this problem by making it impossible to
|
||||
// ask ambiguous questions. All civil-time objects are aligned to a particular
|
||||
// civil-field boundary (such as aligned to a year, month, day, hour, minute,
|
||||
// or second), and arithmetic operates on the field to which the object is
|
||||
// aligned. This means that in order to "add a month" the object must first be
|
||||
// aligned to a month boundary, which is equivalent to the first day of that
|
||||
// month.
|
||||
//
|
||||
// Of course, there are ways to compute an answer the question at hand using
|
||||
// this Abseil time library, but they require the programmer to be explicit
|
||||
// about the answer they expect. To illustrate, let's see how to compute all
|
||||
// three of the above possible answers to the question of "Jan 31 plus 1
|
||||
// month":
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// const absl::CivilDay d(2015, 1, 31);
|
||||
//
|
||||
// // Answer 1:
|
||||
// // Add 1 to the month field in the constructor, and rely on normalization.
|
||||
// const auto normalized = absl::CivilDay(d.year(), d.month() + 1, d.day());
|
||||
// // normalized == 2015-03-03 (aka Feb 31)
|
||||
//
|
||||
// // Answer 2:
|
||||
// // Add 1 to month field, capping to the end of next month.
|
||||
// const auto next_month = absl::CivilMonth(d) + 1;
|
||||
// const auto last_day_of_next_month = absl::CivilDay(next_month + 1) - 1;
|
||||
// const auto capped = std::min(normalized, last_day_of_next_month);
|
||||
// // capped == 2015-02-28
|
||||
//
|
||||
// // Answer 3:
|
||||
// // Signal an error if the normalized answer is not in next month.
|
||||
// if (absl::CivilMonth(normalized) != next_month) {
|
||||
// // error, month overflow
|
||||
// }
|
||||
//
|
||||
using CivilSecond =
|
||||
time_internal::cctz::detail::civil_time<time_internal::second_tag>;
|
||||
using CivilMinute =
|
||||
time_internal::cctz::detail::civil_time<time_internal::minute_tag>;
|
||||
using CivilHour =
|
||||
time_internal::cctz::detail::civil_time<time_internal::hour_tag>;
|
||||
using CivilDay =
|
||||
time_internal::cctz::detail::civil_time<time_internal::day_tag>;
|
||||
using CivilMonth =
|
||||
time_internal::cctz::detail::civil_time<time_internal::month_tag>;
|
||||
using CivilYear =
|
||||
time_internal::cctz::detail::civil_time<time_internal::year_tag>;
|
||||
|
||||
// civil_year_t
|
||||
//
|
||||
// Type alias of a civil-time year value. This type is guaranteed to (at least)
|
||||
// support any year value supported by `time_t`.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::CivilSecond cs = ...;
|
||||
// absl::civil_year_t y = cs.year();
|
||||
// cs = absl::CivilSecond(y, 1, 1, 0, 0, 0); // CivilSecond(CivilYear(cs))
|
||||
//
|
||||
using civil_year_t = time_internal::cctz::year_t;
|
||||
|
||||
// civil_diff_t
|
||||
//
|
||||
// Type alias of the difference between two civil-time values.
|
||||
// This type is used to indicate arguments that are not
|
||||
// normalized (such as parameters to the civil-time constructors), the results
|
||||
// of civil-time subtraction, or the operand to civil-time addition.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::civil_diff_t n_sec = cs1 - cs2; // cs1 == cs2 + n_sec;
|
||||
//
|
||||
using civil_diff_t = time_internal::cctz::diff_t;
|
||||
|
||||
// Weekday::monday, Weekday::tuesday, Weekday::wednesday, Weekday::thursday,
|
||||
// Weekday::friday, Weekday::saturday, Weekday::sunday
|
||||
//
|
||||
// The Weekday enum class represents the civil-time concept of a "weekday" with
|
||||
// members for all days of the week.
|
||||
//
|
||||
// absl::Weekday wd = absl::Weekday::thursday;
|
||||
//
|
||||
using Weekday = time_internal::cctz::weekday;
|
||||
|
||||
// GetWeekday()
|
||||
//
|
||||
// Returns the absl::Weekday for the given (realigned) civil-time value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::CivilDay a(2015, 8, 13);
|
||||
// absl::Weekday wd = absl::GetWeekday(a); // wd == absl::Weekday::thursday
|
||||
//
|
||||
inline Weekday GetWeekday(CivilSecond cs) {
|
||||
return time_internal::cctz::get_weekday(cs);
|
||||
}
|
||||
|
||||
// NextWeekday()
|
||||
// PrevWeekday()
|
||||
//
|
||||
// Returns the absl::CivilDay that strictly follows or precedes a given
|
||||
// absl::CivilDay, and that falls on the given absl::Weekday.
|
||||
//
|
||||
// Example, given the following month:
|
||||
//
|
||||
// August 2015
|
||||
// Su Mo Tu We Th Fr Sa
|
||||
// 1
|
||||
// 2 3 4 5 6 7 8
|
||||
// 9 10 11 12 13 14 15
|
||||
// 16 17 18 19 20 21 22
|
||||
// 23 24 25 26 27 28 29
|
||||
// 30 31
|
||||
//
|
||||
// absl::CivilDay a(2015, 8, 13);
|
||||
// // absl::GetWeekday(a) == absl::Weekday::thursday
|
||||
// absl::CivilDay b = absl::NextWeekday(a, absl::Weekday::thursday);
|
||||
// // b = 2015-08-20
|
||||
// absl::CivilDay c = absl::PrevWeekday(a, absl::Weekday::thursday);
|
||||
// // c = 2015-08-06
|
||||
//
|
||||
// absl::CivilDay d = ...
|
||||
// // Gets the following Thursday if d is not already Thursday
|
||||
// absl::CivilDay thurs1 = absl::NextWeekday(d - 1, absl::Weekday::thursday);
|
||||
// // Gets the previous Thursday if d is not already Thursday
|
||||
// absl::CivilDay thurs2 = absl::PrevWeekday(d + 1, absl::Weekday::thursday);
|
||||
//
|
||||
inline CivilDay NextWeekday(CivilDay cd, Weekday wd) {
|
||||
return CivilDay(time_internal::cctz::next_weekday(cd, wd));
|
||||
}
|
||||
inline CivilDay PrevWeekday(CivilDay cd, Weekday wd) {
|
||||
return CivilDay(time_internal::cctz::prev_weekday(cd, wd));
|
||||
}
|
||||
|
||||
// GetYearDay()
|
||||
//
|
||||
// Returns the day-of-year for the given (realigned) civil-time value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::CivilDay a(2015, 1, 1);
|
||||
// int yd_jan_1 = absl::GetYearDay(a); // yd_jan_1 = 1
|
||||
// absl::CivilDay b(2015, 12, 31);
|
||||
// int yd_dec_31 = absl::GetYearDay(b); // yd_dec_31 = 365
|
||||
//
|
||||
inline int GetYearDay(CivilSecond cs) {
|
||||
return time_internal::cctz::get_yearday(cs);
|
||||
}
|
||||
|
||||
// FormatCivilTime()
|
||||
//
|
||||
// Formats the given civil-time value into a string value of the following
|
||||
// format:
|
||||
//
|
||||
// Type | Format
|
||||
// ---------------------------------
|
||||
// CivilSecond | YYYY-MM-DDTHH:MM:SS
|
||||
// CivilMinute | YYYY-MM-DDTHH:MM
|
||||
// CivilHour | YYYY-MM-DDTHH
|
||||
// CivilDay | YYYY-MM-DD
|
||||
// CivilMonth | YYYY-MM
|
||||
// CivilYear | YYYY
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::CivilDay d = absl::CivilDay(1969, 7, 20);
|
||||
// std::string day_string = absl::FormatCivilTime(d); // "1969-07-20"
|
||||
//
|
||||
std::string FormatCivilTime(CivilSecond c);
|
||||
std::string FormatCivilTime(CivilMinute c);
|
||||
std::string FormatCivilTime(CivilHour c);
|
||||
std::string FormatCivilTime(CivilDay c);
|
||||
std::string FormatCivilTime(CivilMonth c);
|
||||
std::string FormatCivilTime(CivilYear c);
|
||||
|
||||
// absl::ParseCivilTime()
|
||||
//
|
||||
// Parses a civil-time value from the specified `absl::string_view` into the
|
||||
// passed output parameter. Returns `true` upon successful parsing.
|
||||
//
|
||||
// The expected form of the input string is as follows:
|
||||
//
|
||||
// Type | Format
|
||||
// ---------------------------------
|
||||
// CivilSecond | YYYY-MM-DDTHH:MM:SS
|
||||
// CivilMinute | YYYY-MM-DDTHH:MM
|
||||
// CivilHour | YYYY-MM-DDTHH
|
||||
// CivilDay | YYYY-MM-DD
|
||||
// CivilMonth | YYYY-MM
|
||||
// CivilYear | YYYY
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::CivilDay d;
|
||||
// bool ok = absl::ParseCivilTime("2018-01-02", &d); // OK
|
||||
//
|
||||
// Note that parsing will fail if the string's format does not match the
|
||||
// expected type exactly. `ParseLenientCivilTime()` below is more lenient.
|
||||
//
|
||||
bool ParseCivilTime(absl::string_view s, CivilSecond* c);
|
||||
bool ParseCivilTime(absl::string_view s, CivilMinute* c);
|
||||
bool ParseCivilTime(absl::string_view s, CivilHour* c);
|
||||
bool ParseCivilTime(absl::string_view s, CivilDay* c);
|
||||
bool ParseCivilTime(absl::string_view s, CivilMonth* c);
|
||||
bool ParseCivilTime(absl::string_view s, CivilYear* c);
|
||||
|
||||
// ParseLenientCivilTime()
|
||||
//
|
||||
// Parses any of the formats accepted by `absl::ParseCivilTime()`, but is more
|
||||
// lenient if the format of the string does not exactly match the associated
|
||||
// type.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::CivilDay d;
|
||||
// bool ok = absl::ParseLenientCivilTime("1969-07-20", &d); // OK
|
||||
// ok = absl::ParseLenientCivilTime("1969-07-20T10", &d); // OK: T10 floored
|
||||
// ok = absl::ParseLenientCivilTime("1969-07", &d); // OK: day defaults to 1
|
||||
//
|
||||
bool ParseLenientCivilTime(absl::string_view s, CivilSecond* c);
|
||||
bool ParseLenientCivilTime(absl::string_view s, CivilMinute* c);
|
||||
bool ParseLenientCivilTime(absl::string_view s, CivilHour* c);
|
||||
bool ParseLenientCivilTime(absl::string_view s, CivilDay* c);
|
||||
bool ParseLenientCivilTime(absl::string_view s, CivilMonth* c);
|
||||
bool ParseLenientCivilTime(absl::string_view s, CivilYear* c);
|
||||
|
||||
namespace time_internal { // For functions found via ADL on civil-time tags.
|
||||
|
||||
// Streaming Operators
|
||||
//
|
||||
// Each civil-time type may be sent to an output stream using operator<<().
|
||||
// The result matches the string produced by `FormatCivilTime()`.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// absl::CivilDay d = absl::CivilDay(1969, 7, 20);
|
||||
// std::cout << "Date is: " << d << "\n";
|
||||
//
|
||||
std::ostream& operator<<(std::ostream& os, CivilYear y);
|
||||
std::ostream& operator<<(std::ostream& os, CivilMonth m);
|
||||
std::ostream& operator<<(std::ostream& os, CivilDay d);
|
||||
std::ostream& operator<<(std::ostream& os, CivilHour h);
|
||||
std::ostream& operator<<(std::ostream& os, CivilMinute m);
|
||||
std::ostream& operator<<(std::ostream& os, CivilSecond s);
|
||||
|
||||
} // namespace time_internal
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_CIVIL_TIME_H_
|
||||
127
third_party/abseil_cpp/absl/time/civil_time_benchmark.cc
vendored
Normal file
127
third_party/abseil_cpp/absl/time/civil_time_benchmark.cc
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/time/civil_time.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/hash/hash.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Run on (12 X 3492 MHz CPUs); 2018-11-05T13:44:29.814239103-08:00
|
||||
// CPU: Intel Haswell with HyperThreading (6 cores) dL1:32KB dL2:256KB dL3:15MB
|
||||
// Benchmark Time(ns) CPU(ns) Iterations
|
||||
// ----------------------------------------------------------------
|
||||
// BM_Difference_Days 14.5 14.5 48531105
|
||||
// BM_Step_Days 12.6 12.6 54876006
|
||||
// BM_Format 587 587 1000000
|
||||
// BM_Parse 692 692 1000000
|
||||
// BM_RoundTripFormatParse 1309 1309 532075
|
||||
// BM_CivilYearAbslHash 0.710 0.710 976400000
|
||||
// BM_CivilMonthAbslHash 1.13 1.13 619500000
|
||||
// BM_CivilDayAbslHash 1.70 1.70 426000000
|
||||
// BM_CivilHourAbslHash 2.45 2.45 287600000
|
||||
// BM_CivilMinuteAbslHash 3.21 3.21 226200000
|
||||
// BM_CivilSecondAbslHash 4.10 4.10 171800000
|
||||
|
||||
void BM_Difference_Days(benchmark::State& state) {
|
||||
const absl::CivilDay c(2014, 8, 22);
|
||||
const absl::CivilDay epoch(1970, 1, 1);
|
||||
while (state.KeepRunning()) {
|
||||
const absl::civil_diff_t n = c - epoch;
|
||||
benchmark::DoNotOptimize(n);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Difference_Days);
|
||||
|
||||
void BM_Step_Days(benchmark::State& state) {
|
||||
const absl::CivilDay kStart(2014, 8, 22);
|
||||
absl::CivilDay c = kStart;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(++c);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Step_Days);
|
||||
|
||||
void BM_Format(benchmark::State& state) {
|
||||
const absl::CivilSecond c(2014, 1, 2, 3, 4, 5);
|
||||
while (state.KeepRunning()) {
|
||||
const std::string s = absl::FormatCivilTime(c);
|
||||
benchmark::DoNotOptimize(s);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Format);
|
||||
|
||||
void BM_Parse(benchmark::State& state) {
|
||||
const std::string f = "2014-01-02T03:04:05";
|
||||
absl::CivilSecond c;
|
||||
while (state.KeepRunning()) {
|
||||
const bool b = absl::ParseCivilTime(f, &c);
|
||||
benchmark::DoNotOptimize(b);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Parse);
|
||||
|
||||
void BM_RoundTripFormatParse(benchmark::State& state) {
|
||||
const absl::CivilSecond c(2014, 1, 2, 3, 4, 5);
|
||||
absl::CivilSecond out;
|
||||
while (state.KeepRunning()) {
|
||||
const bool b = absl::ParseCivilTime(absl::FormatCivilTime(c), &out);
|
||||
benchmark::DoNotOptimize(b);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_RoundTripFormatParse);
|
||||
|
||||
template <typename T>
|
||||
void BM_CivilTimeAbslHash(benchmark::State& state) {
|
||||
const int kSize = 100000;
|
||||
std::vector<T> civil_times(kSize);
|
||||
std::iota(civil_times.begin(), civil_times.end(), T(2018));
|
||||
|
||||
absl::Hash<T> absl_hasher;
|
||||
while (state.KeepRunningBatch(kSize)) {
|
||||
for (const T civil_time : civil_times) {
|
||||
benchmark::DoNotOptimize(absl_hasher(civil_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
void BM_CivilYearAbslHash(benchmark::State& state) {
|
||||
BM_CivilTimeAbslHash<absl::CivilYear>(state);
|
||||
}
|
||||
void BM_CivilMonthAbslHash(benchmark::State& state) {
|
||||
BM_CivilTimeAbslHash<absl::CivilMonth>(state);
|
||||
}
|
||||
void BM_CivilDayAbslHash(benchmark::State& state) {
|
||||
BM_CivilTimeAbslHash<absl::CivilDay>(state);
|
||||
}
|
||||
void BM_CivilHourAbslHash(benchmark::State& state) {
|
||||
BM_CivilTimeAbslHash<absl::CivilHour>(state);
|
||||
}
|
||||
void BM_CivilMinuteAbslHash(benchmark::State& state) {
|
||||
BM_CivilTimeAbslHash<absl::CivilMinute>(state);
|
||||
}
|
||||
void BM_CivilSecondAbslHash(benchmark::State& state) {
|
||||
BM_CivilTimeAbslHash<absl::CivilSecond>(state);
|
||||
}
|
||||
BENCHMARK(BM_CivilYearAbslHash);
|
||||
BENCHMARK(BM_CivilMonthAbslHash);
|
||||
BENCHMARK(BM_CivilDayAbslHash);
|
||||
BENCHMARK(BM_CivilHourAbslHash);
|
||||
BENCHMARK(BM_CivilMinuteAbslHash);
|
||||
BENCHMARK(BM_CivilSecondAbslHash);
|
||||
|
||||
} // namespace
|
||||
1243
third_party/abseil_cpp/absl/time/civil_time_test.cc
vendored
Normal file
1243
third_party/abseil_cpp/absl/time/civil_time_test.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
569
third_party/abseil_cpp/absl/time/clock.cc
vendored
Normal file
569
third_party/abseil_cpp/absl/time/clock.cc
vendored
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
|
||||
#include "absl/base/internal/spinlock.h"
|
||||
#include "absl/base/internal/unscaledcycleclock.h"
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/base/port.h"
|
||||
#include "absl/base/thread_annotations.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
Time Now() {
|
||||
// TODO(bww): Get a timespec instead so we don't have to divide.
|
||||
int64_t n = absl::GetCurrentTimeNanos();
|
||||
if (n >= 0) {
|
||||
return time_internal::FromUnixDuration(
|
||||
time_internal::MakeDuration(n / 1000000000, n % 1000000000 * 4));
|
||||
}
|
||||
return time_internal::FromUnixDuration(absl::Nanoseconds(n));
|
||||
}
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
// Decide if we should use the fast GetCurrentTimeNanos() algorithm
|
||||
// based on the cyclecounter, otherwise just get the time directly
|
||||
// from the OS on every call. This can be chosen at compile-time via
|
||||
// -DABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS=[0|1]
|
||||
#ifndef ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
|
||||
#if ABSL_USE_UNSCALED_CYCLECLOCK
|
||||
#define ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS 1
|
||||
#else
|
||||
#define ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
#include "absl/time/internal/get_current_time_chrono.inc"
|
||||
#else
|
||||
#include "absl/time/internal/get_current_time_posix.inc"
|
||||
#endif
|
||||
|
||||
// Allows override by test.
|
||||
#ifndef GET_CURRENT_TIME_NANOS_FROM_SYSTEM
|
||||
#define GET_CURRENT_TIME_NANOS_FROM_SYSTEM() \
|
||||
::absl::time_internal::GetCurrentTimeNanosFromSystem()
|
||||
#endif
|
||||
|
||||
#if !ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
int64_t GetCurrentTimeNanos() {
|
||||
return GET_CURRENT_TIME_NANOS_FROM_SYSTEM();
|
||||
}
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
#else // Use the cyclecounter-based implementation below.
|
||||
|
||||
// Allows override by test.
|
||||
#ifndef GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW
|
||||
#define GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW() \
|
||||
::absl::time_internal::UnscaledCycleClockWrapperForGetCurrentTime::Now()
|
||||
#endif
|
||||
|
||||
// The following counters are used only by the test code.
|
||||
static int64_t stats_initializations;
|
||||
static int64_t stats_reinitializations;
|
||||
static int64_t stats_calibrations;
|
||||
static int64_t stats_slow_paths;
|
||||
static int64_t stats_fast_slow_paths;
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
// This is a friend wrapper around UnscaledCycleClock::Now()
|
||||
// (needed to access UnscaledCycleClock).
|
||||
class UnscaledCycleClockWrapperForGetCurrentTime {
|
||||
public:
|
||||
static int64_t Now() { return base_internal::UnscaledCycleClock::Now(); }
|
||||
};
|
||||
} // namespace time_internal
|
||||
|
||||
// uint64_t is used in this module to provide an extra bit in multiplications
|
||||
|
||||
// Return the time in ns as told by the kernel interface. Place in *cycleclock
|
||||
// the value of the cycleclock at about the time of the syscall.
|
||||
// This call represents the time base that this module synchronizes to.
|
||||
// Ensures that *cycleclock does not step back by up to (1 << 16) from
|
||||
// last_cycleclock, to discard small backward counter steps. (Larger steps are
|
||||
// assumed to be complete resyncs, which shouldn't happen. If they do, a full
|
||||
// reinitialization of the outer algorithm should occur.)
|
||||
static int64_t GetCurrentTimeNanosFromKernel(uint64_t last_cycleclock,
|
||||
uint64_t *cycleclock) {
|
||||
// We try to read clock values at about the same time as the kernel clock.
|
||||
// This value gets adjusted up or down as estimate of how long that should
|
||||
// take, so we can reject attempts that take unusually long.
|
||||
static std::atomic<uint64_t> approx_syscall_time_in_cycles{10 * 1000};
|
||||
|
||||
uint64_t local_approx_syscall_time_in_cycles = // local copy
|
||||
approx_syscall_time_in_cycles.load(std::memory_order_relaxed);
|
||||
|
||||
int64_t current_time_nanos_from_system;
|
||||
uint64_t before_cycles;
|
||||
uint64_t after_cycles;
|
||||
uint64_t elapsed_cycles;
|
||||
int loops = 0;
|
||||
do {
|
||||
before_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
|
||||
current_time_nanos_from_system = GET_CURRENT_TIME_NANOS_FROM_SYSTEM();
|
||||
after_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
|
||||
// elapsed_cycles is unsigned, so is large on overflow
|
||||
elapsed_cycles = after_cycles - before_cycles;
|
||||
if (elapsed_cycles >= local_approx_syscall_time_in_cycles &&
|
||||
++loops == 20) { // clock changed frequencies? Back off.
|
||||
loops = 0;
|
||||
if (local_approx_syscall_time_in_cycles < 1000 * 1000) {
|
||||
local_approx_syscall_time_in_cycles =
|
||||
(local_approx_syscall_time_in_cycles + 1) << 1;
|
||||
}
|
||||
approx_syscall_time_in_cycles.store(
|
||||
local_approx_syscall_time_in_cycles,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
} while (elapsed_cycles >= local_approx_syscall_time_in_cycles ||
|
||||
last_cycleclock - after_cycles < (static_cast<uint64_t>(1) << 16));
|
||||
|
||||
// Number of times in a row we've seen a kernel time call take substantially
|
||||
// less than approx_syscall_time_in_cycles.
|
||||
static std::atomic<uint32_t> seen_smaller{ 0 };
|
||||
|
||||
// Adjust approx_syscall_time_in_cycles to be within a factor of 2
|
||||
// of the typical time to execute one iteration of the loop above.
|
||||
if ((local_approx_syscall_time_in_cycles >> 1) < elapsed_cycles) {
|
||||
// measured time is no smaller than half current approximation
|
||||
seen_smaller.store(0, std::memory_order_relaxed);
|
||||
} else if (seen_smaller.fetch_add(1, std::memory_order_relaxed) >= 3) {
|
||||
// smaller delays several times in a row; reduce approximation by 12.5%
|
||||
const uint64_t new_approximation =
|
||||
local_approx_syscall_time_in_cycles -
|
||||
(local_approx_syscall_time_in_cycles >> 3);
|
||||
approx_syscall_time_in_cycles.store(new_approximation,
|
||||
std::memory_order_relaxed);
|
||||
seen_smaller.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
*cycleclock = after_cycles;
|
||||
return current_time_nanos_from_system;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// An implementation of reader-write locks that use no atomic ops in the read
|
||||
// case. This is a generalization of Lamport's method for reading a multiword
|
||||
// clock. Increment a word on each write acquisition, using the low-order bit
|
||||
// as a spinlock; the word is the high word of the "clock". Readers read the
|
||||
// high word, then all other data, then the high word again, and repeat the
|
||||
// read if the reads of the high words yields different answers, or an odd
|
||||
// value (either case suggests possible interference from a writer).
|
||||
// Here we use a spinlock to ensure only one writer at a time, rather than
|
||||
// spinning on the bottom bit of the word to benefit from SpinLock
|
||||
// spin-delay tuning.
|
||||
|
||||
// Acquire seqlock (*seq) and return the value to be written to unlock.
|
||||
static inline uint64_t SeqAcquire(std::atomic<uint64_t> *seq) {
|
||||
uint64_t x = seq->fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// We put a release fence between update to *seq and writes to shared data.
|
||||
// Thus all stores to shared data are effectively release operations and
|
||||
// update to *seq above cannot be re-ordered past any of them. Note that
|
||||
// this barrier is not for the fetch_add above. A release barrier for the
|
||||
// fetch_add would be before it, not after.
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
|
||||
return x + 2; // original word plus 2
|
||||
}
|
||||
|
||||
// Release seqlock (*seq) by writing x to it---a value previously returned by
|
||||
// SeqAcquire.
|
||||
static inline void SeqRelease(std::atomic<uint64_t> *seq, uint64_t x) {
|
||||
// The unlock store to *seq must have release ordering so that all
|
||||
// updates to shared data must finish before this store.
|
||||
seq->store(x, std::memory_order_release); // release lock for readers
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// "nsscaled" is unit of time equal to a (2**kScale)th of a nanosecond.
|
||||
enum { kScale = 30 };
|
||||
|
||||
// The minimum interval between samples of the time base.
|
||||
// We pick enough time to amortize the cost of the sample,
|
||||
// to get a reasonably accurate cycle counter rate reading,
|
||||
// and not so much that calculations will overflow 64-bits.
|
||||
static const uint64_t kMinNSBetweenSamples = 2000 << 20;
|
||||
|
||||
// We require that kMinNSBetweenSamples shifted by kScale
|
||||
// have at least a bit left over for 64-bit calculations.
|
||||
static_assert(((kMinNSBetweenSamples << (kScale + 1)) >> (kScale + 1)) ==
|
||||
kMinNSBetweenSamples,
|
||||
"cannot represent kMaxBetweenSamplesNSScaled");
|
||||
|
||||
// A reader-writer lock protecting the static locations below.
|
||||
// See SeqAcquire() and SeqRelease() above.
|
||||
ABSL_CONST_INIT static absl::base_internal::SpinLock lock(
|
||||
absl::kConstInit, base_internal::SCHEDULE_KERNEL_ONLY);
|
||||
ABSL_CONST_INIT static std::atomic<uint64_t> seq(0);
|
||||
|
||||
// data from a sample of the kernel's time value
|
||||
struct TimeSampleAtomic {
|
||||
std::atomic<uint64_t> raw_ns; // raw kernel time
|
||||
std::atomic<uint64_t> base_ns; // our estimate of time
|
||||
std::atomic<uint64_t> base_cycles; // cycle counter reading
|
||||
std::atomic<uint64_t> nsscaled_per_cycle; // cycle period
|
||||
// cycles before we'll sample again (a scaled reciprocal of the period,
|
||||
// to avoid a division on the fast path).
|
||||
std::atomic<uint64_t> min_cycles_per_sample;
|
||||
};
|
||||
// Same again, but with non-atomic types
|
||||
struct TimeSample {
|
||||
uint64_t raw_ns; // raw kernel time
|
||||
uint64_t base_ns; // our estimate of time
|
||||
uint64_t base_cycles; // cycle counter reading
|
||||
uint64_t nsscaled_per_cycle; // cycle period
|
||||
uint64_t min_cycles_per_sample; // approx cycles before next sample
|
||||
};
|
||||
|
||||
static struct TimeSampleAtomic last_sample; // the last sample; under seq
|
||||
|
||||
static int64_t GetCurrentTimeNanosSlowPath() ABSL_ATTRIBUTE_COLD;
|
||||
|
||||
// Read the contents of *atomic into *sample.
|
||||
// Each field is read atomically, but to maintain atomicity between fields,
|
||||
// the access must be done under a lock.
|
||||
static void ReadTimeSampleAtomic(const struct TimeSampleAtomic *atomic,
|
||||
struct TimeSample *sample) {
|
||||
sample->base_ns = atomic->base_ns.load(std::memory_order_relaxed);
|
||||
sample->base_cycles = atomic->base_cycles.load(std::memory_order_relaxed);
|
||||
sample->nsscaled_per_cycle =
|
||||
atomic->nsscaled_per_cycle.load(std::memory_order_relaxed);
|
||||
sample->min_cycles_per_sample =
|
||||
atomic->min_cycles_per_sample.load(std::memory_order_relaxed);
|
||||
sample->raw_ns = atomic->raw_ns.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Public routine.
|
||||
// Algorithm: We wish to compute real time from a cycle counter. In normal
|
||||
// operation, we construct a piecewise linear approximation to the kernel time
|
||||
// source, using the cycle counter value. The start of each line segment is at
|
||||
// the same point as the end of the last, but may have a different slope (that
|
||||
// is, a different idea of the cycle counter frequency). Every couple of
|
||||
// seconds, the kernel time source is sampled and compared with the current
|
||||
// approximation. A new slope is chosen that, if followed for another couple
|
||||
// of seconds, will correct the error at the current position. The information
|
||||
// for a sample is in the "last_sample" struct. The linear approximation is
|
||||
// estimated_time = last_sample.base_ns +
|
||||
// last_sample.ns_per_cycle * (counter_reading - last_sample.base_cycles)
|
||||
// (ns_per_cycle is actually stored in different units and scaled, to avoid
|
||||
// overflow). The base_ns of the next linear approximation is the
|
||||
// estimated_time using the last approximation; the base_cycles is the cycle
|
||||
// counter value at that time; the ns_per_cycle is the number of ns per cycle
|
||||
// measured since the last sample, but adjusted so that most of the difference
|
||||
// between the estimated_time and the kernel time will be corrected by the
|
||||
// estimated time to the next sample. In normal operation, this algorithm
|
||||
// relies on:
|
||||
// - the cycle counter and kernel time rates not changing a lot in a few
|
||||
// seconds.
|
||||
// - the client calling into the code often compared to a couple of seconds, so
|
||||
// the time to the next correction can be estimated.
|
||||
// Any time ns_per_cycle is not known, a major error is detected, or the
|
||||
// assumption about frequent calls is violated, the implementation returns the
|
||||
// kernel time. It records sufficient data that a linear approximation can
|
||||
// resume a little later.
|
||||
|
||||
int64_t GetCurrentTimeNanos() {
|
||||
// read the data from the "last_sample" struct (but don't need raw_ns yet)
|
||||
// The reads of "seq" and test of the values emulate a reader lock.
|
||||
uint64_t base_ns;
|
||||
uint64_t base_cycles;
|
||||
uint64_t nsscaled_per_cycle;
|
||||
uint64_t min_cycles_per_sample;
|
||||
uint64_t seq_read0;
|
||||
uint64_t seq_read1;
|
||||
|
||||
// If we have enough information to interpolate, the value returned will be
|
||||
// derived from this cycleclock-derived time estimate. On some platforms
|
||||
// (POWER) the function to retrieve this value has enough complexity to
|
||||
// contribute to register pressure - reading it early before initializing
|
||||
// the other pieces of the calculation minimizes spill/restore instructions,
|
||||
// minimizing icache cost.
|
||||
uint64_t now_cycles = GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW();
|
||||
|
||||
// Acquire pairs with the barrier in SeqRelease - if this load sees that
|
||||
// store, the shared-data reads necessarily see that SeqRelease's updates
|
||||
// to the same shared data.
|
||||
seq_read0 = seq.load(std::memory_order_acquire);
|
||||
|
||||
base_ns = last_sample.base_ns.load(std::memory_order_relaxed);
|
||||
base_cycles = last_sample.base_cycles.load(std::memory_order_relaxed);
|
||||
nsscaled_per_cycle =
|
||||
last_sample.nsscaled_per_cycle.load(std::memory_order_relaxed);
|
||||
min_cycles_per_sample =
|
||||
last_sample.min_cycles_per_sample.load(std::memory_order_relaxed);
|
||||
|
||||
// This acquire fence pairs with the release fence in SeqAcquire. Since it
|
||||
// is sequenced between reads of shared data and seq_read1, the reads of
|
||||
// shared data are effectively acquiring.
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
|
||||
// The shared-data reads are effectively acquire ordered, and the
|
||||
// shared-data writes are effectively release ordered. Therefore if our
|
||||
// shared-data reads see any of a particular update's shared-data writes,
|
||||
// seq_read1 is guaranteed to see that update's SeqAcquire.
|
||||
seq_read1 = seq.load(std::memory_order_relaxed);
|
||||
|
||||
// Fast path. Return if min_cycles_per_sample has not yet elapsed since the
|
||||
// last sample, and we read a consistent sample. The fast path activates
|
||||
// only when min_cycles_per_sample is non-zero, which happens when we get an
|
||||
// estimate for the cycle time. The predicate will fail if now_cycles <
|
||||
// base_cycles, or if some other thread is in the slow path.
|
||||
//
|
||||
// Since we now read now_cycles before base_ns, it is possible for now_cycles
|
||||
// to be less than base_cycles (if we were interrupted between those loads and
|
||||
// last_sample was updated). This is harmless, because delta_cycles will wrap
|
||||
// and report a time much much bigger than min_cycles_per_sample. In that case
|
||||
// we will take the slow path.
|
||||
uint64_t delta_cycles = now_cycles - base_cycles;
|
||||
if (seq_read0 == seq_read1 && (seq_read0 & 1) == 0 &&
|
||||
delta_cycles < min_cycles_per_sample) {
|
||||
return base_ns + ((delta_cycles * nsscaled_per_cycle) >> kScale);
|
||||
}
|
||||
return GetCurrentTimeNanosSlowPath();
|
||||
}
|
||||
|
||||
// Return (a << kScale)/b.
|
||||
// Zero is returned if b==0. Scaling is performed internally to
|
||||
// preserve precision without overflow.
|
||||
static uint64_t SafeDivideAndScale(uint64_t a, uint64_t b) {
|
||||
// Find maximum safe_shift so that
|
||||
// 0 <= safe_shift <= kScale and (a << safe_shift) does not overflow.
|
||||
int safe_shift = kScale;
|
||||
while (((a << safe_shift) >> safe_shift) != a) {
|
||||
safe_shift--;
|
||||
}
|
||||
uint64_t scaled_b = b >> (kScale - safe_shift);
|
||||
uint64_t quotient = 0;
|
||||
if (scaled_b != 0) {
|
||||
quotient = (a << safe_shift) / scaled_b;
|
||||
}
|
||||
return quotient;
|
||||
}
|
||||
|
||||
static uint64_t UpdateLastSample(
|
||||
uint64_t now_cycles, uint64_t now_ns, uint64_t delta_cycles,
|
||||
const struct TimeSample *sample) ABSL_ATTRIBUTE_COLD;
|
||||
|
||||
// The slow path of GetCurrentTimeNanos(). This is taken while gathering
|
||||
// initial samples, when enough time has elapsed since the last sample, and if
|
||||
// any other thread is writing to last_sample.
|
||||
//
|
||||
// Manually mark this 'noinline' to minimize stack frame size of the fast
|
||||
// path. Without this, sometimes a compiler may inline this big block of code
|
||||
// into the fast path. That causes lots of register spills and reloads that
|
||||
// are unnecessary unless the slow path is taken.
|
||||
//
|
||||
// TODO(absl-team): Remove this attribute when our compiler is smart enough
|
||||
// to do the right thing.
|
||||
ABSL_ATTRIBUTE_NOINLINE
|
||||
static int64_t GetCurrentTimeNanosSlowPath() ABSL_LOCKS_EXCLUDED(lock) {
|
||||
// Serialize access to slow-path. Fast-path readers are not blocked yet, and
|
||||
// code below must not modify last_sample until the seqlock is acquired.
|
||||
lock.Lock();
|
||||
|
||||
// Sample the kernel time base. This is the definition of
|
||||
// "now" if we take the slow path.
|
||||
static uint64_t last_now_cycles; // protected by lock
|
||||
uint64_t now_cycles;
|
||||
uint64_t now_ns = GetCurrentTimeNanosFromKernel(last_now_cycles, &now_cycles);
|
||||
last_now_cycles = now_cycles;
|
||||
|
||||
uint64_t estimated_base_ns;
|
||||
|
||||
// ----------
|
||||
// Read the "last_sample" values again; this time holding the write lock.
|
||||
struct TimeSample sample;
|
||||
ReadTimeSampleAtomic(&last_sample, &sample);
|
||||
|
||||
// ----------
|
||||
// Try running the fast path again; another thread may have updated the
|
||||
// sample between our run of the fast path and the sample we just read.
|
||||
uint64_t delta_cycles = now_cycles - sample.base_cycles;
|
||||
if (delta_cycles < sample.min_cycles_per_sample) {
|
||||
// Another thread updated the sample. This path does not take the seqlock
|
||||
// so that blocked readers can make progress without blocking new readers.
|
||||
estimated_base_ns = sample.base_ns +
|
||||
((delta_cycles * sample.nsscaled_per_cycle) >> kScale);
|
||||
stats_fast_slow_paths++;
|
||||
} else {
|
||||
estimated_base_ns =
|
||||
UpdateLastSample(now_cycles, now_ns, delta_cycles, &sample);
|
||||
}
|
||||
|
||||
lock.Unlock();
|
||||
|
||||
return estimated_base_ns;
|
||||
}
|
||||
|
||||
// Main part of the algorithm. Locks out readers, updates the approximation
|
||||
// using the new sample from the kernel, and stores the result in last_sample
|
||||
// for readers. Returns the new estimated time.
|
||||
static uint64_t UpdateLastSample(uint64_t now_cycles, uint64_t now_ns,
|
||||
uint64_t delta_cycles,
|
||||
const struct TimeSample *sample)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock) {
|
||||
uint64_t estimated_base_ns = now_ns;
|
||||
uint64_t lock_value = SeqAcquire(&seq); // acquire seqlock to block readers
|
||||
|
||||
// The 5s in the next if-statement limits the time for which we will trust
|
||||
// the cycle counter and our last sample to give a reasonable result.
|
||||
// Errors in the rate of the source clock can be multiplied by the ratio
|
||||
// between this limit and kMinNSBetweenSamples.
|
||||
if (sample->raw_ns == 0 || // no recent sample, or clock went backwards
|
||||
sample->raw_ns + static_cast<uint64_t>(5) * 1000 * 1000 * 1000 < now_ns ||
|
||||
now_ns < sample->raw_ns || now_cycles < sample->base_cycles) {
|
||||
// record this sample, and forget any previously known slope.
|
||||
last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
|
||||
last_sample.base_ns.store(estimated_base_ns, std::memory_order_relaxed);
|
||||
last_sample.base_cycles.store(now_cycles, std::memory_order_relaxed);
|
||||
last_sample.nsscaled_per_cycle.store(0, std::memory_order_relaxed);
|
||||
last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed);
|
||||
stats_initializations++;
|
||||
} else if (sample->raw_ns + 500 * 1000 * 1000 < now_ns &&
|
||||
sample->base_cycles + 50 < now_cycles) {
|
||||
// Enough time has passed to compute the cycle time.
|
||||
if (sample->nsscaled_per_cycle != 0) { // Have a cycle time estimate.
|
||||
// Compute time from counter reading, but avoiding overflow
|
||||
// delta_cycles may be larger than on the fast path.
|
||||
uint64_t estimated_scaled_ns;
|
||||
int s = -1;
|
||||
do {
|
||||
s++;
|
||||
estimated_scaled_ns = (delta_cycles >> s) * sample->nsscaled_per_cycle;
|
||||
} while (estimated_scaled_ns / sample->nsscaled_per_cycle !=
|
||||
(delta_cycles >> s));
|
||||
estimated_base_ns = sample->base_ns +
|
||||
(estimated_scaled_ns >> (kScale - s));
|
||||
}
|
||||
|
||||
// Compute the assumed cycle time kMinNSBetweenSamples ns into the future
|
||||
// assuming the cycle counter rate stays the same as the last interval.
|
||||
uint64_t ns = now_ns - sample->raw_ns;
|
||||
uint64_t measured_nsscaled_per_cycle = SafeDivideAndScale(ns, delta_cycles);
|
||||
|
||||
uint64_t assumed_next_sample_delta_cycles =
|
||||
SafeDivideAndScale(kMinNSBetweenSamples, measured_nsscaled_per_cycle);
|
||||
|
||||
int64_t diff_ns = now_ns - estimated_base_ns; // estimate low by this much
|
||||
|
||||
// We want to set nsscaled_per_cycle so that our estimate of the ns time
|
||||
// at the assumed cycle time is the assumed ns time.
|
||||
// That is, we want to set nsscaled_per_cycle so:
|
||||
// kMinNSBetweenSamples + diff_ns ==
|
||||
// (assumed_next_sample_delta_cycles * nsscaled_per_cycle) >> kScale
|
||||
// But we wish to damp oscillations, so instead correct only most
|
||||
// of our current error, by solving:
|
||||
// kMinNSBetweenSamples + diff_ns - (diff_ns / 16) ==
|
||||
// (assumed_next_sample_delta_cycles * nsscaled_per_cycle) >> kScale
|
||||
ns = kMinNSBetweenSamples + diff_ns - (diff_ns / 16);
|
||||
uint64_t new_nsscaled_per_cycle =
|
||||
SafeDivideAndScale(ns, assumed_next_sample_delta_cycles);
|
||||
if (new_nsscaled_per_cycle != 0 &&
|
||||
diff_ns < 100 * 1000 * 1000 && -diff_ns < 100 * 1000 * 1000) {
|
||||
// record the cycle time measurement
|
||||
last_sample.nsscaled_per_cycle.store(
|
||||
new_nsscaled_per_cycle, std::memory_order_relaxed);
|
||||
uint64_t new_min_cycles_per_sample =
|
||||
SafeDivideAndScale(kMinNSBetweenSamples, new_nsscaled_per_cycle);
|
||||
last_sample.min_cycles_per_sample.store(
|
||||
new_min_cycles_per_sample, std::memory_order_relaxed);
|
||||
stats_calibrations++;
|
||||
} else { // something went wrong; forget the slope
|
||||
last_sample.nsscaled_per_cycle.store(0, std::memory_order_relaxed);
|
||||
last_sample.min_cycles_per_sample.store(0, std::memory_order_relaxed);
|
||||
estimated_base_ns = now_ns;
|
||||
stats_reinitializations++;
|
||||
}
|
||||
last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
|
||||
last_sample.base_ns.store(estimated_base_ns, std::memory_order_relaxed);
|
||||
last_sample.base_cycles.store(now_cycles, std::memory_order_relaxed);
|
||||
} else {
|
||||
// have a sample, but no slope; waiting for enough time for a calibration
|
||||
stats_slow_paths++;
|
||||
}
|
||||
|
||||
SeqRelease(&seq, lock_value); // release the readers
|
||||
|
||||
return estimated_base_ns;
|
||||
}
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
#endif // ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace {
|
||||
|
||||
// Returns the maximum duration that SleepOnce() can sleep for.
|
||||
constexpr absl::Duration MaxSleep() {
|
||||
#ifdef _WIN32
|
||||
// Windows Sleep() takes unsigned long argument in milliseconds.
|
||||
return absl::Milliseconds(
|
||||
std::numeric_limits<unsigned long>::max()); // NOLINT(runtime/int)
|
||||
#else
|
||||
return absl::Seconds(std::numeric_limits<time_t>::max());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Sleeps for the given duration.
|
||||
// REQUIRES: to_sleep <= MaxSleep().
|
||||
void SleepOnce(absl::Duration to_sleep) {
|
||||
#ifdef _WIN32
|
||||
Sleep(to_sleep / absl::Milliseconds(1));
|
||||
#else
|
||||
struct timespec sleep_time = absl::ToTimespec(to_sleep);
|
||||
while (nanosleep(&sleep_time, &sleep_time) != 0 && errno == EINTR) {
|
||||
// Ignore signals and wait for the full interval to elapse.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
extern "C" {
|
||||
|
||||
ABSL_ATTRIBUTE_WEAK void AbslInternalSleepFor(absl::Duration duration) {
|
||||
while (duration > absl::ZeroDuration()) {
|
||||
absl::Duration to_sleep = std::min(duration, absl::MaxSleep());
|
||||
absl::SleepOnce(to_sleep);
|
||||
duration -= to_sleep;
|
||||
}
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
74
third_party/abseil_cpp/absl/time/clock.h
vendored
Normal file
74
third_party/abseil_cpp/absl/time/clock.h
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
// File: clock.h
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// This header file contains utility functions for working with the system-wide
|
||||
// realtime clock. For descriptions of the main time abstractions used within
|
||||
// this header file, consult the time.h header file.
|
||||
#ifndef ABSL_TIME_CLOCK_H_
|
||||
#define ABSL_TIME_CLOCK_H_
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
// Now()
|
||||
//
|
||||
// Returns the current time, expressed as an `absl::Time` absolute time value.
|
||||
absl::Time Now();
|
||||
|
||||
// GetCurrentTimeNanos()
|
||||
//
|
||||
// Returns the current time, expressed as a count of nanoseconds since the Unix
|
||||
// Epoch (https://en.wikipedia.org/wiki/Unix_time). Prefer `absl::Now()` instead
|
||||
// for all but the most performance-sensitive cases (i.e. when you are calling
|
||||
// this function hundreds of thousands of times per second).
|
||||
int64_t GetCurrentTimeNanos();
|
||||
|
||||
// SleepFor()
|
||||
//
|
||||
// Sleeps for the specified duration, expressed as an `absl::Duration`.
|
||||
//
|
||||
// Notes:
|
||||
// * Signal interruptions will not reduce the sleep duration.
|
||||
// * Returns immediately when passed a nonpositive duration.
|
||||
void SleepFor(absl::Duration duration);
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Implementation Details
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// In some build configurations we pass --detect-odr-violations to the
|
||||
// gold linker. This causes it to flag weak symbol overrides as ODR
|
||||
// violations. Because ODR only applies to C++ and not C,
|
||||
// --detect-odr-violations ignores symbols not mangled with C++ names.
|
||||
// By changing our extension points to be extern "C", we dodge this
|
||||
// check.
|
||||
extern "C" {
|
||||
void AbslInternalSleepFor(absl::Duration duration);
|
||||
} // extern "C"
|
||||
|
||||
inline void absl::SleepFor(absl::Duration duration) {
|
||||
AbslInternalSleepFor(duration);
|
||||
}
|
||||
|
||||
#endif // ABSL_TIME_CLOCK_H_
|
||||
74
third_party/abseil_cpp/absl/time/clock_benchmark.cc
vendored
Normal file
74
third_party/abseil_cpp/absl/time/clock_benchmark.cc
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#endif // _WIN32
|
||||
#include <cstdio>
|
||||
|
||||
#include "absl/base/internal/cycleclock.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void BM_Clock_Now_AbslTime(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Now());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_AbslTime);
|
||||
|
||||
void BM_Clock_Now_GetCurrentTimeNanos(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::GetCurrentTimeNanos());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_GetCurrentTimeNanos);
|
||||
|
||||
void BM_Clock_Now_AbslTime_ToUnixNanos(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToUnixNanos(absl::Now()));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_AbslTime_ToUnixNanos);
|
||||
|
||||
void BM_Clock_Now_CycleClock(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::base_internal::CycleClock::Now());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_CycleClock);
|
||||
|
||||
#if !defined(_WIN32)
|
||||
static void BM_Clock_Now_gettimeofday(benchmark::State& state) {
|
||||
struct timeval tv;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(gettimeofday(&tv, nullptr));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_gettimeofday);
|
||||
|
||||
static void BM_Clock_Now_clock_gettime(benchmark::State& state) {
|
||||
struct timespec ts;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(clock_gettime(CLOCK_REALTIME, &ts));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_clock_gettime);
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace
|
||||
118
third_party/abseil_cpp/absl/time/clock_test.cc
vendored
Normal file
118
third_party/abseil_cpp/absl/time/clock_test.cc
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#if defined(ABSL_HAVE_ALARM)
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
#error all known Linux and Apple targets have alarm
|
||||
#endif
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Time, Now) {
|
||||
const absl::Time before = absl::FromUnixNanos(absl::GetCurrentTimeNanos());
|
||||
const absl::Time now = absl::Now();
|
||||
const absl::Time after = absl::FromUnixNanos(absl::GetCurrentTimeNanos());
|
||||
EXPECT_GE(now, before);
|
||||
EXPECT_GE(after, now);
|
||||
}
|
||||
|
||||
enum class AlarmPolicy { kWithoutAlarm, kWithAlarm };
|
||||
|
||||
#if defined(ABSL_HAVE_ALARM)
|
||||
bool alarm_handler_invoked = false;
|
||||
|
||||
void AlarmHandler(int signo) {
|
||||
ASSERT_EQ(signo, SIGALRM);
|
||||
alarm_handler_invoked = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Does SleepFor(d) take between lower_bound and upper_bound at least
|
||||
// once between now and (now + timeout)? If requested (and supported),
|
||||
// add an alarm for the middle of the sleep period and expect it to fire.
|
||||
bool SleepForBounded(absl::Duration d, absl::Duration lower_bound,
|
||||
absl::Duration upper_bound, absl::Duration timeout,
|
||||
AlarmPolicy alarm_policy, int* attempts) {
|
||||
const absl::Time deadline = absl::Now() + timeout;
|
||||
while (absl::Now() < deadline) {
|
||||
#if defined(ABSL_HAVE_ALARM)
|
||||
sig_t old_alarm = SIG_DFL;
|
||||
if (alarm_policy == AlarmPolicy::kWithAlarm) {
|
||||
alarm_handler_invoked = false;
|
||||
old_alarm = signal(SIGALRM, AlarmHandler);
|
||||
alarm(absl::ToInt64Seconds(d / 2));
|
||||
}
|
||||
#else
|
||||
EXPECT_EQ(alarm_policy, AlarmPolicy::kWithoutAlarm);
|
||||
#endif
|
||||
++*attempts;
|
||||
absl::Time start = absl::Now();
|
||||
absl::SleepFor(d);
|
||||
absl::Duration actual = absl::Now() - start;
|
||||
#if defined(ABSL_HAVE_ALARM)
|
||||
if (alarm_policy == AlarmPolicy::kWithAlarm) {
|
||||
signal(SIGALRM, old_alarm);
|
||||
if (!alarm_handler_invoked) continue;
|
||||
}
|
||||
#endif
|
||||
if (lower_bound <= actual && actual <= upper_bound) {
|
||||
return true; // yes, the SleepFor() was correctly bounded
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
testing::AssertionResult AssertSleepForBounded(absl::Duration d,
|
||||
absl::Duration early,
|
||||
absl::Duration late,
|
||||
absl::Duration timeout,
|
||||
AlarmPolicy alarm_policy) {
|
||||
const absl::Duration lower_bound = d - early;
|
||||
const absl::Duration upper_bound = d + late;
|
||||
int attempts = 0;
|
||||
if (SleepForBounded(d, lower_bound, upper_bound, timeout, alarm_policy,
|
||||
&attempts)) {
|
||||
return testing::AssertionSuccess();
|
||||
}
|
||||
return testing::AssertionFailure()
|
||||
<< "SleepFor(" << d << ") did not return within [" << lower_bound
|
||||
<< ":" << upper_bound << "] in " << attempts << " attempt"
|
||||
<< (attempts == 1 ? "" : "s") << " over " << timeout
|
||||
<< (alarm_policy == AlarmPolicy::kWithAlarm ? " with" : " without")
|
||||
<< " an alarm";
|
||||
}
|
||||
|
||||
// Tests that SleepFor() returns neither too early nor too late.
|
||||
TEST(SleepFor, Bounded) {
|
||||
const absl::Duration d = absl::Milliseconds(2500);
|
||||
const absl::Duration early = absl::Milliseconds(100);
|
||||
const absl::Duration late = absl::Milliseconds(300);
|
||||
const absl::Duration timeout = 48 * d;
|
||||
EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
|
||||
AlarmPolicy::kWithoutAlarm));
|
||||
#if defined(ABSL_HAVE_ALARM)
|
||||
EXPECT_TRUE(AssertSleepForBounded(d, early, late, timeout,
|
||||
AlarmPolicy::kWithAlarm));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
951
third_party/abseil_cpp/absl/time/duration.cc
vendored
Normal file
951
third_party/abseil_cpp/absl/time/duration.cc
vendored
Normal file
|
|
@ -0,0 +1,951 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The implementation of the absl::Duration class, which is declared in
|
||||
// //absl/time.h. This class behaves like a numeric type; it has no public
|
||||
// methods and is used only through the operators defined here.
|
||||
//
|
||||
// Implementation notes:
|
||||
//
|
||||
// An absl::Duration is represented as
|
||||
//
|
||||
// rep_hi_ : (int64_t) Whole seconds
|
||||
// rep_lo_ : (uint32_t) Fractions of a second
|
||||
//
|
||||
// The seconds value (rep_hi_) may be positive or negative as appropriate.
|
||||
// The fractional seconds (rep_lo_) is always a positive offset from rep_hi_.
|
||||
// The API for Duration guarantees at least nanosecond resolution, which
|
||||
// means rep_lo_ could have a max value of 1B - 1 if it stored nanoseconds.
|
||||
// However, to utilize more of the available 32 bits of space in rep_lo_,
|
||||
// we instead store quarters of a nanosecond in rep_lo_ resulting in a max
|
||||
// value of 4B - 1. This allows us to correctly handle calculations like
|
||||
// 0.5 nanos + 0.5 nanos = 1 nano. The following example shows the actual
|
||||
// Duration rep using quarters of a nanosecond.
|
||||
//
|
||||
// 2.5 sec = {rep_hi_=2, rep_lo_=2000000000} // lo = 4 * 500000000
|
||||
// -2.5 sec = {rep_hi_=-3, rep_lo_=2000000000}
|
||||
//
|
||||
// Infinite durations are represented as Durations with the rep_lo_ field set
|
||||
// to all 1s.
|
||||
//
|
||||
// +InfiniteDuration:
|
||||
// rep_hi_ : kint64max
|
||||
// rep_lo_ : ~0U
|
||||
//
|
||||
// -InfiniteDuration:
|
||||
// rep_hi_ : kint64min
|
||||
// rep_lo_ : ~0U
|
||||
//
|
||||
// Arithmetic overflows/underflows to +/- infinity and saturates.
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <winsock2.h> // for timeval
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/casts.h"
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/strings/strip.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
using time_internal::kTicksPerNanosecond;
|
||||
using time_internal::kTicksPerSecond;
|
||||
|
||||
constexpr int64_t kint64max = std::numeric_limits<int64_t>::max();
|
||||
constexpr int64_t kint64min = std::numeric_limits<int64_t>::min();
|
||||
|
||||
// Can't use std::isinfinite() because it doesn't exist on windows.
|
||||
inline bool IsFinite(double d) {
|
||||
if (std::isnan(d)) return false;
|
||||
return d != std::numeric_limits<double>::infinity() &&
|
||||
d != -std::numeric_limits<double>::infinity();
|
||||
}
|
||||
|
||||
inline bool IsValidDivisor(double d) {
|
||||
if (std::isnan(d)) return false;
|
||||
return d != 0.0;
|
||||
}
|
||||
|
||||
// Can't use std::round() because it is only available in C++11.
|
||||
// Note that we ignore the possibility of floating-point over/underflow.
|
||||
template <typename Double>
|
||||
inline double Round(Double d) {
|
||||
return d < 0 ? std::ceil(d - 0.5) : std::floor(d + 0.5);
|
||||
}
|
||||
|
||||
// *sec may be positive or negative. *ticks must be in the range
|
||||
// -kTicksPerSecond < *ticks < kTicksPerSecond. If *ticks is negative it
|
||||
// will be normalized to a positive value by adjusting *sec accordingly.
|
||||
inline void NormalizeTicks(int64_t* sec, int64_t* ticks) {
|
||||
if (*ticks < 0) {
|
||||
--*sec;
|
||||
*ticks += kTicksPerSecond;
|
||||
}
|
||||
}
|
||||
|
||||
// Makes a uint128 from the absolute value of the given scalar.
|
||||
inline uint128 MakeU128(int64_t a) {
|
||||
uint128 u128 = 0;
|
||||
if (a < 0) {
|
||||
++u128;
|
||||
++a; // Makes it safe to negate 'a'
|
||||
a = -a;
|
||||
}
|
||||
u128 += static_cast<uint64_t>(a);
|
||||
return u128;
|
||||
}
|
||||
|
||||
// Makes a uint128 count of ticks out of the absolute value of the Duration.
|
||||
inline uint128 MakeU128Ticks(Duration d) {
|
||||
int64_t rep_hi = time_internal::GetRepHi(d);
|
||||
uint32_t rep_lo = time_internal::GetRepLo(d);
|
||||
if (rep_hi < 0) {
|
||||
++rep_hi;
|
||||
rep_hi = -rep_hi;
|
||||
rep_lo = kTicksPerSecond - rep_lo;
|
||||
}
|
||||
uint128 u128 = static_cast<uint64_t>(rep_hi);
|
||||
u128 *= static_cast<uint64_t>(kTicksPerSecond);
|
||||
u128 += rep_lo;
|
||||
return u128;
|
||||
}
|
||||
|
||||
// Breaks a uint128 of ticks into a Duration.
|
||||
inline Duration MakeDurationFromU128(uint128 u128, bool is_neg) {
|
||||
int64_t rep_hi;
|
||||
uint32_t rep_lo;
|
||||
const uint64_t h64 = Uint128High64(u128);
|
||||
const uint64_t l64 = Uint128Low64(u128);
|
||||
if (h64 == 0) { // fastpath
|
||||
const uint64_t hi = l64 / kTicksPerSecond;
|
||||
rep_hi = static_cast<int64_t>(hi);
|
||||
rep_lo = static_cast<uint32_t>(l64 - hi * kTicksPerSecond);
|
||||
} else {
|
||||
// kMaxRepHi64 is the high 64 bits of (2^63 * kTicksPerSecond).
|
||||
// Any positive tick count whose high 64 bits are >= kMaxRepHi64
|
||||
// is not representable as a Duration. A negative tick count can
|
||||
// have its high 64 bits == kMaxRepHi64 but only when the low 64
|
||||
// bits are all zero, otherwise it is not representable either.
|
||||
const uint64_t kMaxRepHi64 = 0x77359400UL;
|
||||
if (h64 >= kMaxRepHi64) {
|
||||
if (is_neg && h64 == kMaxRepHi64 && l64 == 0) {
|
||||
// Avoid trying to represent -kint64min below.
|
||||
return time_internal::MakeDuration(kint64min);
|
||||
}
|
||||
return is_neg ? -InfiniteDuration() : InfiniteDuration();
|
||||
}
|
||||
const uint128 kTicksPerSecond128 = static_cast<uint64_t>(kTicksPerSecond);
|
||||
const uint128 hi = u128 / kTicksPerSecond128;
|
||||
rep_hi = static_cast<int64_t>(Uint128Low64(hi));
|
||||
rep_lo =
|
||||
static_cast<uint32_t>(Uint128Low64(u128 - hi * kTicksPerSecond128));
|
||||
}
|
||||
if (is_neg) {
|
||||
rep_hi = -rep_hi;
|
||||
if (rep_lo != 0) {
|
||||
--rep_hi;
|
||||
rep_lo = kTicksPerSecond - rep_lo;
|
||||
}
|
||||
}
|
||||
return time_internal::MakeDuration(rep_hi, rep_lo);
|
||||
}
|
||||
|
||||
// Convert between int64_t and uint64_t, preserving representation. This
|
||||
// allows us to do arithmetic in the unsigned domain, where overflow has
|
||||
// well-defined behavior. See operator+=() and operator-=().
|
||||
//
|
||||
// C99 7.20.1.1.1, as referenced by C++11 18.4.1.2, says, "The typedef
|
||||
// name intN_t designates a signed integer type with width N, no padding
|
||||
// bits, and a two's complement representation." So, we can convert to
|
||||
// and from the corresponding uint64_t value using a bit cast.
|
||||
inline uint64_t EncodeTwosComp(int64_t v) {
|
||||
return absl::bit_cast<uint64_t>(v);
|
||||
}
|
||||
inline int64_t DecodeTwosComp(uint64_t v) { return absl::bit_cast<int64_t>(v); }
|
||||
|
||||
// Note: The overflow detection in this function is done using greater/less *or
|
||||
// equal* because kint64max/min is too large to be represented exactly in a
|
||||
// double (which only has 53 bits of precision). In order to avoid assigning to
|
||||
// rep->hi a double value that is too large for an int64_t (and therefore is
|
||||
// undefined), we must consider computations that equal kint64max/min as a
|
||||
// double as overflow cases.
|
||||
inline bool SafeAddRepHi(double a_hi, double b_hi, Duration* d) {
|
||||
double c = a_hi + b_hi;
|
||||
if (c >= static_cast<double>(kint64max)) {
|
||||
*d = InfiniteDuration();
|
||||
return false;
|
||||
}
|
||||
if (c <= static_cast<double>(kint64min)) {
|
||||
*d = -InfiniteDuration();
|
||||
return false;
|
||||
}
|
||||
*d = time_internal::MakeDuration(c, time_internal::GetRepLo(*d));
|
||||
return true;
|
||||
}
|
||||
|
||||
// A functor that's similar to std::multiplies<T>, except this returns the max
|
||||
// T value instead of overflowing. This is only defined for uint128.
|
||||
template <typename Ignored>
|
||||
struct SafeMultiply {
|
||||
uint128 operator()(uint128 a, uint128 b) const {
|
||||
// b hi is always zero because it originated as an int64_t.
|
||||
assert(Uint128High64(b) == 0);
|
||||
// Fastpath to avoid the expensive overflow check with division.
|
||||
if (Uint128High64(a) == 0) {
|
||||
return (((Uint128Low64(a) | Uint128Low64(b)) >> 32) == 0)
|
||||
? static_cast<uint128>(Uint128Low64(a) * Uint128Low64(b))
|
||||
: a * b;
|
||||
}
|
||||
return b == 0 ? b : (a > kuint128max / b) ? kuint128max : a * b;
|
||||
}
|
||||
};
|
||||
|
||||
// Scales (i.e., multiplies or divides, depending on the Operation template)
|
||||
// the Duration d by the int64_t r.
|
||||
template <template <typename> class Operation>
|
||||
inline Duration ScaleFixed(Duration d, int64_t r) {
|
||||
const uint128 a = MakeU128Ticks(d);
|
||||
const uint128 b = MakeU128(r);
|
||||
const uint128 q = Operation<uint128>()(a, b);
|
||||
const bool is_neg = (time_internal::GetRepHi(d) < 0) != (r < 0);
|
||||
return MakeDurationFromU128(q, is_neg);
|
||||
}
|
||||
|
||||
// Scales (i.e., multiplies or divides, depending on the Operation template)
|
||||
// the Duration d by the double r.
|
||||
template <template <typename> class Operation>
|
||||
inline Duration ScaleDouble(Duration d, double r) {
|
||||
Operation<double> op;
|
||||
double hi_doub = op(time_internal::GetRepHi(d), r);
|
||||
double lo_doub = op(time_internal::GetRepLo(d), r);
|
||||
|
||||
double hi_int = 0;
|
||||
double hi_frac = std::modf(hi_doub, &hi_int);
|
||||
|
||||
// Moves hi's fractional bits to lo.
|
||||
lo_doub /= kTicksPerSecond;
|
||||
lo_doub += hi_frac;
|
||||
|
||||
double lo_int = 0;
|
||||
double lo_frac = std::modf(lo_doub, &lo_int);
|
||||
|
||||
// Rolls lo into hi if necessary.
|
||||
int64_t lo64 = Round(lo_frac * kTicksPerSecond);
|
||||
|
||||
Duration ans;
|
||||
if (!SafeAddRepHi(hi_int, lo_int, &ans)) return ans;
|
||||
int64_t hi64 = time_internal::GetRepHi(ans);
|
||||
if (!SafeAddRepHi(hi64, lo64 / kTicksPerSecond, &ans)) return ans;
|
||||
hi64 = time_internal::GetRepHi(ans);
|
||||
lo64 %= kTicksPerSecond;
|
||||
NormalizeTicks(&hi64, &lo64);
|
||||
return time_internal::MakeDuration(hi64, lo64);
|
||||
}
|
||||
|
||||
// Tries to divide num by den as fast as possible by looking for common, easy
|
||||
// cases. If the division was done, the quotient is in *q and the remainder is
|
||||
// in *rem and true will be returned.
|
||||
inline bool IDivFastPath(const Duration num, const Duration den, int64_t* q,
|
||||
Duration* rem) {
|
||||
// Bail if num or den is an infinity.
|
||||
if (time_internal::IsInfiniteDuration(num) ||
|
||||
time_internal::IsInfiniteDuration(den))
|
||||
return false;
|
||||
|
||||
int64_t num_hi = time_internal::GetRepHi(num);
|
||||
uint32_t num_lo = time_internal::GetRepLo(num);
|
||||
int64_t den_hi = time_internal::GetRepHi(den);
|
||||
uint32_t den_lo = time_internal::GetRepLo(den);
|
||||
|
||||
if (den_hi == 0 && den_lo == kTicksPerNanosecond) {
|
||||
// Dividing by 1ns
|
||||
if (num_hi >= 0 && num_hi < (kint64max - kTicksPerSecond) / 1000000000) {
|
||||
*q = num_hi * 1000000000 + num_lo / kTicksPerNanosecond;
|
||||
*rem = time_internal::MakeDuration(0, num_lo % den_lo);
|
||||
return true;
|
||||
}
|
||||
} else if (den_hi == 0 && den_lo == 100 * kTicksPerNanosecond) {
|
||||
// Dividing by 100ns (common when converting to Universal time)
|
||||
if (num_hi >= 0 && num_hi < (kint64max - kTicksPerSecond) / 10000000) {
|
||||
*q = num_hi * 10000000 + num_lo / (100 * kTicksPerNanosecond);
|
||||
*rem = time_internal::MakeDuration(0, num_lo % den_lo);
|
||||
return true;
|
||||
}
|
||||
} else if (den_hi == 0 && den_lo == 1000 * kTicksPerNanosecond) {
|
||||
// Dividing by 1us
|
||||
if (num_hi >= 0 && num_hi < (kint64max - kTicksPerSecond) / 1000000) {
|
||||
*q = num_hi * 1000000 + num_lo / (1000 * kTicksPerNanosecond);
|
||||
*rem = time_internal::MakeDuration(0, num_lo % den_lo);
|
||||
return true;
|
||||
}
|
||||
} else if (den_hi == 0 && den_lo == 1000000 * kTicksPerNanosecond) {
|
||||
// Dividing by 1ms
|
||||
if (num_hi >= 0 && num_hi < (kint64max - kTicksPerSecond) / 1000) {
|
||||
*q = num_hi * 1000 + num_lo / (1000000 * kTicksPerNanosecond);
|
||||
*rem = time_internal::MakeDuration(0, num_lo % den_lo);
|
||||
return true;
|
||||
}
|
||||
} else if (den_hi > 0 && den_lo == 0) {
|
||||
// Dividing by positive multiple of 1s
|
||||
if (num_hi >= 0) {
|
||||
if (den_hi == 1) {
|
||||
*q = num_hi;
|
||||
*rem = time_internal::MakeDuration(0, num_lo);
|
||||
return true;
|
||||
}
|
||||
*q = num_hi / den_hi;
|
||||
*rem = time_internal::MakeDuration(num_hi % den_hi, num_lo);
|
||||
return true;
|
||||
}
|
||||
if (num_lo != 0) {
|
||||
num_hi += 1;
|
||||
}
|
||||
int64_t quotient = num_hi / den_hi;
|
||||
int64_t rem_sec = num_hi % den_hi;
|
||||
if (rem_sec > 0) {
|
||||
rem_sec -= den_hi;
|
||||
quotient += 1;
|
||||
}
|
||||
if (num_lo != 0) {
|
||||
rem_sec -= 1;
|
||||
}
|
||||
*q = quotient;
|
||||
*rem = time_internal::MakeDuration(rem_sec, num_lo);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace time_internal {
|
||||
|
||||
// The 'satq' argument indicates whether the quotient should saturate at the
|
||||
// bounds of int64_t. If it does saturate, the difference will spill over to
|
||||
// the remainder. If it does not saturate, the remainder remain accurate,
|
||||
// but the returned quotient will over/underflow int64_t and should not be used.
|
||||
int64_t IDivDuration(bool satq, const Duration num, const Duration den,
|
||||
Duration* rem) {
|
||||
int64_t q = 0;
|
||||
if (IDivFastPath(num, den, &q, rem)) {
|
||||
return q;
|
||||
}
|
||||
|
||||
const bool num_neg = num < ZeroDuration();
|
||||
const bool den_neg = den < ZeroDuration();
|
||||
const bool quotient_neg = num_neg != den_neg;
|
||||
|
||||
if (time_internal::IsInfiniteDuration(num) || den == ZeroDuration()) {
|
||||
*rem = num_neg ? -InfiniteDuration() : InfiniteDuration();
|
||||
return quotient_neg ? kint64min : kint64max;
|
||||
}
|
||||
if (time_internal::IsInfiniteDuration(den)) {
|
||||
*rem = num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint128 a = MakeU128Ticks(num);
|
||||
const uint128 b = MakeU128Ticks(den);
|
||||
uint128 quotient128 = a / b;
|
||||
|
||||
if (satq) {
|
||||
// Limits the quotient to the range of int64_t.
|
||||
if (quotient128 > uint128(static_cast<uint64_t>(kint64max))) {
|
||||
quotient128 = quotient_neg ? uint128(static_cast<uint64_t>(kint64min))
|
||||
: uint128(static_cast<uint64_t>(kint64max));
|
||||
}
|
||||
}
|
||||
|
||||
const uint128 remainder128 = a - quotient128 * b;
|
||||
*rem = MakeDurationFromU128(remainder128, num_neg);
|
||||
|
||||
if (!quotient_neg || quotient128 == 0) {
|
||||
return Uint128Low64(quotient128) & kint64max;
|
||||
}
|
||||
// The quotient needs to be negated, but we need to carefully handle
|
||||
// quotient128s with the top bit on.
|
||||
return -static_cast<int64_t>(Uint128Low64(quotient128 - 1) & kint64max) - 1;
|
||||
}
|
||||
|
||||
} // namespace time_internal
|
||||
|
||||
//
|
||||
// Additive operators.
|
||||
//
|
||||
|
||||
Duration& Duration::operator+=(Duration rhs) {
|
||||
if (time_internal::IsInfiniteDuration(*this)) return *this;
|
||||
if (time_internal::IsInfiniteDuration(rhs)) return *this = rhs;
|
||||
const int64_t orig_rep_hi = rep_hi_;
|
||||
rep_hi_ =
|
||||
DecodeTwosComp(EncodeTwosComp(rep_hi_) + EncodeTwosComp(rhs.rep_hi_));
|
||||
if (rep_lo_ >= kTicksPerSecond - rhs.rep_lo_) {
|
||||
rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_) + 1);
|
||||
rep_lo_ -= kTicksPerSecond;
|
||||
}
|
||||
rep_lo_ += rhs.rep_lo_;
|
||||
if (rhs.rep_hi_ < 0 ? rep_hi_ > orig_rep_hi : rep_hi_ < orig_rep_hi) {
|
||||
return *this = rhs.rep_hi_ < 0 ? -InfiniteDuration() : InfiniteDuration();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Duration& Duration::operator-=(Duration rhs) {
|
||||
if (time_internal::IsInfiniteDuration(*this)) return *this;
|
||||
if (time_internal::IsInfiniteDuration(rhs)) {
|
||||
return *this = rhs.rep_hi_ >= 0 ? -InfiniteDuration() : InfiniteDuration();
|
||||
}
|
||||
const int64_t orig_rep_hi = rep_hi_;
|
||||
rep_hi_ =
|
||||
DecodeTwosComp(EncodeTwosComp(rep_hi_) - EncodeTwosComp(rhs.rep_hi_));
|
||||
if (rep_lo_ < rhs.rep_lo_) {
|
||||
rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_) - 1);
|
||||
rep_lo_ += kTicksPerSecond;
|
||||
}
|
||||
rep_lo_ -= rhs.rep_lo_;
|
||||
if (rhs.rep_hi_ < 0 ? rep_hi_ < orig_rep_hi : rep_hi_ > orig_rep_hi) {
|
||||
return *this = rhs.rep_hi_ >= 0 ? -InfiniteDuration() : InfiniteDuration();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//
|
||||
// Multiplicative operators.
|
||||
//
|
||||
|
||||
Duration& Duration::operator*=(int64_t r) {
|
||||
if (time_internal::IsInfiniteDuration(*this)) {
|
||||
const bool is_neg = (r < 0) != (rep_hi_ < 0);
|
||||
return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
|
||||
}
|
||||
return *this = ScaleFixed<SafeMultiply>(*this, r);
|
||||
}
|
||||
|
||||
Duration& Duration::operator*=(double r) {
|
||||
if (time_internal::IsInfiniteDuration(*this) || !IsFinite(r)) {
|
||||
const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0);
|
||||
return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
|
||||
}
|
||||
return *this = ScaleDouble<std::multiplies>(*this, r);
|
||||
}
|
||||
|
||||
Duration& Duration::operator/=(int64_t r) {
|
||||
if (time_internal::IsInfiniteDuration(*this) || r == 0) {
|
||||
const bool is_neg = (r < 0) != (rep_hi_ < 0);
|
||||
return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
|
||||
}
|
||||
return *this = ScaleFixed<std::divides>(*this, r);
|
||||
}
|
||||
|
||||
Duration& Duration::operator/=(double r) {
|
||||
if (time_internal::IsInfiniteDuration(*this) || !IsValidDivisor(r)) {
|
||||
const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0);
|
||||
return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
|
||||
}
|
||||
return *this = ScaleDouble<std::divides>(*this, r);
|
||||
}
|
||||
|
||||
Duration& Duration::operator%=(Duration rhs) {
|
||||
time_internal::IDivDuration(false, *this, rhs, this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
double FDivDuration(Duration num, Duration den) {
|
||||
// Arithmetic with infinity is sticky.
|
||||
if (time_internal::IsInfiniteDuration(num) || den == ZeroDuration()) {
|
||||
return (num < ZeroDuration()) == (den < ZeroDuration())
|
||||
? std::numeric_limits<double>::infinity()
|
||||
: -std::numeric_limits<double>::infinity();
|
||||
}
|
||||
if (time_internal::IsInfiniteDuration(den)) return 0.0;
|
||||
|
||||
double a =
|
||||
static_cast<double>(time_internal::GetRepHi(num)) * kTicksPerSecond +
|
||||
time_internal::GetRepLo(num);
|
||||
double b =
|
||||
static_cast<double>(time_internal::GetRepHi(den)) * kTicksPerSecond +
|
||||
time_internal::GetRepLo(den);
|
||||
return a / b;
|
||||
}
|
||||
|
||||
//
|
||||
// Trunc/Floor/Ceil.
|
||||
//
|
||||
|
||||
Duration Trunc(Duration d, Duration unit) {
|
||||
return d - (d % unit);
|
||||
}
|
||||
|
||||
Duration Floor(const Duration d, const Duration unit) {
|
||||
const absl::Duration td = Trunc(d, unit);
|
||||
return td <= d ? td : td - AbsDuration(unit);
|
||||
}
|
||||
|
||||
Duration Ceil(const Duration d, const Duration unit) {
|
||||
const absl::Duration td = Trunc(d, unit);
|
||||
return td >= d ? td : td + AbsDuration(unit);
|
||||
}
|
||||
|
||||
//
|
||||
// Factory functions.
|
||||
//
|
||||
|
||||
Duration DurationFromTimespec(timespec ts) {
|
||||
if (static_cast<uint64_t>(ts.tv_nsec) < 1000 * 1000 * 1000) {
|
||||
int64_t ticks = ts.tv_nsec * kTicksPerNanosecond;
|
||||
return time_internal::MakeDuration(ts.tv_sec, ticks);
|
||||
}
|
||||
return Seconds(ts.tv_sec) + Nanoseconds(ts.tv_nsec);
|
||||
}
|
||||
|
||||
Duration DurationFromTimeval(timeval tv) {
|
||||
if (static_cast<uint64_t>(tv.tv_usec) < 1000 * 1000) {
|
||||
int64_t ticks = tv.tv_usec * 1000 * kTicksPerNanosecond;
|
||||
return time_internal::MakeDuration(tv.tv_sec, ticks);
|
||||
}
|
||||
return Seconds(tv.tv_sec) + Microseconds(tv.tv_usec);
|
||||
}
|
||||
|
||||
//
|
||||
// Conversion to other duration types.
|
||||
//
|
||||
|
||||
int64_t ToInt64Nanoseconds(Duration d) {
|
||||
if (time_internal::GetRepHi(d) >= 0 &&
|
||||
time_internal::GetRepHi(d) >> 33 == 0) {
|
||||
return (time_internal::GetRepHi(d) * 1000 * 1000 * 1000) +
|
||||
(time_internal::GetRepLo(d) / kTicksPerNanosecond);
|
||||
}
|
||||
return d / Nanoseconds(1);
|
||||
}
|
||||
int64_t ToInt64Microseconds(Duration d) {
|
||||
if (time_internal::GetRepHi(d) >= 0 &&
|
||||
time_internal::GetRepHi(d) >> 43 == 0) {
|
||||
return (time_internal::GetRepHi(d) * 1000 * 1000) +
|
||||
(time_internal::GetRepLo(d) / (kTicksPerNanosecond * 1000));
|
||||
}
|
||||
return d / Microseconds(1);
|
||||
}
|
||||
int64_t ToInt64Milliseconds(Duration d) {
|
||||
if (time_internal::GetRepHi(d) >= 0 &&
|
||||
time_internal::GetRepHi(d) >> 53 == 0) {
|
||||
return (time_internal::GetRepHi(d) * 1000) +
|
||||
(time_internal::GetRepLo(d) / (kTicksPerNanosecond * 1000 * 1000));
|
||||
}
|
||||
return d / Milliseconds(1);
|
||||
}
|
||||
int64_t ToInt64Seconds(Duration d) {
|
||||
int64_t hi = time_internal::GetRepHi(d);
|
||||
if (time_internal::IsInfiniteDuration(d)) return hi;
|
||||
if (hi < 0 && time_internal::GetRepLo(d) != 0) ++hi;
|
||||
return hi;
|
||||
}
|
||||
int64_t ToInt64Minutes(Duration d) {
|
||||
int64_t hi = time_internal::GetRepHi(d);
|
||||
if (time_internal::IsInfiniteDuration(d)) return hi;
|
||||
if (hi < 0 && time_internal::GetRepLo(d) != 0) ++hi;
|
||||
return hi / 60;
|
||||
}
|
||||
int64_t ToInt64Hours(Duration d) {
|
||||
int64_t hi = time_internal::GetRepHi(d);
|
||||
if (time_internal::IsInfiniteDuration(d)) return hi;
|
||||
if (hi < 0 && time_internal::GetRepLo(d) != 0) ++hi;
|
||||
return hi / (60 * 60);
|
||||
}
|
||||
|
||||
double ToDoubleNanoseconds(Duration d) {
|
||||
return FDivDuration(d, Nanoseconds(1));
|
||||
}
|
||||
double ToDoubleMicroseconds(Duration d) {
|
||||
return FDivDuration(d, Microseconds(1));
|
||||
}
|
||||
double ToDoubleMilliseconds(Duration d) {
|
||||
return FDivDuration(d, Milliseconds(1));
|
||||
}
|
||||
double ToDoubleSeconds(Duration d) {
|
||||
return FDivDuration(d, Seconds(1));
|
||||
}
|
||||
double ToDoubleMinutes(Duration d) {
|
||||
return FDivDuration(d, Minutes(1));
|
||||
}
|
||||
double ToDoubleHours(Duration d) {
|
||||
return FDivDuration(d, Hours(1));
|
||||
}
|
||||
|
||||
timespec ToTimespec(Duration d) {
|
||||
timespec ts;
|
||||
if (!time_internal::IsInfiniteDuration(d)) {
|
||||
int64_t rep_hi = time_internal::GetRepHi(d);
|
||||
uint32_t rep_lo = time_internal::GetRepLo(d);
|
||||
if (rep_hi < 0) {
|
||||
// Tweak the fields so that unsigned division of rep_lo
|
||||
// maps to truncation (towards zero) for the timespec.
|
||||
rep_lo += kTicksPerNanosecond - 1;
|
||||
if (rep_lo >= kTicksPerSecond) {
|
||||
rep_hi += 1;
|
||||
rep_lo -= kTicksPerSecond;
|
||||
}
|
||||
}
|
||||
ts.tv_sec = rep_hi;
|
||||
if (ts.tv_sec == rep_hi) { // no time_t narrowing
|
||||
ts.tv_nsec = rep_lo / kTicksPerNanosecond;
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
if (d >= ZeroDuration()) {
|
||||
ts.tv_sec = std::numeric_limits<time_t>::max();
|
||||
ts.tv_nsec = 1000 * 1000 * 1000 - 1;
|
||||
} else {
|
||||
ts.tv_sec = std::numeric_limits<time_t>::min();
|
||||
ts.tv_nsec = 0;
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
||||
timeval ToTimeval(Duration d) {
|
||||
timeval tv;
|
||||
timespec ts = ToTimespec(d);
|
||||
if (ts.tv_sec < 0) {
|
||||
// Tweak the fields so that positive division of tv_nsec
|
||||
// maps to truncation (towards zero) for the timeval.
|
||||
ts.tv_nsec += 1000 - 1;
|
||||
if (ts.tv_nsec >= 1000 * 1000 * 1000) {
|
||||
ts.tv_sec += 1;
|
||||
ts.tv_nsec -= 1000 * 1000 * 1000;
|
||||
}
|
||||
}
|
||||
tv.tv_sec = ts.tv_sec;
|
||||
if (tv.tv_sec != ts.tv_sec) { // narrowing
|
||||
if (ts.tv_sec < 0) {
|
||||
tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::min();
|
||||
tv.tv_usec = 0;
|
||||
} else {
|
||||
tv.tv_sec = std::numeric_limits<decltype(tv.tv_sec)>::max();
|
||||
tv.tv_usec = 1000 * 1000 - 1;
|
||||
}
|
||||
return tv;
|
||||
}
|
||||
tv.tv_usec = static_cast<int>(ts.tv_nsec / 1000); // suseconds_t
|
||||
return tv;
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds ToChronoNanoseconds(Duration d) {
|
||||
return time_internal::ToChronoDuration<std::chrono::nanoseconds>(d);
|
||||
}
|
||||
std::chrono::microseconds ToChronoMicroseconds(Duration d) {
|
||||
return time_internal::ToChronoDuration<std::chrono::microseconds>(d);
|
||||
}
|
||||
std::chrono::milliseconds ToChronoMilliseconds(Duration d) {
|
||||
return time_internal::ToChronoDuration<std::chrono::milliseconds>(d);
|
||||
}
|
||||
std::chrono::seconds ToChronoSeconds(Duration d) {
|
||||
return time_internal::ToChronoDuration<std::chrono::seconds>(d);
|
||||
}
|
||||
std::chrono::minutes ToChronoMinutes(Duration d) {
|
||||
return time_internal::ToChronoDuration<std::chrono::minutes>(d);
|
||||
}
|
||||
std::chrono::hours ToChronoHours(Duration d) {
|
||||
return time_internal::ToChronoDuration<std::chrono::hours>(d);
|
||||
}
|
||||
|
||||
//
|
||||
// To/From string formatting.
|
||||
//
|
||||
|
||||
namespace {
|
||||
|
||||
// Formats a positive 64-bit integer in the given field width. Note that
|
||||
// it is up to the caller of Format64() to ensure that there is sufficient
|
||||
// space before ep to hold the conversion.
|
||||
char* Format64(char* ep, int width, int64_t v) {
|
||||
do {
|
||||
--width;
|
||||
*--ep = '0' + (v % 10); // contiguous digits
|
||||
} while (v /= 10);
|
||||
while (--width >= 0) *--ep = '0'; // zero pad
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Helpers for FormatDuration() that format 'n' and append it to 'out'
|
||||
// followed by the given 'unit'. If 'n' formats to "0", nothing is
|
||||
// appended (not even the unit).
|
||||
|
||||
// A type that encapsulates how to display a value of a particular unit. For
|
||||
// values that are displayed with fractional parts, the precision indicates
|
||||
// where to round the value. The precision varies with the display unit because
|
||||
// a Duration can hold only quarters of a nanosecond, so displaying information
|
||||
// beyond that is just noise.
|
||||
//
|
||||
// For example, a microsecond value of 42.00025xxxxx should not display beyond 5
|
||||
// fractional digits, because it is in the noise of what a Duration can
|
||||
// represent.
|
||||
struct DisplayUnit {
|
||||
const char* abbr;
|
||||
int prec;
|
||||
double pow10;
|
||||
};
|
||||
const DisplayUnit kDisplayNano = {"ns", 2, 1e2};
|
||||
const DisplayUnit kDisplayMicro = {"us", 5, 1e5};
|
||||
const DisplayUnit kDisplayMilli = {"ms", 8, 1e8};
|
||||
const DisplayUnit kDisplaySec = {"s", 11, 1e11};
|
||||
const DisplayUnit kDisplayMin = {"m", -1, 0.0}; // prec ignored
|
||||
const DisplayUnit kDisplayHour = {"h", -1, 0.0}; // prec ignored
|
||||
|
||||
void AppendNumberUnit(std::string* out, int64_t n, DisplayUnit unit) {
|
||||
char buf[sizeof("2562047788015216")]; // hours in max duration
|
||||
char* const ep = buf + sizeof(buf);
|
||||
char* bp = Format64(ep, 0, n);
|
||||
if (*bp != '0' || bp + 1 != ep) {
|
||||
out->append(bp, ep - bp);
|
||||
out->append(unit.abbr);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: unit.prec is limited to double's digits10 value (typically 15) so it
|
||||
// always fits in buf[].
|
||||
void AppendNumberUnit(std::string* out, double n, DisplayUnit unit) {
|
||||
constexpr int kBufferSize = std::numeric_limits<double>::digits10;
|
||||
const int prec = std::min(kBufferSize, unit.prec);
|
||||
char buf[kBufferSize]; // also large enough to hold integer part
|
||||
char* ep = buf + sizeof(buf);
|
||||
double d = 0;
|
||||
int64_t frac_part = Round(std::modf(n, &d) * unit.pow10);
|
||||
int64_t int_part = d;
|
||||
if (int_part != 0 || frac_part != 0) {
|
||||
char* bp = Format64(ep, 0, int_part); // always < 1000
|
||||
out->append(bp, ep - bp);
|
||||
if (frac_part != 0) {
|
||||
out->push_back('.');
|
||||
bp = Format64(ep, prec, frac_part);
|
||||
while (ep[-1] == '0') --ep;
|
||||
out->append(bp, ep - bp);
|
||||
}
|
||||
out->append(unit.abbr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// From Go's doc at https://golang.org/pkg/time/#Duration.String
|
||||
// [FormatDuration] returns a string representing the duration in the
|
||||
// form "72h3m0.5s". Leading zero units are omitted. As a special
|
||||
// case, durations less than one second format use a smaller unit
|
||||
// (milli-, micro-, or nanoseconds) to ensure that the leading digit
|
||||
// is non-zero. The zero duration formats as 0, with no unit.
|
||||
std::string FormatDuration(Duration d) {
|
||||
const Duration min_duration = Seconds(kint64min);
|
||||
if (d == min_duration) {
|
||||
// Avoid needing to negate kint64min by directly returning what the
|
||||
// following code should produce in that case.
|
||||
return "-2562047788015215h30m8s";
|
||||
}
|
||||
std::string s;
|
||||
if (d < ZeroDuration()) {
|
||||
s.append("-");
|
||||
d = -d;
|
||||
}
|
||||
if (d == InfiniteDuration()) {
|
||||
s.append("inf");
|
||||
} else if (d < Seconds(1)) {
|
||||
// Special case for durations with a magnitude < 1 second. The duration
|
||||
// is printed as a fraction of a single unit, e.g., "1.2ms".
|
||||
if (d < Microseconds(1)) {
|
||||
AppendNumberUnit(&s, FDivDuration(d, Nanoseconds(1)), kDisplayNano);
|
||||
} else if (d < Milliseconds(1)) {
|
||||
AppendNumberUnit(&s, FDivDuration(d, Microseconds(1)), kDisplayMicro);
|
||||
} else {
|
||||
AppendNumberUnit(&s, FDivDuration(d, Milliseconds(1)), kDisplayMilli);
|
||||
}
|
||||
} else {
|
||||
AppendNumberUnit(&s, IDivDuration(d, Hours(1), &d), kDisplayHour);
|
||||
AppendNumberUnit(&s, IDivDuration(d, Minutes(1), &d), kDisplayMin);
|
||||
AppendNumberUnit(&s, FDivDuration(d, Seconds(1)), kDisplaySec);
|
||||
}
|
||||
if (s.empty() || s == "-") {
|
||||
s = "0";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A helper for ParseDuration() that parses a leading number from the given
|
||||
// string and stores the result in *int_part/*frac_part/*frac_scale. The
|
||||
// given string pointer is modified to point to the first unconsumed char.
|
||||
bool ConsumeDurationNumber(const char** dpp, const char* ep, int64_t* int_part,
|
||||
int64_t* frac_part, int64_t* frac_scale) {
|
||||
*int_part = 0;
|
||||
*frac_part = 0;
|
||||
*frac_scale = 1; // invariant: *frac_part < *frac_scale
|
||||
const char* start = *dpp;
|
||||
for (; *dpp != ep; *dpp += 1) {
|
||||
const int d = **dpp - '0'; // contiguous digits
|
||||
if (d < 0 || 10 <= d) break;
|
||||
|
||||
if (*int_part > kint64max / 10) return false;
|
||||
*int_part *= 10;
|
||||
if (*int_part > kint64max - d) return false;
|
||||
*int_part += d;
|
||||
}
|
||||
const bool int_part_empty = (*dpp == start);
|
||||
if (*dpp == ep || **dpp != '.') return !int_part_empty;
|
||||
|
||||
for (*dpp += 1; *dpp != ep; *dpp += 1) {
|
||||
const int d = **dpp - '0'; // contiguous digits
|
||||
if (d < 0 || 10 <= d) break;
|
||||
if (*frac_scale <= kint64max / 10) {
|
||||
*frac_part *= 10;
|
||||
*frac_part += d;
|
||||
*frac_scale *= 10;
|
||||
}
|
||||
}
|
||||
return !int_part_empty || *frac_scale != 1;
|
||||
}
|
||||
|
||||
// A helper for ParseDuration() that parses a leading unit designator (e.g.,
|
||||
// ns, us, ms, s, m, h) from the given string and stores the resulting unit
|
||||
// in "*unit". The given string pointer is modified to point to the first
|
||||
// unconsumed char.
|
||||
bool ConsumeDurationUnit(const char** start, const char* end, Duration* unit) {
|
||||
size_t size = end - *start;
|
||||
switch (size) {
|
||||
case 0:
|
||||
return false;
|
||||
default:
|
||||
switch (**start) {
|
||||
case 'n':
|
||||
if (*(*start + 1) == 's') {
|
||||
*start += 2;
|
||||
*unit = Nanoseconds(1);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (*(*start + 1) == 's') {
|
||||
*start += 2;
|
||||
*unit = Microseconds(1);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (*(*start + 1) == 's') {
|
||||
*start += 2;
|
||||
*unit = Milliseconds(1);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ABSL_FALLTHROUGH_INTENDED;
|
||||
case 1:
|
||||
switch (**start) {
|
||||
case 's':
|
||||
*unit = Seconds(1);
|
||||
*start += 1;
|
||||
return true;
|
||||
case 'm':
|
||||
*unit = Minutes(1);
|
||||
*start += 1;
|
||||
return true;
|
||||
case 'h':
|
||||
*unit = Hours(1);
|
||||
*start += 1;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// From Go's doc at https://golang.org/pkg/time/#ParseDuration
|
||||
// [ParseDuration] parses a duration string. A duration string is
|
||||
// a possibly signed sequence of decimal numbers, each with optional
|
||||
// fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m".
|
||||
// Valid time units are "ns", "us" "ms", "s", "m", "h".
|
||||
bool ParseDuration(absl::string_view dur_sv, Duration* d) {
|
||||
int sign = 1;
|
||||
if (absl::ConsumePrefix(&dur_sv, "-")) {
|
||||
sign = -1;
|
||||
} else {
|
||||
absl::ConsumePrefix(&dur_sv, "+");
|
||||
}
|
||||
if (dur_sv.empty()) return false;
|
||||
|
||||
// Special case for a string of "0".
|
||||
if (dur_sv == "0") {
|
||||
*d = ZeroDuration();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dur_sv == "inf") {
|
||||
*d = sign * InfiniteDuration();
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* start = dur_sv.data();
|
||||
const char* end = start + dur_sv.size();
|
||||
|
||||
Duration dur;
|
||||
while (start != end) {
|
||||
int64_t int_part;
|
||||
int64_t frac_part;
|
||||
int64_t frac_scale;
|
||||
Duration unit;
|
||||
if (!ConsumeDurationNumber(&start, end, &int_part, &frac_part,
|
||||
&frac_scale) ||
|
||||
!ConsumeDurationUnit(&start, end, &unit)) {
|
||||
return false;
|
||||
}
|
||||
if (int_part != 0) dur += sign * int_part * unit;
|
||||
if (frac_part != 0) dur += sign * frac_part * unit / frac_scale;
|
||||
}
|
||||
*d = dur;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbslParseFlag(absl::string_view text, Duration* dst, std::string*) {
|
||||
return ParseDuration(text, dst);
|
||||
}
|
||||
|
||||
std::string AbslUnparseFlag(Duration d) { return FormatDuration(d); }
|
||||
bool ParseFlag(const std::string& text, Duration* dst, std::string* ) {
|
||||
return ParseDuration(text, dst);
|
||||
}
|
||||
|
||||
std::string UnparseFlag(Duration d) { return FormatDuration(d); }
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
428
third_party/abseil_cpp/absl/time/duration_benchmark.cc
vendored
Normal file
428
third_party/abseil_cpp/absl/time/duration_benchmark.cc
vendored
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
//
|
||||
// Factory functions
|
||||
//
|
||||
|
||||
void BM_Duration_Factory_Nanoseconds(benchmark::State& state) {
|
||||
int64_t i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Nanoseconds(i));
|
||||
i += 314159;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_Nanoseconds);
|
||||
|
||||
void BM_Duration_Factory_Microseconds(benchmark::State& state) {
|
||||
int64_t i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Microseconds(i));
|
||||
i += 314;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_Microseconds);
|
||||
|
||||
void BM_Duration_Factory_Milliseconds(benchmark::State& state) {
|
||||
int64_t i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Milliseconds(i));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_Milliseconds);
|
||||
|
||||
void BM_Duration_Factory_Seconds(benchmark::State& state) {
|
||||
int64_t i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Seconds(i));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_Seconds);
|
||||
|
||||
void BM_Duration_Factory_Minutes(benchmark::State& state) {
|
||||
int64_t i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Minutes(i));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_Minutes);
|
||||
|
||||
void BM_Duration_Factory_Hours(benchmark::State& state) {
|
||||
int64_t i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Hours(i));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_Hours);
|
||||
|
||||
void BM_Duration_Factory_DoubleNanoseconds(benchmark::State& state) {
|
||||
double d = 1;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Nanoseconds(d));
|
||||
d = d * 1.00000001 + 1;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_DoubleNanoseconds);
|
||||
|
||||
void BM_Duration_Factory_DoubleMicroseconds(benchmark::State& state) {
|
||||
double d = 1e-3;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Microseconds(d));
|
||||
d = d * 1.00000001 + 1e-3;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_DoubleMicroseconds);
|
||||
|
||||
void BM_Duration_Factory_DoubleMilliseconds(benchmark::State& state) {
|
||||
double d = 1e-6;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Milliseconds(d));
|
||||
d = d * 1.00000001 + 1e-6;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_DoubleMilliseconds);
|
||||
|
||||
void BM_Duration_Factory_DoubleSeconds(benchmark::State& state) {
|
||||
double d = 1e-9;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Seconds(d));
|
||||
d = d * 1.00000001 + 1e-9;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_DoubleSeconds);
|
||||
|
||||
void BM_Duration_Factory_DoubleMinutes(benchmark::State& state) {
|
||||
double d = 1e-9;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Minutes(d));
|
||||
d = d * 1.00000001 + 1e-9;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_DoubleMinutes);
|
||||
|
||||
void BM_Duration_Factory_DoubleHours(benchmark::State& state) {
|
||||
double d = 1e-9;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Hours(d));
|
||||
d = d * 1.00000001 + 1e-9;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Factory_DoubleHours);
|
||||
|
||||
//
|
||||
// Arithmetic
|
||||
//
|
||||
|
||||
void BM_Duration_Addition(benchmark::State& state) {
|
||||
absl::Duration d = absl::Nanoseconds(1);
|
||||
absl::Duration step = absl::Milliseconds(1);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(d += step);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Addition);
|
||||
|
||||
void BM_Duration_Subtraction(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(std::numeric_limits<int64_t>::max());
|
||||
absl::Duration step = absl::Milliseconds(1);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(d -= step);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Subtraction);
|
||||
|
||||
void BM_Duration_Multiplication_Fixed(benchmark::State& state) {
|
||||
absl::Duration d = absl::Milliseconds(1);
|
||||
absl::Duration s;
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(s += d * (i + 1));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Multiplication_Fixed);
|
||||
|
||||
void BM_Duration_Multiplication_Double(benchmark::State& state) {
|
||||
absl::Duration d = absl::Milliseconds(1);
|
||||
absl::Duration s;
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(s += d * (i + 1.0));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Multiplication_Double);
|
||||
|
||||
void BM_Duration_Division_Fixed(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(1);
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(d /= i + 1);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Division_Fixed);
|
||||
|
||||
void BM_Duration_Division_Double(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(1);
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(d /= i + 1.0);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Division_Double);
|
||||
|
||||
void BM_Duration_FDivDuration_Nanoseconds(benchmark::State& state) {
|
||||
double d = 1;
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(
|
||||
d += absl::FDivDuration(absl::Milliseconds(i), absl::Nanoseconds(1)));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_FDivDuration_Nanoseconds);
|
||||
|
||||
void BM_Duration_IDivDuration_Nanoseconds(benchmark::State& state) {
|
||||
int64_t a = 1;
|
||||
absl::Duration ignore;
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(a +=
|
||||
absl::IDivDuration(absl::Nanoseconds(i),
|
||||
absl::Nanoseconds(1), &ignore));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_IDivDuration_Nanoseconds);
|
||||
|
||||
void BM_Duration_IDivDuration_Microseconds(benchmark::State& state) {
|
||||
int64_t a = 1;
|
||||
absl::Duration ignore;
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(a += absl::IDivDuration(absl::Microseconds(i),
|
||||
absl::Microseconds(1),
|
||||
&ignore));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_IDivDuration_Microseconds);
|
||||
|
||||
void BM_Duration_IDivDuration_Milliseconds(benchmark::State& state) {
|
||||
int64_t a = 1;
|
||||
absl::Duration ignore;
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(a += absl::IDivDuration(absl::Milliseconds(i),
|
||||
absl::Milliseconds(1),
|
||||
&ignore));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_IDivDuration_Milliseconds);
|
||||
|
||||
void BM_Duration_IDivDuration_Seconds(benchmark::State& state) {
|
||||
int64_t a = 1;
|
||||
absl::Duration ignore;
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(
|
||||
a += absl::IDivDuration(absl::Seconds(i), absl::Seconds(1), &ignore));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_IDivDuration_Seconds);
|
||||
|
||||
void BM_Duration_IDivDuration_Minutes(benchmark::State& state) {
|
||||
int64_t a = 1;
|
||||
absl::Duration ignore;
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(
|
||||
a += absl::IDivDuration(absl::Minutes(i), absl::Minutes(1), &ignore));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_IDivDuration_Minutes);
|
||||
|
||||
void BM_Duration_IDivDuration_Hours(benchmark::State& state) {
|
||||
int64_t a = 1;
|
||||
absl::Duration ignore;
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(
|
||||
a += absl::IDivDuration(absl::Hours(i), absl::Hours(1), &ignore));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_IDivDuration_Hours);
|
||||
|
||||
void BM_Duration_ToInt64Nanoseconds(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(100000);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToInt64Nanoseconds(d));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_ToInt64Nanoseconds);
|
||||
|
||||
void BM_Duration_ToInt64Microseconds(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(100000);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToInt64Microseconds(d));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_ToInt64Microseconds);
|
||||
|
||||
void BM_Duration_ToInt64Milliseconds(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(100000);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToInt64Milliseconds(d));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_ToInt64Milliseconds);
|
||||
|
||||
void BM_Duration_ToInt64Seconds(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(100000);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToInt64Seconds(d));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_ToInt64Seconds);
|
||||
|
||||
void BM_Duration_ToInt64Minutes(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(100000);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToInt64Minutes(d));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_ToInt64Minutes);
|
||||
|
||||
void BM_Duration_ToInt64Hours(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(100000);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToInt64Hours(d));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_ToInt64Hours);
|
||||
|
||||
//
|
||||
// To/FromTimespec
|
||||
//
|
||||
|
||||
void BM_Duration_ToTimespec_AbslTime(benchmark::State& state) {
|
||||
absl::Duration d = absl::Seconds(1);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToTimespec(d));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_ToTimespec_AbslTime);
|
||||
|
||||
ABSL_ATTRIBUTE_NOINLINE timespec DoubleToTimespec(double seconds) {
|
||||
timespec ts;
|
||||
ts.tv_sec = seconds;
|
||||
ts.tv_nsec = (seconds - ts.tv_sec) * (1000 * 1000 * 1000);
|
||||
return ts;
|
||||
}
|
||||
|
||||
void BM_Duration_ToTimespec_Double(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(DoubleToTimespec(1.0));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_ToTimespec_Double);
|
||||
|
||||
void BM_Duration_FromTimespec_AbslTime(benchmark::State& state) {
|
||||
timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 0;
|
||||
while (state.KeepRunning()) {
|
||||
if (++ts.tv_nsec == 1000 * 1000 * 1000) {
|
||||
++ts.tv_sec;
|
||||
ts.tv_nsec = 0;
|
||||
}
|
||||
benchmark::DoNotOptimize(absl::DurationFromTimespec(ts));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_FromTimespec_AbslTime);
|
||||
|
||||
ABSL_ATTRIBUTE_NOINLINE double TimespecToDouble(timespec ts) {
|
||||
return ts.tv_sec + (ts.tv_nsec / (1000 * 1000 * 1000));
|
||||
}
|
||||
|
||||
void BM_Duration_FromTimespec_Double(benchmark::State& state) {
|
||||
timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 0;
|
||||
while (state.KeepRunning()) {
|
||||
if (++ts.tv_nsec == 1000 * 1000 * 1000) {
|
||||
++ts.tv_sec;
|
||||
ts.tv_nsec = 0;
|
||||
}
|
||||
benchmark::DoNotOptimize(TimespecToDouble(ts));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_FromTimespec_Double);
|
||||
|
||||
//
|
||||
// String conversions
|
||||
//
|
||||
|
||||
const char* const kDurations[] = {
|
||||
"0", // 0
|
||||
"123ns", // 1
|
||||
"1h2m3s", // 2
|
||||
"-2h3m4.005006007s", // 3
|
||||
"2562047788015215h30m7.99999999975s", // 4
|
||||
};
|
||||
const int kNumDurations = sizeof(kDurations) / sizeof(kDurations[0]);
|
||||
|
||||
void BM_Duration_FormatDuration(benchmark::State& state) {
|
||||
const std::string s = kDurations[state.range(0)];
|
||||
state.SetLabel(s);
|
||||
absl::Duration d;
|
||||
absl::ParseDuration(kDurations[state.range(0)], &d);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::FormatDuration(d));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_FormatDuration)->DenseRange(0, kNumDurations - 1);
|
||||
|
||||
void BM_Duration_ParseDuration(benchmark::State& state) {
|
||||
const std::string s = kDurations[state.range(0)];
|
||||
state.SetLabel(s);
|
||||
absl::Duration d;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ParseDuration(s, &d));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_ParseDuration)->DenseRange(0, kNumDurations - 1);
|
||||
|
||||
} // namespace
|
||||
1808
third_party/abseil_cpp/absl/time/duration_test.cc
vendored
Normal file
1808
third_party/abseil_cpp/absl/time/duration_test.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
163
third_party/abseil_cpp/absl/time/format.cc
vendored
Normal file
163
third_party/abseil_cpp/absl/time/format.cc
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace cctz = absl::time_internal::cctz;
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
ABSL_DLL extern const char RFC3339_full[] =
|
||||
"%Y-%m-%dT%H:%M:%E*S%Ez";
|
||||
ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%dT%H:%M:%S%Ez";
|
||||
|
||||
ABSL_DLL extern const char RFC1123_full[] =
|
||||
"%a, %d %b %E4Y %H:%M:%S %z";
|
||||
ABSL_DLL extern const char RFC1123_no_wday[] =
|
||||
"%d %b %E4Y %H:%M:%S %z";
|
||||
|
||||
namespace {
|
||||
|
||||
const char kInfiniteFutureStr[] = "infinite-future";
|
||||
const char kInfinitePastStr[] = "infinite-past";
|
||||
|
||||
struct cctz_parts {
|
||||
cctz::time_point<cctz::seconds> sec;
|
||||
cctz::detail::femtoseconds fem;
|
||||
};
|
||||
|
||||
inline cctz::time_point<cctz::seconds> unix_epoch() {
|
||||
return std::chrono::time_point_cast<cctz::seconds>(
|
||||
std::chrono::system_clock::from_time_t(0));
|
||||
}
|
||||
|
||||
// Splits a Time into seconds and femtoseconds, which can be used with CCTZ.
|
||||
// Requires that 't' is finite. See duration.cc for details about rep_hi and
|
||||
// rep_lo.
|
||||
cctz_parts Split(absl::Time t) {
|
||||
const auto d = time_internal::ToUnixDuration(t);
|
||||
const int64_t rep_hi = time_internal::GetRepHi(d);
|
||||
const int64_t rep_lo = time_internal::GetRepLo(d);
|
||||
const auto sec = unix_epoch() + cctz::seconds(rep_hi);
|
||||
const auto fem = cctz::detail::femtoseconds(rep_lo * (1000 * 1000 / 4));
|
||||
return {sec, fem};
|
||||
}
|
||||
|
||||
// Joins the given seconds and femtoseconds into a Time. See duration.cc for
|
||||
// details about rep_hi and rep_lo.
|
||||
absl::Time Join(const cctz_parts& parts) {
|
||||
const int64_t rep_hi = (parts.sec - unix_epoch()).count();
|
||||
const uint32_t rep_lo = parts.fem.count() / (1000 * 1000 / 4);
|
||||
const auto d = time_internal::MakeDuration(rep_hi, rep_lo);
|
||||
return time_internal::FromUnixDuration(d);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string FormatTime(absl::string_view format, absl::Time t,
|
||||
absl::TimeZone tz) {
|
||||
if (t == absl::InfiniteFuture()) return std::string(kInfiniteFutureStr);
|
||||
if (t == absl::InfinitePast()) return std::string(kInfinitePastStr);
|
||||
const auto parts = Split(t);
|
||||
return cctz::detail::format(std::string(format), parts.sec, parts.fem,
|
||||
cctz::time_zone(tz));
|
||||
}
|
||||
|
||||
std::string FormatTime(absl::Time t, absl::TimeZone tz) {
|
||||
return FormatTime(RFC3339_full, t, tz);
|
||||
}
|
||||
|
||||
std::string FormatTime(absl::Time t) {
|
||||
return absl::FormatTime(RFC3339_full, t, absl::LocalTimeZone());
|
||||
}
|
||||
|
||||
bool ParseTime(absl::string_view format, absl::string_view input,
|
||||
absl::Time* time, std::string* err) {
|
||||
return absl::ParseTime(format, input, absl::UTCTimeZone(), time, err);
|
||||
}
|
||||
|
||||
// If the input string does not contain an explicit UTC offset, interpret
|
||||
// the fields with respect to the given TimeZone.
|
||||
bool ParseTime(absl::string_view format, absl::string_view input,
|
||||
absl::TimeZone tz, absl::Time* time, std::string* err) {
|
||||
auto strip_leading_space = [](absl::string_view* sv) {
|
||||
while (!sv->empty()) {
|
||||
if (!std::isspace(sv->front())) return;
|
||||
sv->remove_prefix(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Portable toolchains means we don't get nice constexpr here.
|
||||
struct Literal {
|
||||
const char* name;
|
||||
size_t size;
|
||||
absl::Time value;
|
||||
};
|
||||
static Literal literals[] = {
|
||||
{kInfiniteFutureStr, strlen(kInfiniteFutureStr), InfiniteFuture()},
|
||||
{kInfinitePastStr, strlen(kInfinitePastStr), InfinitePast()},
|
||||
};
|
||||
strip_leading_space(&input);
|
||||
for (const auto& lit : literals) {
|
||||
if (absl::StartsWith(input, absl::string_view(lit.name, lit.size))) {
|
||||
absl::string_view tail = input;
|
||||
tail.remove_prefix(lit.size);
|
||||
strip_leading_space(&tail);
|
||||
if (tail.empty()) {
|
||||
*time = lit.value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string error;
|
||||
cctz_parts parts;
|
||||
const bool b =
|
||||
cctz::detail::parse(std::string(format), std::string(input),
|
||||
cctz::time_zone(tz), &parts.sec, &parts.fem, &error);
|
||||
if (b) {
|
||||
*time = Join(parts);
|
||||
} else if (err != nullptr) {
|
||||
*err = error;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// Functions required to support absl::Time flags.
|
||||
bool AbslParseFlag(absl::string_view text, absl::Time* t, std::string* error) {
|
||||
return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error);
|
||||
}
|
||||
|
||||
std::string AbslUnparseFlag(absl::Time t) {
|
||||
return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone());
|
||||
}
|
||||
bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) {
|
||||
return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error);
|
||||
}
|
||||
|
||||
std::string UnparseFlag(absl::Time t) {
|
||||
return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone());
|
||||
}
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
64
third_party/abseil_cpp/absl/time/format_benchmark.cc
vendored
Normal file
64
third_party/abseil_cpp/absl/time/format_benchmark.cc
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include "absl/time/internal/test_util.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
namespace {
|
||||
const char* const kFormats[] = {
|
||||
absl::RFC1123_full, // 0
|
||||
absl::RFC1123_no_wday, // 1
|
||||
absl::RFC3339_full, // 2
|
||||
absl::RFC3339_sec, // 3
|
||||
"%Y-%m-%dT%H:%M:%S", // 4
|
||||
"%Y-%m-%d", // 5
|
||||
};
|
||||
const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
|
||||
} // namespace
|
||||
|
||||
void BM_Format_FormatTime(benchmark::State& state) {
|
||||
const std::string fmt = kFormats[state.range(0)];
|
||||
state.SetLabel(fmt);
|
||||
const absl::TimeZone lax =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
const absl::Time t =
|
||||
absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax) +
|
||||
absl::Nanoseconds(1);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::FormatTime(fmt, t, lax).length());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Format_FormatTime)->DenseRange(0, kNumFormats - 1);
|
||||
|
||||
void BM_Format_ParseTime(benchmark::State& state) {
|
||||
const std::string fmt = kFormats[state.range(0)];
|
||||
state.SetLabel(fmt);
|
||||
const absl::TimeZone lax =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
absl::Time t = absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax) +
|
||||
absl::Nanoseconds(1);
|
||||
const std::string when = absl::FormatTime(fmt, t, lax);
|
||||
std::string err;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ParseTime(fmt, when, lax, &t, &err));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Format_ParseTime)->DenseRange(0, kNumFormats - 1);
|
||||
|
||||
} // namespace
|
||||
441
third_party/abseil_cpp/absl/time/format_test.cc
vendored
Normal file
441
third_party/abseil_cpp/absl/time/format_test.cc
vendored
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/internal/test_util.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
using testing::HasSubstr;
|
||||
|
||||
namespace {
|
||||
|
||||
// A helper that tests the given format specifier by itself, and with leading
|
||||
// and trailing characters. For example: TestFormatSpecifier(t, "%a", "Thu").
|
||||
void TestFormatSpecifier(absl::Time t, absl::TimeZone tz,
|
||||
const std::string& fmt, const std::string& ans) {
|
||||
EXPECT_EQ(ans, absl::FormatTime(fmt, t, tz));
|
||||
EXPECT_EQ("xxx " + ans, absl::FormatTime("xxx " + fmt, t, tz));
|
||||
EXPECT_EQ(ans + " yyy", absl::FormatTime(fmt + " yyy", t, tz));
|
||||
EXPECT_EQ("xxx " + ans + " yyy",
|
||||
absl::FormatTime("xxx " + fmt + " yyy", t, tz));
|
||||
}
|
||||
|
||||
//
|
||||
// Testing FormatTime()
|
||||
//
|
||||
|
||||
TEST(FormatTime, Basics) {
|
||||
absl::TimeZone tz = absl::UTCTimeZone();
|
||||
absl::Time t = absl::FromTimeT(0);
|
||||
|
||||
// Starts with a couple basic edge cases.
|
||||
EXPECT_EQ("", absl::FormatTime("", t, tz));
|
||||
EXPECT_EQ(" ", absl::FormatTime(" ", t, tz));
|
||||
EXPECT_EQ(" ", absl::FormatTime(" ", t, tz));
|
||||
EXPECT_EQ("xxx", absl::FormatTime("xxx", t, tz));
|
||||
std::string big(128, 'x');
|
||||
EXPECT_EQ(big, absl::FormatTime(big, t, tz));
|
||||
// Cause the 1024-byte buffer to grow.
|
||||
std::string bigger(100000, 'x');
|
||||
EXPECT_EQ(bigger, absl::FormatTime(bigger, t, tz));
|
||||
|
||||
t += absl::Hours(13) + absl::Minutes(4) + absl::Seconds(5);
|
||||
t += absl::Milliseconds(6) + absl::Microseconds(7) + absl::Nanoseconds(8);
|
||||
EXPECT_EQ("1970-01-01", absl::FormatTime("%Y-%m-%d", t, tz));
|
||||
EXPECT_EQ("13:04:05", absl::FormatTime("%H:%M:%S", t, tz));
|
||||
EXPECT_EQ("13:04:05.006", absl::FormatTime("%H:%M:%E3S", t, tz));
|
||||
EXPECT_EQ("13:04:05.006007", absl::FormatTime("%H:%M:%E6S", t, tz));
|
||||
EXPECT_EQ("13:04:05.006007008", absl::FormatTime("%H:%M:%E9S", t, tz));
|
||||
}
|
||||
|
||||
TEST(FormatTime, LocaleSpecific) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
absl::Time t = absl::FromTimeT(0);
|
||||
|
||||
TestFormatSpecifier(t, tz, "%a", "Thu");
|
||||
TestFormatSpecifier(t, tz, "%A", "Thursday");
|
||||
TestFormatSpecifier(t, tz, "%b", "Jan");
|
||||
TestFormatSpecifier(t, tz, "%B", "January");
|
||||
|
||||
// %c should at least produce the numeric year and time-of-day.
|
||||
const std::string s =
|
||||
absl::FormatTime("%c", absl::FromTimeT(0), absl::UTCTimeZone());
|
||||
EXPECT_THAT(s, HasSubstr("1970"));
|
||||
EXPECT_THAT(s, HasSubstr("00:00:00"));
|
||||
|
||||
TestFormatSpecifier(t, tz, "%p", "AM");
|
||||
TestFormatSpecifier(t, tz, "%x", "01/01/70");
|
||||
TestFormatSpecifier(t, tz, "%X", "00:00:00");
|
||||
}
|
||||
|
||||
TEST(FormatTime, ExtendedSeconds) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
|
||||
// No subseconds.
|
||||
absl::Time t = absl::FromTimeT(0) + absl::Seconds(5);
|
||||
EXPECT_EQ("05", absl::FormatTime("%E*S", t, tz));
|
||||
EXPECT_EQ("05.000000000000000", absl::FormatTime("%E15S", t, tz));
|
||||
|
||||
// With subseconds.
|
||||
t += absl::Milliseconds(6) + absl::Microseconds(7) + absl::Nanoseconds(8);
|
||||
EXPECT_EQ("05.006007008", absl::FormatTime("%E*S", t, tz));
|
||||
EXPECT_EQ("05", absl::FormatTime("%E0S", t, tz));
|
||||
EXPECT_EQ("05.006007008000000", absl::FormatTime("%E15S", t, tz));
|
||||
|
||||
// Times before the Unix epoch.
|
||||
t = absl::FromUnixMicros(-1);
|
||||
EXPECT_EQ("1969-12-31 23:59:59.999999",
|
||||
absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
|
||||
|
||||
// Here is a "%E*S" case we got wrong for a while. While the first
|
||||
// instant below is correctly rendered as "...:07.333304", the second
|
||||
// one used to appear as "...:07.33330499999999999".
|
||||
t = absl::FromUnixMicros(1395024427333304);
|
||||
EXPECT_EQ("2014-03-17 02:47:07.333304",
|
||||
absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
|
||||
t += absl::Microseconds(1);
|
||||
EXPECT_EQ("2014-03-17 02:47:07.333305",
|
||||
absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
|
||||
}
|
||||
|
||||
TEST(FormatTime, RFC1123FormatPadsYear) { // locale specific
|
||||
absl::TimeZone tz = absl::UTCTimeZone();
|
||||
|
||||
// A year of 77 should be padded to 0077.
|
||||
absl::Time t = absl::FromCivil(absl::CivilSecond(77, 6, 28, 9, 8, 7), tz);
|
||||
EXPECT_EQ("Mon, 28 Jun 0077 09:08:07 +0000",
|
||||
absl::FormatTime(absl::RFC1123_full, t, tz));
|
||||
EXPECT_EQ("28 Jun 0077 09:08:07 +0000",
|
||||
absl::FormatTime(absl::RFC1123_no_wday, t, tz));
|
||||
}
|
||||
|
||||
TEST(FormatTime, InfiniteTime) {
|
||||
absl::TimeZone tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
|
||||
// The format and timezone are ignored.
|
||||
EXPECT_EQ("infinite-future",
|
||||
absl::FormatTime("%H:%M blah", absl::InfiniteFuture(), tz));
|
||||
EXPECT_EQ("infinite-past",
|
||||
absl::FormatTime("%H:%M blah", absl::InfinitePast(), tz));
|
||||
}
|
||||
|
||||
//
|
||||
// Testing ParseTime()
|
||||
//
|
||||
|
||||
TEST(ParseTime, Basics) {
|
||||
absl::Time t = absl::FromTimeT(1234567890);
|
||||
std::string err;
|
||||
|
||||
// Simple edge cases.
|
||||
EXPECT_TRUE(absl::ParseTime("", "", &t, &err)) << err;
|
||||
EXPECT_EQ(absl::UnixEpoch(), t); // everything defaulted
|
||||
EXPECT_TRUE(absl::ParseTime(" ", " ", &t, &err)) << err;
|
||||
EXPECT_TRUE(absl::ParseTime(" ", " ", &t, &err)) << err;
|
||||
EXPECT_TRUE(absl::ParseTime("x", "x", &t, &err)) << err;
|
||||
EXPECT_TRUE(absl::ParseTime("xxx", "xxx", &t, &err)) << err;
|
||||
|
||||
EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
|
||||
"2013-06-28 19:08:09 -0800", &t, &err))
|
||||
<< err;
|
||||
const auto ci = absl::FixedTimeZone(-8 * 60 * 60).At(t);
|
||||
EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
|
||||
EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
|
||||
}
|
||||
|
||||
TEST(ParseTime, NullErrorString) {
|
||||
absl::Time t;
|
||||
EXPECT_FALSE(absl::ParseTime("%Q", "invalid format", &t, nullptr));
|
||||
EXPECT_FALSE(absl::ParseTime("%H", "12 trailing data", &t, nullptr));
|
||||
EXPECT_FALSE(
|
||||
absl::ParseTime("%H out of range", "42 out of range", &t, nullptr));
|
||||
}
|
||||
|
||||
TEST(ParseTime, WithTimeZone) {
|
||||
const absl::TimeZone tz =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
absl::Time t;
|
||||
std::string e;
|
||||
|
||||
// We can parse a string without a UTC offset if we supply a timezone.
|
||||
EXPECT_TRUE(
|
||||
absl::ParseTime("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &t, &e))
|
||||
<< e;
|
||||
auto ci = tz.At(t);
|
||||
EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
|
||||
EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
|
||||
|
||||
// But the timezone is ignored when a UTC offset is present.
|
||||
EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
|
||||
"2013-06-28 19:08:09 +0800", tz, &t, &e))
|
||||
<< e;
|
||||
ci = absl::FixedTimeZone(8 * 60 * 60).At(t);
|
||||
EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
|
||||
EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
|
||||
}
|
||||
|
||||
TEST(ParseTime, ErrorCases) {
|
||||
absl::Time t = absl::FromTimeT(0);
|
||||
std::string err;
|
||||
|
||||
EXPECT_FALSE(absl::ParseTime("%S", "123", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// Can't parse an illegal format specifier.
|
||||
err.clear();
|
||||
EXPECT_FALSE(absl::ParseTime("%Q", "x", &t, &err)) << err;
|
||||
// Exact contents of "err" are platform-dependent because of
|
||||
// differences in the strptime implementation between macOS and Linux.
|
||||
EXPECT_FALSE(err.empty());
|
||||
|
||||
// Fails because of trailing, unparsed data "blah".
|
||||
EXPECT_FALSE(absl::ParseTime("%m-%d", "2-3 blah", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// Feb 31 requires normalization.
|
||||
EXPECT_FALSE(absl::ParseTime("%m-%d", "2-31", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Out-of-range"));
|
||||
|
||||
// Check that we cannot have spaces in UTC offsets.
|
||||
EXPECT_TRUE(absl::ParseTime("%z", "-0203", &t, &err)) << err;
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "- 2 3", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_TRUE(absl::ParseTime("%Ez", "-02:03", &t, &err)) << err;
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "- 2: 3", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
|
||||
// Check that we reject other malformed UTC offsets.
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "+-08:00", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-+08:00", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
|
||||
// Check that we do not accept "-0" in fields that allow zero.
|
||||
EXPECT_FALSE(absl::ParseTime("%Y", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%E4Y", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%H", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%M", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%S", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "+-000", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "+-0:00", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "-00-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-00:-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
}
|
||||
|
||||
TEST(ParseTime, ExtendedSeconds) {
|
||||
std::string err;
|
||||
absl::Time t;
|
||||
|
||||
// Here is a "%E*S" case we got wrong for a while. The fractional
|
||||
// part of the first instant is less than 2^31 and was correctly
|
||||
// parsed, while the second (and any subsecond field >=2^31) failed.
|
||||
t = absl::UnixEpoch();
|
||||
EXPECT_TRUE(absl::ParseTime("%E*S", "0.2147483647", &t, &err)) << err;
|
||||
EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
|
||||
absl::Nanoseconds(1) / 2,
|
||||
t);
|
||||
t = absl::UnixEpoch();
|
||||
EXPECT_TRUE(absl::ParseTime("%E*S", "0.2147483648", &t, &err)) << err;
|
||||
EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
|
||||
absl::Nanoseconds(3) / 4,
|
||||
t);
|
||||
|
||||
// We should also be able to specify long strings of digits far
|
||||
// beyond the current resolution and have them convert the same way.
|
||||
t = absl::UnixEpoch();
|
||||
EXPECT_TRUE(absl::ParseTime(
|
||||
"%E*S", "0.214748364801234567890123456789012345678901234567890123456789",
|
||||
&t, &err))
|
||||
<< err;
|
||||
EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
|
||||
absl::Nanoseconds(3) / 4,
|
||||
t);
|
||||
}
|
||||
|
||||
TEST(ParseTime, ExtendedOffsetErrors) {
|
||||
std::string err;
|
||||
absl::Time t;
|
||||
|
||||
// %z against +-HHMM.
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "-123", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// %z against +-HH.
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "-1", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
|
||||
// %Ez against +-HH:MM.
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-12:3", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// %Ez against +-HHMM.
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-123", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// %Ez against +-HH.
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-1", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
}
|
||||
|
||||
TEST(ParseTime, InfiniteTime) {
|
||||
absl::Time t;
|
||||
std::string err;
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-future", &t, &err));
|
||||
EXPECT_EQ(absl::InfiniteFuture(), t);
|
||||
|
||||
// Surrounding whitespace.
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-future", &t, &err));
|
||||
EXPECT_EQ(absl::InfiniteFuture(), t);
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-future ", &t, &err));
|
||||
EXPECT_EQ(absl::InfiniteFuture(), t);
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-future ", &t, &err));
|
||||
EXPECT_EQ(absl::InfiniteFuture(), t);
|
||||
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-past", &t, &err));
|
||||
EXPECT_EQ(absl::InfinitePast(), t);
|
||||
|
||||
// Surrounding whitespace.
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-past", &t, &err));
|
||||
EXPECT_EQ(absl::InfinitePast(), t);
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-past ", &t, &err));
|
||||
EXPECT_EQ(absl::InfinitePast(), t);
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-past ", &t, &err));
|
||||
EXPECT_EQ(absl::InfinitePast(), t);
|
||||
|
||||
// "infinite-future" as literal string
|
||||
absl::TimeZone tz = absl::UTCTimeZone();
|
||||
EXPECT_TRUE(absl::ParseTime("infinite-future %H:%M", "infinite-future 03:04",
|
||||
&t, &err));
|
||||
EXPECT_NE(absl::InfiniteFuture(), t);
|
||||
EXPECT_EQ(3, tz.At(t).cs.hour());
|
||||
EXPECT_EQ(4, tz.At(t).cs.minute());
|
||||
|
||||
// "infinite-past" as literal string
|
||||
EXPECT_TRUE(
|
||||
absl::ParseTime("infinite-past %H:%M", "infinite-past 03:04", &t, &err));
|
||||
EXPECT_NE(absl::InfinitePast(), t);
|
||||
EXPECT_EQ(3, tz.At(t).cs.hour());
|
||||
EXPECT_EQ(4, tz.At(t).cs.minute());
|
||||
|
||||
// The input doesn't match the format.
|
||||
EXPECT_FALSE(absl::ParseTime("infinite-future %H:%M", "03:04", &t, &err));
|
||||
EXPECT_FALSE(absl::ParseTime("infinite-past %H:%M", "03:04", &t, &err));
|
||||
}
|
||||
|
||||
TEST(ParseTime, FailsOnUnrepresentableTime) {
|
||||
const absl::TimeZone utc = absl::UTCTimeZone();
|
||||
absl::Time t;
|
||||
EXPECT_FALSE(
|
||||
absl::ParseTime("%Y-%m-%d", "-292277022657-01-27", utc, &t, nullptr));
|
||||
EXPECT_TRUE(
|
||||
absl::ParseTime("%Y-%m-%d", "-292277022657-01-28", utc, &t, nullptr));
|
||||
EXPECT_TRUE(
|
||||
absl::ParseTime("%Y-%m-%d", "292277026596-12-04", utc, &t, nullptr));
|
||||
EXPECT_FALSE(
|
||||
absl::ParseTime("%Y-%m-%d", "292277026596-12-05", utc, &t, nullptr));
|
||||
}
|
||||
|
||||
//
|
||||
// Roundtrip test for FormatTime()/ParseTime().
|
||||
//
|
||||
|
||||
TEST(FormatParse, RoundTrip) {
|
||||
const absl::TimeZone lax =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
const absl::Time in =
|
||||
absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax);
|
||||
const absl::Duration subseconds = absl::Nanoseconds(654321);
|
||||
std::string err;
|
||||
|
||||
// RFC3339, which renders subseconds.
|
||||
{
|
||||
absl::Time out;
|
||||
const std::string s =
|
||||
absl::FormatTime(absl::RFC3339_full, in + subseconds, lax);
|
||||
EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
|
||||
<< s << ": " << err;
|
||||
EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez
|
||||
}
|
||||
|
||||
// RFC1123, which only does whole seconds.
|
||||
{
|
||||
absl::Time out;
|
||||
const std::string s = absl::FormatTime(absl::RFC1123_full, in, lax);
|
||||
EXPECT_TRUE(absl::ParseTime(absl::RFC1123_full, s, &out, &err))
|
||||
<< s << ": " << err;
|
||||
EXPECT_EQ(in, out); // RFC1123_full includes %z
|
||||
}
|
||||
|
||||
// `absl::FormatTime()` falls back to strftime() for "%c", which appears to
|
||||
// work. On Windows, `absl::ParseTime()` falls back to std::get_time() which
|
||||
// appears to fail on "%c" (or at least on the "%c" text produced by
|
||||
// `strftime()`). This makes it fail the round-trip test.
|
||||
//
|
||||
// Under the emscripten compiler `absl::ParseTime() falls back to
|
||||
// `strptime()`, but that ends up using a different definition for "%c"
|
||||
// compared to `strftime()`, also causing the round-trip test to fail
|
||||
// (see https://github.com/kripken/emscripten/pull/7491).
|
||||
#if !defined(_MSC_VER) && !defined(__EMSCRIPTEN__)
|
||||
// Even though we don't know what %c will produce, it should roundtrip,
|
||||
// but only in the 0-offset timezone.
|
||||
{
|
||||
absl::Time out;
|
||||
const std::string s = absl::FormatTime("%c", in, absl::UTCTimeZone());
|
||||
EXPECT_TRUE(absl::ParseTime("%c", s, &out, &err)) << s << ": " << err;
|
||||
EXPECT_EQ(in, out);
|
||||
}
|
||||
#endif // !_MSC_VER && !__EMSCRIPTEN__
|
||||
}
|
||||
|
||||
TEST(FormatParse, RoundTripDistantFuture) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
const absl::Time in =
|
||||
absl::FromUnixSeconds(std::numeric_limits<int64_t>::max());
|
||||
std::string err;
|
||||
|
||||
absl::Time out;
|
||||
const std::string s = absl::FormatTime(absl::RFC3339_full, in, tz);
|
||||
EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
|
||||
<< s << ": " << err;
|
||||
EXPECT_EQ(in, out);
|
||||
}
|
||||
|
||||
TEST(FormatParse, RoundTripDistantPast) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
const absl::Time in =
|
||||
absl::FromUnixSeconds(std::numeric_limits<int64_t>::min());
|
||||
std::string err;
|
||||
|
||||
absl::Time out;
|
||||
const std::string s = absl::FormatTime(absl::RFC3339_full, in, tz);
|
||||
EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
|
||||
<< s << ": " << err;
|
||||
EXPECT_EQ(in, out);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
166
third_party/abseil_cpp/absl/time/internal/cctz/BUILD.bazel
vendored
Normal file
166
third_party/abseil_cpp/absl/time/internal/cctz/BUILD.bazel
vendored
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
# Copyright 2016 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
|
||||
|
||||
package(features = ["-parse_headers"])
|
||||
|
||||
licenses(["notice"]) # Apache License
|
||||
|
||||
filegroup(
|
||||
name = "zoneinfo",
|
||||
srcs = glob(["testdata/zoneinfo/**"]),
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "osx",
|
||||
constraint_values = [
|
||||
"@bazel_tools//platforms:osx",
|
||||
],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "ios",
|
||||
constraint_values = [
|
||||
"@bazel_tools//platforms:ios",
|
||||
],
|
||||
)
|
||||
|
||||
### libraries
|
||||
|
||||
cc_library(
|
||||
name = "civil_time",
|
||||
srcs = ["src/civil_time_detail.cc"],
|
||||
hdrs = [
|
||||
"include/cctz/civil_time.h",
|
||||
],
|
||||
textual_hdrs = ["include/cctz/civil_time_detail.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//absl/base:config"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "time_zone",
|
||||
srcs = [
|
||||
"src/time_zone_fixed.cc",
|
||||
"src/time_zone_fixed.h",
|
||||
"src/time_zone_format.cc",
|
||||
"src/time_zone_if.cc",
|
||||
"src/time_zone_if.h",
|
||||
"src/time_zone_impl.cc",
|
||||
"src/time_zone_impl.h",
|
||||
"src/time_zone_info.cc",
|
||||
"src/time_zone_info.h",
|
||||
"src/time_zone_libc.cc",
|
||||
"src/time_zone_libc.h",
|
||||
"src/time_zone_lookup.cc",
|
||||
"src/time_zone_posix.cc",
|
||||
"src/time_zone_posix.h",
|
||||
"src/tzfile.h",
|
||||
"src/zone_info_source.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"include/cctz/time_zone.h",
|
||||
"include/cctz/zone_info_source.h",
|
||||
],
|
||||
linkopts = select({
|
||||
":osx": [
|
||||
"-framework Foundation",
|
||||
],
|
||||
":ios": [
|
||||
"-framework Foundation",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":civil_time",
|
||||
"//absl/base:config",
|
||||
],
|
||||
)
|
||||
|
||||
### tests
|
||||
|
||||
cc_test(
|
||||
name = "civil_time_test",
|
||||
size = "small",
|
||||
srcs = ["src/civil_time_test.cc"],
|
||||
deps = [
|
||||
":civil_time",
|
||||
"//absl/base:config",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "time_zone_format_test",
|
||||
size = "small",
|
||||
srcs = ["src/time_zone_format_test.cc"],
|
||||
data = [":zoneinfo"],
|
||||
tags = [
|
||||
"no_test_android_arm",
|
||||
"no_test_android_arm64",
|
||||
"no_test_android_x86",
|
||||
],
|
||||
deps = [
|
||||
":civil_time",
|
||||
":time_zone",
|
||||
"//absl/base:config",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "time_zone_lookup_test",
|
||||
size = "small",
|
||||
timeout = "moderate",
|
||||
srcs = ["src/time_zone_lookup_test.cc"],
|
||||
data = [":zoneinfo"],
|
||||
tags = [
|
||||
"no_test_android_arm",
|
||||
"no_test_android_arm64",
|
||||
"no_test_android_x86",
|
||||
],
|
||||
deps = [
|
||||
":civil_time",
|
||||
":time_zone",
|
||||
"//absl/base:config",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
### benchmarks
|
||||
|
||||
cc_test(
|
||||
name = "cctz_benchmark",
|
||||
srcs = [
|
||||
"src/cctz_benchmark.cc",
|
||||
"src/time_zone_if.h",
|
||||
"src/time_zone_impl.h",
|
||||
"src/time_zone_info.h",
|
||||
"src/tzfile.h",
|
||||
],
|
||||
linkstatic = 1,
|
||||
tags = ["benchmark"],
|
||||
deps = [
|
||||
":civil_time",
|
||||
":time_zone",
|
||||
"//absl/base:config",
|
||||
"@com_github_google_benchmark//:benchmark_main",
|
||||
],
|
||||
)
|
||||
|
||||
### examples
|
||||
|
||||
### binaries
|
||||
332
third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time.h
vendored
Normal file
332
third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time.h
vendored
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// The term "civil time" refers to the legally recognized human-scale time
|
||||
// that is represented by the six fields YYYY-MM-DD hh:mm:ss. Modern-day civil
|
||||
// time follows the Gregorian Calendar and is a time-zone-independent concept.
|
||||
// A "date" is perhaps the most common example of a civil time (represented in
|
||||
// this library as cctz::civil_day). This library provides six classes and a
|
||||
// handful of functions that help with rounding, iterating, and arithmetic on
|
||||
// civil times while avoiding complications like daylight-saving time (DST).
|
||||
//
|
||||
// The following six classes form the core of this civil-time library:
|
||||
//
|
||||
// * civil_second
|
||||
// * civil_minute
|
||||
// * civil_hour
|
||||
// * civil_day
|
||||
// * civil_month
|
||||
// * civil_year
|
||||
//
|
||||
// Each class is a simple value type with the same interface for construction
|
||||
// and the same six accessors for each of the civil fields (year, month, day,
|
||||
// hour, minute, and second, aka YMDHMS). These classes differ only in their
|
||||
// alignment, which is indicated by the type name and specifies the field on
|
||||
// which arithmetic operates.
|
||||
//
|
||||
// Each class can be constructed by passing up to six optional integer
|
||||
// arguments representing the YMDHMS fields (in that order) to the
|
||||
// constructor. Omitted fields are assigned their minimum valid value. Hours,
|
||||
// minutes, and seconds will be set to 0, month and day will be set to 1, and
|
||||
// since there is no minimum valid year, it will be set to 1970. So, a
|
||||
// default-constructed civil-time object will have YMDHMS fields representing
|
||||
// "1970-01-01 00:00:00". Fields that are out-of-range are normalized (e.g.,
|
||||
// October 32 -> November 1) so that all civil-time objects represent valid
|
||||
// values.
|
||||
//
|
||||
// Each civil-time class is aligned to the civil-time field indicated in the
|
||||
// class's name after normalization. Alignment is performed by setting all the
|
||||
// inferior fields to their minimum valid value (as described above). The
|
||||
// following are examples of how each of the six types would align the fields
|
||||
// representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the
|
||||
// string format used here is not important; it's just a shorthand way of
|
||||
// showing the six YMDHMS fields.)
|
||||
//
|
||||
// civil_second 2015-11-22 12:34:56
|
||||
// civil_minute 2015-11-22 12:34:00
|
||||
// civil_hour 2015-11-22 12:00:00
|
||||
// civil_day 2015-11-22 00:00:00
|
||||
// civil_month 2015-11-01 00:00:00
|
||||
// civil_year 2015-01-01 00:00:00
|
||||
//
|
||||
// Each civil-time type performs arithmetic on the field to which it is
|
||||
// aligned. This means that adding 1 to a civil_day increments the day field
|
||||
// (normalizing as necessary), and subtracting 7 from a civil_month operates
|
||||
// on the month field (normalizing as necessary). All arithmetic produces a
|
||||
// valid civil time. Difference requires two similarly aligned civil-time
|
||||
// objects and returns the scalar answer in units of the objects' alignment.
|
||||
// For example, the difference between two civil_hour objects will give an
|
||||
// answer in units of civil hours.
|
||||
//
|
||||
// In addition to the six civil-time types just described, there are
|
||||
// a handful of helper functions and algorithms for performing common
|
||||
// calculations. These are described below.
|
||||
//
|
||||
// Note: In C++14 and later, this library is usable in a constexpr context.
|
||||
//
|
||||
// CONSTRUCTION:
|
||||
//
|
||||
// Each of the civil-time types can be constructed in two ways: by directly
|
||||
// passing to the constructor up to six (optional) integers representing the
|
||||
// YMDHMS fields, or by copying the YMDHMS fields from a differently aligned
|
||||
// civil-time type.
|
||||
//
|
||||
// civil_day default_value; // 1970-01-01 00:00:00
|
||||
//
|
||||
// civil_day a(2015, 2, 3); // 2015-02-03 00:00:00
|
||||
// civil_day b(2015, 2, 3, 4, 5, 6); // 2015-02-03 00:00:00
|
||||
// civil_day c(2015); // 2015-01-01 00:00:00
|
||||
//
|
||||
// civil_second ss(2015, 2, 3, 4, 5, 6); // 2015-02-03 04:05:06
|
||||
// civil_minute mm(ss); // 2015-02-03 04:05:00
|
||||
// civil_hour hh(mm); // 2015-02-03 04:00:00
|
||||
// civil_day d(hh); // 2015-02-03 00:00:00
|
||||
// civil_month m(d); // 2015-02-01 00:00:00
|
||||
// civil_year y(m); // 2015-01-01 00:00:00
|
||||
//
|
||||
// m = civil_month(y); // 2015-01-01 00:00:00
|
||||
// d = civil_day(m); // 2015-01-01 00:00:00
|
||||
// hh = civil_hour(d); // 2015-01-01 00:00:00
|
||||
// mm = civil_minute(hh); // 2015-01-01 00:00:00
|
||||
// ss = civil_second(mm); // 2015-01-01 00:00:00
|
||||
//
|
||||
// ALIGNMENT CONVERSION:
|
||||
//
|
||||
// The alignment of a civil-time object cannot change, but the object may be
|
||||
// used to construct a new object with a different alignment. This is referred
|
||||
// to as "realigning". When realigning to a type with the same or more
|
||||
// precision (e.g., civil_day -> civil_second), the conversion may be
|
||||
// performed implicitly since no information is lost. However, if information
|
||||
// could be discarded (e.g., civil_second -> civil_day), the conversion must
|
||||
// be explicit at the call site.
|
||||
//
|
||||
// void fun(const civil_day& day);
|
||||
//
|
||||
// civil_second cs;
|
||||
// fun(cs); // Won't compile because data may be discarded
|
||||
// fun(civil_day(cs)); // OK: explicit conversion
|
||||
//
|
||||
// civil_day cd;
|
||||
// fun(cd); // OK: no conversion needed
|
||||
//
|
||||
// civil_month cm;
|
||||
// fun(cm); // OK: implicit conversion to civil_day
|
||||
//
|
||||
// NORMALIZATION:
|
||||
//
|
||||
// Integer arguments passed to the constructor may be out-of-range, in which
|
||||
// case they are normalized to produce a valid civil-time object. This enables
|
||||
// natural arithmetic on constructor arguments without worrying about the
|
||||
// field's range. Normalization guarantees that there are no invalid
|
||||
// civil-time objects.
|
||||
//
|
||||
// civil_day d(2016, 10, 32); // Out-of-range day; normalized to 2016-11-01
|
||||
//
|
||||
// Note: If normalization is undesired, you can signal an error by comparing
|
||||
// the constructor arguments to the normalized values returned by the YMDHMS
|
||||
// properties.
|
||||
//
|
||||
// PROPERTIES:
|
||||
//
|
||||
// All civil-time types have accessors for all six of the civil-time fields:
|
||||
// year, month, day, hour, minute, and second. Recall that fields inferior to
|
||||
// the type's alignment will be set to their minimum valid value.
|
||||
//
|
||||
// civil_day d(2015, 6, 28);
|
||||
// // d.year() == 2015
|
||||
// // d.month() == 6
|
||||
// // d.day() == 28
|
||||
// // d.hour() == 0
|
||||
// // d.minute() == 0
|
||||
// // d.second() == 0
|
||||
//
|
||||
// COMPARISON:
|
||||
//
|
||||
// Comparison always considers all six YMDHMS fields, regardless of the type's
|
||||
// alignment. Comparison between differently aligned civil-time types is
|
||||
// allowed.
|
||||
//
|
||||
// civil_day feb_3(2015, 2, 3); // 2015-02-03 00:00:00
|
||||
// civil_day mar_4(2015, 3, 4); // 2015-03-04 00:00:00
|
||||
// // feb_3 < mar_4
|
||||
// // civil_year(feb_3) == civil_year(mar_4)
|
||||
//
|
||||
// civil_second feb_3_noon(2015, 2, 3, 12, 0, 0); // 2015-02-03 12:00:00
|
||||
// // feb_3 < feb_3_noon
|
||||
// // feb_3 == civil_day(feb_3_noon)
|
||||
//
|
||||
// // Iterates all the days of February 2015.
|
||||
// for (civil_day d(2015, 2, 1); d < civil_month(2015, 3); ++d) {
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// STREAMING:
|
||||
//
|
||||
// Each civil-time type may be sent to an output stream using operator<<().
|
||||
// The output format follows the pattern "YYYY-MM-DDThh:mm:ss" where fields
|
||||
// inferior to the type's alignment are omitted.
|
||||
//
|
||||
// civil_second cs(2015, 2, 3, 4, 5, 6);
|
||||
// std::cout << cs << "\n"; // Outputs: 2015-02-03T04:05:06
|
||||
//
|
||||
// civil_day cd(cs);
|
||||
// std::cout << cd << "\n"; // Outputs: 2015-02-03
|
||||
//
|
||||
// civil_year cy(cs);
|
||||
// std::cout << cy << "\n"; // Outputs: 2015
|
||||
//
|
||||
// ARITHMETIC:
|
||||
//
|
||||
// Civil-time types support natural arithmetic operators such as addition,
|
||||
// subtraction, and difference. Arithmetic operates on the civil-time field
|
||||
// indicated in the type's name. Difference requires arguments with the same
|
||||
// alignment and returns the answer in units of the alignment.
|
||||
//
|
||||
// civil_day a(2015, 2, 3);
|
||||
// ++a; // 2015-02-04 00:00:00
|
||||
// --a; // 2015-02-03 00:00:00
|
||||
// civil_day b = a + 1; // 2015-02-04 00:00:00
|
||||
// civil_day c = 1 + b; // 2015-02-05 00:00:00
|
||||
// int n = c - a; // n = 2 (civil days)
|
||||
// int m = c - civil_month(c); // Won't compile: different types.
|
||||
//
|
||||
// EXAMPLE: Adding a month to January 31.
|
||||
//
|
||||
// One of the classic questions that arises when considering a civil-time
|
||||
// library (or a date library or a date/time library) is this: "What happens
|
||||
// when you add a month to January 31?" This is an interesting question
|
||||
// because there could be a number of possible answers:
|
||||
//
|
||||
// 1. March 3 (or 2 if a leap year). This may make sense if the operation
|
||||
// wants the equivalent of February 31.
|
||||
// 2. February 28 (or 29 if a leap year). This may make sense if the operation
|
||||
// wants the last day of January to go to the last day of February.
|
||||
// 3. Error. The caller may get some error, an exception, an invalid date
|
||||
// object, or maybe false is returned. This may make sense because there is
|
||||
// no single unambiguously correct answer to the question.
|
||||
//
|
||||
// Practically speaking, any answer that is not what the programmer intended
|
||||
// is the wrong answer.
|
||||
//
|
||||
// This civil-time library avoids the problem by making it impossible to ask
|
||||
// ambiguous questions. All civil-time objects are aligned to a particular
|
||||
// civil-field boundary (such as aligned to a year, month, day, hour, minute,
|
||||
// or second), and arithmetic operates on the field to which the object is
|
||||
// aligned. This means that in order to "add a month" the object must first be
|
||||
// aligned to a month boundary, which is equivalent to the first day of that
|
||||
// month.
|
||||
//
|
||||
// Of course, there are ways to compute an answer the question at hand using
|
||||
// this civil-time library, but they require the programmer to be explicit
|
||||
// about the answer they expect. To illustrate, let's see how to compute all
|
||||
// three of the above possible answers to the question of "Jan 31 plus 1
|
||||
// month":
|
||||
//
|
||||
// const civil_day d(2015, 1, 31);
|
||||
//
|
||||
// // Answer 1:
|
||||
// // Add 1 to the month field in the constructor, and rely on normalization.
|
||||
// const auto ans_normalized = civil_day(d.year(), d.month() + 1, d.day());
|
||||
// // ans_normalized == 2015-03-03 (aka Feb 31)
|
||||
//
|
||||
// // Answer 2:
|
||||
// // Add 1 to month field, capping to the end of next month.
|
||||
// const auto next_month = civil_month(d) + 1;
|
||||
// const auto last_day_of_next_month = civil_day(next_month + 1) - 1;
|
||||
// const auto ans_capped = std::min(ans_normalized, last_day_of_next_month);
|
||||
// // ans_capped == 2015-02-28
|
||||
//
|
||||
// // Answer 3:
|
||||
// // Signal an error if the normalized answer is not in next month.
|
||||
// if (civil_month(ans_normalized) != next_month) {
|
||||
// // error, month overflow
|
||||
// }
|
||||
//
|
||||
using civil_year = detail::civil_year;
|
||||
using civil_month = detail::civil_month;
|
||||
using civil_day = detail::civil_day;
|
||||
using civil_hour = detail::civil_hour;
|
||||
using civil_minute = detail::civil_minute;
|
||||
using civil_second = detail::civil_second;
|
||||
|
||||
// An enum class with members monday, tuesday, wednesday, thursday, friday,
|
||||
// saturday, and sunday. These enum values may be sent to an output stream
|
||||
// using operator<<(). The result is the full weekday name in English with a
|
||||
// leading capital letter.
|
||||
//
|
||||
// weekday wd = weekday::thursday;
|
||||
// std::cout << wd << "\n"; // Outputs: Thursday
|
||||
//
|
||||
using detail::weekday;
|
||||
|
||||
// Returns the weekday for the given civil-time value.
|
||||
//
|
||||
// civil_day a(2015, 8, 13);
|
||||
// weekday wd = get_weekday(a); // wd == weekday::thursday
|
||||
//
|
||||
using detail::get_weekday;
|
||||
|
||||
// Returns the civil_day that strictly follows or precedes the given
|
||||
// civil_day, and that falls on the given weekday.
|
||||
//
|
||||
// For example, given:
|
||||
//
|
||||
// August 2015
|
||||
// Su Mo Tu We Th Fr Sa
|
||||
// 1
|
||||
// 2 3 4 5 6 7 8
|
||||
// 9 10 11 12 13 14 15
|
||||
// 16 17 18 19 20 21 22
|
||||
// 23 24 25 26 27 28 29
|
||||
// 30 31
|
||||
//
|
||||
// civil_day a(2015, 8, 13); // get_weekday(a) == weekday::thursday
|
||||
// civil_day b = next_weekday(a, weekday::thursday); // b = 2015-08-20
|
||||
// civil_day c = prev_weekday(a, weekday::thursday); // c = 2015-08-06
|
||||
//
|
||||
// civil_day d = ...
|
||||
// // Gets the following Thursday if d is not already Thursday
|
||||
// civil_day thurs1 = next_weekday(d - 1, weekday::thursday);
|
||||
// // Gets the previous Thursday if d is not already Thursday
|
||||
// civil_day thurs2 = prev_weekday(d + 1, weekday::thursday);
|
||||
//
|
||||
using detail::next_weekday;
|
||||
using detail::prev_weekday;
|
||||
|
||||
// Returns the day-of-year for the given civil-time value.
|
||||
//
|
||||
// civil_day a(2015, 1, 1);
|
||||
// int yd_jan_1 = get_yearday(a); // yd_jan_1 = 1
|
||||
// civil_day b(2015, 12, 31);
|
||||
// int yd_dec_31 = get_yearday(b); // yd_dec_31 = 365
|
||||
//
|
||||
using detail::get_yearday;
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_H_
|
||||
622
third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h
vendored
Normal file
622
third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h
vendored
Normal file
|
|
@ -0,0 +1,622 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
// Disable constexpr support unless we are in C++14 mode.
|
||||
#if __cpp_constexpr >= 201304 || (defined(_MSC_VER) && _MSC_VER >= 1910)
|
||||
#define CONSTEXPR_D constexpr // data
|
||||
#define CONSTEXPR_F constexpr // function
|
||||
#define CONSTEXPR_M constexpr // member
|
||||
#else
|
||||
#define CONSTEXPR_D const
|
||||
#define CONSTEXPR_F inline
|
||||
#define CONSTEXPR_M
|
||||
#endif
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// Support years that at least span the range of 64-bit time_t values.
|
||||
using year_t = std::int_fast64_t;
|
||||
|
||||
// Type alias that indicates an argument is not normalized (e.g., the
|
||||
// constructor parameters and operands/results of addition/subtraction).
|
||||
using diff_t = std::int_fast64_t;
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Type aliases that indicate normalized argument values.
|
||||
using month_t = std::int_fast8_t; // [1:12]
|
||||
using day_t = std::int_fast8_t; // [1:31]
|
||||
using hour_t = std::int_fast8_t; // [0:23]
|
||||
using minute_t = std::int_fast8_t; // [0:59]
|
||||
using second_t = std::int_fast8_t; // [0:59]
|
||||
|
||||
// Normalized civil-time fields: Y-M-D HH:MM:SS.
|
||||
struct fields {
|
||||
CONSTEXPR_M fields(year_t year, month_t month, day_t day, hour_t hour,
|
||||
minute_t minute, second_t second)
|
||||
: y(year), m(month), d(day), hh(hour), mm(minute), ss(second) {}
|
||||
std::int_least64_t y;
|
||||
std::int_least8_t m;
|
||||
std::int_least8_t d;
|
||||
std::int_least8_t hh;
|
||||
std::int_least8_t mm;
|
||||
std::int_least8_t ss;
|
||||
};
|
||||
|
||||
struct second_tag {};
|
||||
struct minute_tag : second_tag {};
|
||||
struct hour_tag : minute_tag {};
|
||||
struct day_tag : hour_tag {};
|
||||
struct month_tag : day_tag {};
|
||||
struct year_tag : month_tag {};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Field normalization (without avoidable overflow).
|
||||
|
||||
namespace impl {
|
||||
|
||||
CONSTEXPR_F bool is_leap_year(year_t y) noexcept {
|
||||
return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
|
||||
}
|
||||
CONSTEXPR_F int year_index(year_t y, month_t m) noexcept {
|
||||
return (static_cast<int>((y + (m > 2)) % 400) + 400) % 400;
|
||||
}
|
||||
CONSTEXPR_F int days_per_century(year_t y, month_t m) noexcept {
|
||||
const int yi = year_index(y, m);
|
||||
return 36524 + (yi == 0 || yi > 300);
|
||||
}
|
||||
CONSTEXPR_F int days_per_4years(year_t y, month_t m) noexcept {
|
||||
const int yi = year_index(y, m);
|
||||
return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96);
|
||||
}
|
||||
CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept {
|
||||
return is_leap_year(y + (m > 2)) ? 366 : 365;
|
||||
}
|
||||
CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept {
|
||||
CONSTEXPR_D int k_days_per_month[1 + 12] = {
|
||||
-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // non leap year
|
||||
};
|
||||
return k_days_per_month[m] + (m == 2 && is_leap_year(y));
|
||||
}
|
||||
|
||||
CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh,
|
||||
minute_t mm, second_t ss) noexcept {
|
||||
y += (cd / 146097) * 400;
|
||||
cd %= 146097;
|
||||
if (cd < 0) {
|
||||
y -= 400;
|
||||
cd += 146097;
|
||||
}
|
||||
y += (d / 146097) * 400;
|
||||
d = d % 146097 + cd;
|
||||
if (d > 0) {
|
||||
if (d > 146097) {
|
||||
y += 400;
|
||||
d -= 146097;
|
||||
}
|
||||
} else {
|
||||
if (d > -365) {
|
||||
// We often hit the previous year when stepping a civil time backwards,
|
||||
// so special case it to avoid counting up by 100/4/1-year chunks.
|
||||
y -= 1;
|
||||
d += days_per_year(y, m);
|
||||
} else {
|
||||
y -= 400;
|
||||
d += 146097;
|
||||
}
|
||||
}
|
||||
if (d > 365) {
|
||||
for (int n = days_per_century(y, m); d > n; n = days_per_century(y, m)) {
|
||||
d -= n;
|
||||
y += 100;
|
||||
}
|
||||
for (int n = days_per_4years(y, m); d > n; n = days_per_4years(y, m)) {
|
||||
d -= n;
|
||||
y += 4;
|
||||
}
|
||||
for (int n = days_per_year(y, m); d > n; n = days_per_year(y, m)) {
|
||||
d -= n;
|
||||
++y;
|
||||
}
|
||||
}
|
||||
if (d > 28) {
|
||||
for (int n = days_per_month(y, m); d > n; n = days_per_month(y, m)) {
|
||||
d -= n;
|
||||
if (++m > 12) {
|
||||
++y;
|
||||
m = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields(y, m, static_cast<day_t>(d), hh, mm, ss);
|
||||
}
|
||||
CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh,
|
||||
minute_t mm, second_t ss) noexcept {
|
||||
if (m != 12) {
|
||||
y += m / 12;
|
||||
m %= 12;
|
||||
if (m <= 0) {
|
||||
y -= 1;
|
||||
m += 12;
|
||||
}
|
||||
}
|
||||
return n_day(y, static_cast<month_t>(m), d, cd, hh, mm, ss);
|
||||
}
|
||||
CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, diff_t hh,
|
||||
minute_t mm, second_t ss) noexcept {
|
||||
cd += hh / 24;
|
||||
hh %= 24;
|
||||
if (hh < 0) {
|
||||
cd -= 1;
|
||||
hh += 24;
|
||||
}
|
||||
return n_mon(y, m, d, cd, static_cast<hour_t>(hh), mm, ss);
|
||||
}
|
||||
CONSTEXPR_F fields n_min(year_t y, diff_t m, diff_t d, diff_t hh, diff_t ch,
|
||||
diff_t mm, second_t ss) noexcept {
|
||||
ch += mm / 60;
|
||||
mm %= 60;
|
||||
if (mm < 0) {
|
||||
ch -= 1;
|
||||
mm += 60;
|
||||
}
|
||||
return n_hour(y, m, d, hh / 24 + ch / 24, hh % 24 + ch % 24,
|
||||
static_cast<minute_t>(mm), ss);
|
||||
}
|
||||
CONSTEXPR_F fields n_sec(year_t y, diff_t m, diff_t d, diff_t hh, diff_t mm,
|
||||
diff_t ss) noexcept {
|
||||
// Optimization for when (non-constexpr) fields are already normalized.
|
||||
if (0 <= ss && ss < 60) {
|
||||
const second_t nss = static_cast<second_t>(ss);
|
||||
if (0 <= mm && mm < 60) {
|
||||
const minute_t nmm = static_cast<minute_t>(mm);
|
||||
if (0 <= hh && hh < 24) {
|
||||
const hour_t nhh = static_cast<hour_t>(hh);
|
||||
if (1 <= d && d <= 28 && 1 <= m && m <= 12) {
|
||||
const day_t nd = static_cast<day_t>(d);
|
||||
const month_t nm = static_cast<month_t>(m);
|
||||
return fields(y, nm, nd, nhh, nmm, nss);
|
||||
}
|
||||
return n_mon(y, m, d, 0, nhh, nmm, nss);
|
||||
}
|
||||
return n_hour(y, m, d, hh / 24, hh % 24, nmm, nss);
|
||||
}
|
||||
return n_min(y, m, d, hh, mm / 60, mm % 60, nss);
|
||||
}
|
||||
diff_t cm = ss / 60;
|
||||
ss %= 60;
|
||||
if (ss < 0) {
|
||||
cm -= 1;
|
||||
ss += 60;
|
||||
}
|
||||
return n_min(y, m, d, hh, mm / 60 + cm / 60, mm % 60 + cm % 60,
|
||||
static_cast<second_t>(ss));
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Increments the indicated (normalized) field by "n".
|
||||
CONSTEXPR_F fields step(second_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_sec(f.y, f.m, f.d, f.hh, f.mm + n / 60, f.ss + n % 60);
|
||||
}
|
||||
CONSTEXPR_F fields step(minute_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_min(f.y, f.m, f.d, f.hh + n / 60, 0, f.mm + n % 60, f.ss);
|
||||
}
|
||||
CONSTEXPR_F fields step(hour_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_hour(f.y, f.m, f.d + n / 24, 0, f.hh + n % 24, f.mm, f.ss);
|
||||
}
|
||||
CONSTEXPR_F fields step(day_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_day(f.y, f.m, f.d, n, f.hh, f.mm, f.ss);
|
||||
}
|
||||
CONSTEXPR_F fields step(month_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_mon(f.y + n / 12, f.m + n % 12, f.d, 0, f.hh, f.mm, f.ss);
|
||||
}
|
||||
CONSTEXPR_F fields step(year_tag, fields f, diff_t n) noexcept {
|
||||
return fields(f.y + n, f.m, f.d, f.hh, f.mm, f.ss);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace impl {
|
||||
|
||||
// Returns (v * f + a) but avoiding intermediate overflow when possible.
|
||||
CONSTEXPR_F diff_t scale_add(diff_t v, diff_t f, diff_t a) noexcept {
|
||||
return (v < 0) ? ((v + 1) * f + a) - f : ((v - 1) * f + a) + f;
|
||||
}
|
||||
|
||||
// Map a (normalized) Y/M/D to the number of days before/after 1970-01-01.
|
||||
// Probably overflows for years outside [-292277022656:292277026595].
|
||||
CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept {
|
||||
const diff_t eyear = (m <= 2) ? y - 1 : y;
|
||||
const diff_t era = (eyear >= 0 ? eyear : eyear - 399) / 400;
|
||||
const diff_t yoe = eyear - era * 400;
|
||||
const diff_t doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
|
||||
const diff_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
||||
return era * 146097 + doe - 719468;
|
||||
}
|
||||
|
||||
// Returns the difference in days between two normalized Y-M-D tuples.
|
||||
// ymd_ord() will encounter integer overflow given extreme year values,
|
||||
// yet the difference between two such extreme values may actually be
|
||||
// small, so we take a little care to avoid overflow when possible by
|
||||
// exploiting the 146097-day cycle.
|
||||
CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, year_t y2,
|
||||
month_t m2, day_t d2) noexcept {
|
||||
const diff_t a_c4_off = y1 % 400;
|
||||
const diff_t b_c4_off = y2 % 400;
|
||||
diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off);
|
||||
diff_t delta = ymd_ord(a_c4_off, m1, d1) - ymd_ord(b_c4_off, m2, d2);
|
||||
if (c4_diff > 0 && delta < 0) {
|
||||
delta += 2 * 146097;
|
||||
c4_diff -= 2 * 400;
|
||||
} else if (c4_diff < 0 && delta > 0) {
|
||||
delta -= 2 * 146097;
|
||||
c4_diff += 2 * 400;
|
||||
}
|
||||
return (c4_diff / 400 * 146097) + delta;
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
// Returns the difference between fields structs using the indicated unit.
|
||||
CONSTEXPR_F diff_t difference(year_tag, fields f1, fields f2) noexcept {
|
||||
return f1.y - f2.y;
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(month_tag, fields f1, fields f2) noexcept {
|
||||
return impl::scale_add(difference(year_tag{}, f1, f2), 12, (f1.m - f2.m));
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(day_tag, fields f1, fields f2) noexcept {
|
||||
return impl::day_difference(f1.y, f1.m, f1.d, f2.y, f2.m, f2.d);
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(hour_tag, fields f1, fields f2) noexcept {
|
||||
return impl::scale_add(difference(day_tag{}, f1, f2), 24, (f1.hh - f2.hh));
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(minute_tag, fields f1, fields f2) noexcept {
|
||||
return impl::scale_add(difference(hour_tag{}, f1, f2), 60, (f1.mm - f2.mm));
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept {
|
||||
return impl::scale_add(difference(minute_tag{}, f1, f2), 60, f1.ss - f2.ss);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Aligns the (normalized) fields struct to the indicated field.
|
||||
CONSTEXPR_F fields align(second_tag, fields f) noexcept { return f; }
|
||||
CONSTEXPR_F fields align(minute_tag, fields f) noexcept {
|
||||
return fields{f.y, f.m, f.d, f.hh, f.mm, 0};
|
||||
}
|
||||
CONSTEXPR_F fields align(hour_tag, fields f) noexcept {
|
||||
return fields{f.y, f.m, f.d, f.hh, 0, 0};
|
||||
}
|
||||
CONSTEXPR_F fields align(day_tag, fields f) noexcept {
|
||||
return fields{f.y, f.m, f.d, 0, 0, 0};
|
||||
}
|
||||
CONSTEXPR_F fields align(month_tag, fields f) noexcept {
|
||||
return fields{f.y, f.m, 1, 0, 0, 0};
|
||||
}
|
||||
CONSTEXPR_F fields align(year_tag, fields f) noexcept {
|
||||
return fields{f.y, 1, 1, 0, 0, 0};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <typename H>
|
||||
H AbslHashValueImpl(second_tag, H h, fields f) {
|
||||
return H::combine(std::move(h), f.y, f.m, f.d, f.hh, f.mm, f.ss);
|
||||
}
|
||||
template <typename H>
|
||||
H AbslHashValueImpl(minute_tag, H h, fields f) {
|
||||
return H::combine(std::move(h), f.y, f.m, f.d, f.hh, f.mm);
|
||||
}
|
||||
template <typename H>
|
||||
H AbslHashValueImpl(hour_tag, H h, fields f) {
|
||||
return H::combine(std::move(h), f.y, f.m, f.d, f.hh);
|
||||
}
|
||||
template <typename H>
|
||||
H AbslHashValueImpl(day_tag, H h, fields f) {
|
||||
return H::combine(std::move(h), f.y, f.m, f.d);
|
||||
}
|
||||
template <typename H>
|
||||
H AbslHashValueImpl(month_tag, H h, fields f) {
|
||||
return H::combine(std::move(h), f.y, f.m);
|
||||
}
|
||||
template <typename H>
|
||||
H AbslHashValueImpl(year_tag, H h, fields f) {
|
||||
return H::combine(std::move(h), f.y);
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
class civil_time {
|
||||
public:
|
||||
explicit CONSTEXPR_M civil_time(year_t y, diff_t m = 1, diff_t d = 1,
|
||||
diff_t hh = 0, diff_t mm = 0,
|
||||
diff_t ss = 0) noexcept
|
||||
: civil_time(impl::n_sec(y, m, d, hh, mm, ss)) {}
|
||||
|
||||
CONSTEXPR_M civil_time() noexcept : f_{1970, 1, 1, 0, 0, 0} {}
|
||||
civil_time(const civil_time&) = default;
|
||||
civil_time& operator=(const civil_time&) = default;
|
||||
|
||||
// Conversion between civil times of different alignment. Conversion to
|
||||
// a more precise alignment is allowed implicitly (e.g., day -> hour),
|
||||
// but conversion where information is discarded must be explicit
|
||||
// (e.g., second -> minute).
|
||||
template <typename U, typename S>
|
||||
using preserves_data =
|
||||
typename std::enable_if<std::is_base_of<U, S>::value>::type;
|
||||
template <typename U>
|
||||
CONSTEXPR_M civil_time(const civil_time<U>& ct,
|
||||
preserves_data<T, U>* = nullptr) noexcept
|
||||
: civil_time(ct.f_) {}
|
||||
template <typename U>
|
||||
explicit CONSTEXPR_M civil_time(const civil_time<U>& ct,
|
||||
preserves_data<U, T>* = nullptr) noexcept
|
||||
: civil_time(ct.f_) {}
|
||||
|
||||
// Factories for the maximum/minimum representable civil_time.
|
||||
static CONSTEXPR_F civil_time(max)() {
|
||||
const auto max_year = (std::numeric_limits<std::int_least64_t>::max)();
|
||||
return civil_time(max_year, 12, 31, 23, 59, 59);
|
||||
}
|
||||
static CONSTEXPR_F civil_time(min)() {
|
||||
const auto min_year = (std::numeric_limits<std::int_least64_t>::min)();
|
||||
return civil_time(min_year, 1, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Field accessors. Note: All but year() return an int.
|
||||
CONSTEXPR_M year_t year() const noexcept { return f_.y; }
|
||||
CONSTEXPR_M int month() const noexcept { return f_.m; }
|
||||
CONSTEXPR_M int day() const noexcept { return f_.d; }
|
||||
CONSTEXPR_M int hour() const noexcept { return f_.hh; }
|
||||
CONSTEXPR_M int minute() const noexcept { return f_.mm; }
|
||||
CONSTEXPR_M int second() const noexcept { return f_.ss; }
|
||||
|
||||
// Assigning arithmetic.
|
||||
CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept {
|
||||
f_ = step(T{}, f_, n);
|
||||
return *this;
|
||||
}
|
||||
CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept {
|
||||
if (n != (std::numeric_limits<diff_t>::min)()) {
|
||||
f_ = step(T{}, f_, -n);
|
||||
} else {
|
||||
f_ = step(T{}, step(T{}, f_, -(n + 1)), 1);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
CONSTEXPR_M civil_time& operator++() noexcept { return *this += 1; }
|
||||
CONSTEXPR_M civil_time operator++(int) noexcept {
|
||||
const civil_time a = *this;
|
||||
++*this;
|
||||
return a;
|
||||
}
|
||||
CONSTEXPR_M civil_time& operator--() noexcept { return *this -= 1; }
|
||||
CONSTEXPR_M civil_time operator--(int) noexcept {
|
||||
const civil_time a = *this;
|
||||
--*this;
|
||||
return a;
|
||||
}
|
||||
|
||||
// Binary arithmetic operators.
|
||||
friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept {
|
||||
return a += n;
|
||||
}
|
||||
friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept {
|
||||
return a += n;
|
||||
}
|
||||
friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept {
|
||||
return a -= n;
|
||||
}
|
||||
friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept {
|
||||
return difference(T{}, lhs.f_, rhs.f_);
|
||||
}
|
||||
|
||||
template <typename H>
|
||||
friend H AbslHashValue(H h, civil_time a) {
|
||||
return impl::AbslHashValueImpl(T{}, std::move(h), a.f_);
|
||||
}
|
||||
|
||||
private:
|
||||
// All instantiations of this template are allowed to call the following
|
||||
// private constructor and access the private fields member.
|
||||
template <typename U>
|
||||
friend class civil_time;
|
||||
|
||||
// The designated constructor that all others eventually call.
|
||||
explicit CONSTEXPR_M civil_time(fields f) noexcept : f_(align(T{}, f)) {}
|
||||
|
||||
fields f_;
|
||||
};
|
||||
|
||||
// Disallows difference between differently aligned types.
|
||||
// auto n = civil_day(...) - civil_hour(...); // would be confusing.
|
||||
template <typename T, typename U>
|
||||
CONSTEXPR_F diff_t operator-(civil_time<T>, civil_time<U>) = delete;
|
||||
|
||||
using civil_year = civil_time<year_tag>;
|
||||
using civil_month = civil_time<month_tag>;
|
||||
using civil_day = civil_time<day_tag>;
|
||||
using civil_hour = civil_time<hour_tag>;
|
||||
using civil_minute = civil_time<minute_tag>;
|
||||
using civil_second = civil_time<second_tag>;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Relational operators that work with differently aligned objects.
|
||||
// Always compares all six fields.
|
||||
template <typename T1, typename T2>
|
||||
CONSTEXPR_F bool operator<(const civil_time<T1>& lhs,
|
||||
const civil_time<T2>& rhs) noexcept {
|
||||
return (
|
||||
lhs.year() < rhs.year() ||
|
||||
(lhs.year() == rhs.year() &&
|
||||
(lhs.month() < rhs.month() ||
|
||||
(lhs.month() == rhs.month() &&
|
||||
(lhs.day() < rhs.day() || (lhs.day() == rhs.day() &&
|
||||
(lhs.hour() < rhs.hour() ||
|
||||
(lhs.hour() == rhs.hour() &&
|
||||
(lhs.minute() < rhs.minute() ||
|
||||
(lhs.minute() == rhs.minute() &&
|
||||
(lhs.second() < rhs.second())))))))))));
|
||||
}
|
||||
template <typename T1, typename T2>
|
||||
CONSTEXPR_F bool operator<=(const civil_time<T1>& lhs,
|
||||
const civil_time<T2>& rhs) noexcept {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
template <typename T1, typename T2>
|
||||
CONSTEXPR_F bool operator>=(const civil_time<T1>& lhs,
|
||||
const civil_time<T2>& rhs) noexcept {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
template <typename T1, typename T2>
|
||||
CONSTEXPR_F bool operator>(const civil_time<T1>& lhs,
|
||||
const civil_time<T2>& rhs) noexcept {
|
||||
return rhs < lhs;
|
||||
}
|
||||
template <typename T1, typename T2>
|
||||
CONSTEXPR_F bool operator==(const civil_time<T1>& lhs,
|
||||
const civil_time<T2>& rhs) noexcept {
|
||||
return lhs.year() == rhs.year() && lhs.month() == rhs.month() &&
|
||||
lhs.day() == rhs.day() && lhs.hour() == rhs.hour() &&
|
||||
lhs.minute() == rhs.minute() && lhs.second() == rhs.second();
|
||||
}
|
||||
template <typename T1, typename T2>
|
||||
CONSTEXPR_F bool operator!=(const civil_time<T1>& lhs,
|
||||
const civil_time<T2>& rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum class weekday {
|
||||
monday,
|
||||
tuesday,
|
||||
wednesday,
|
||||
thursday,
|
||||
friday,
|
||||
saturday,
|
||||
sunday,
|
||||
};
|
||||
|
||||
CONSTEXPR_F weekday get_weekday(const civil_second& cs) noexcept {
|
||||
CONSTEXPR_D weekday k_weekday_by_mon_off[13] = {
|
||||
weekday::monday, weekday::tuesday, weekday::wednesday,
|
||||
weekday::thursday, weekday::friday, weekday::saturday,
|
||||
weekday::sunday, weekday::monday, weekday::tuesday,
|
||||
weekday::wednesday, weekday::thursday, weekday::friday,
|
||||
weekday::saturday,
|
||||
};
|
||||
CONSTEXPR_D int k_weekday_offsets[1 + 12] = {
|
||||
-1, 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4,
|
||||
};
|
||||
year_t wd = 2400 + (cs.year() % 400) - (cs.month() < 3);
|
||||
wd += wd / 4 - wd / 100 + wd / 400;
|
||||
wd += k_weekday_offsets[cs.month()] + cs.day();
|
||||
return k_weekday_by_mon_off[wd % 7 + 6];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CONSTEXPR_F civil_day next_weekday(civil_day cd, weekday wd) noexcept {
|
||||
CONSTEXPR_D weekday k_weekdays_forw[14] = {
|
||||
weekday::monday, weekday::tuesday, weekday::wednesday,
|
||||
weekday::thursday, weekday::friday, weekday::saturday,
|
||||
weekday::sunday, weekday::monday, weekday::tuesday,
|
||||
weekday::wednesday, weekday::thursday, weekday::friday,
|
||||
weekday::saturday, weekday::sunday,
|
||||
};
|
||||
weekday base = get_weekday(cd);
|
||||
for (int i = 0;; ++i) {
|
||||
if (base == k_weekdays_forw[i]) {
|
||||
for (int j = i + 1;; ++j) {
|
||||
if (wd == k_weekdays_forw[j]) {
|
||||
return cd + (j - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CONSTEXPR_F civil_day prev_weekday(civil_day cd, weekday wd) noexcept {
|
||||
CONSTEXPR_D weekday k_weekdays_back[14] = {
|
||||
weekday::sunday, weekday::saturday, weekday::friday,
|
||||
weekday::thursday, weekday::wednesday, weekday::tuesday,
|
||||
weekday::monday, weekday::sunday, weekday::saturday,
|
||||
weekday::friday, weekday::thursday, weekday::wednesday,
|
||||
weekday::tuesday, weekday::monday,
|
||||
};
|
||||
weekday base = get_weekday(cd);
|
||||
for (int i = 0;; ++i) {
|
||||
if (base == k_weekdays_back[i]) {
|
||||
for (int j = i + 1;; ++j) {
|
||||
if (wd == k_weekdays_back[j]) {
|
||||
return cd - (j - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CONSTEXPR_F int get_yearday(const civil_second& cs) noexcept {
|
||||
CONSTEXPR_D int k_month_offsets[1 + 12] = {
|
||||
-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
|
||||
};
|
||||
const int feb29 = (cs.month() > 2 && impl::is_leap_year(cs.year()));
|
||||
return k_month_offsets[cs.month()] + feb29 + cs.day();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const civil_year& y);
|
||||
std::ostream& operator<<(std::ostream& os, const civil_month& m);
|
||||
std::ostream& operator<<(std::ostream& os, const civil_day& d);
|
||||
std::ostream& operator<<(std::ostream& os, const civil_hour& h);
|
||||
std::ostream& operator<<(std::ostream& os, const civil_minute& m);
|
||||
std::ostream& operator<<(std::ostream& os, const civil_second& s);
|
||||
std::ostream& operator<<(std::ostream& os, weekday wd);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#undef CONSTEXPR_M
|
||||
#undef CONSTEXPR_F
|
||||
#undef CONSTEXPR_D
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_CIVIL_TIME_DETAIL_H_
|
||||
384
third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/time_zone.h
vendored
Normal file
384
third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/time_zone.h
vendored
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// A library for translating between absolute times (represented by
|
||||
// std::chrono::time_points of the std::chrono::system_clock) and civil
|
||||
// times (represented by cctz::civil_second) using the rules defined by
|
||||
// a time zone (cctz::time_zone).
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// Convenience aliases. Not intended as public API points.
|
||||
template <typename D>
|
||||
using time_point = std::chrono::time_point<std::chrono::system_clock, D>;
|
||||
using seconds = std::chrono::duration<std::int_fast64_t>;
|
||||
using sys_seconds = seconds; // Deprecated. Use cctz::seconds instead.
|
||||
|
||||
namespace detail {
|
||||
template <typename D>
|
||||
inline std::pair<time_point<seconds>, D> split_seconds(
|
||||
const time_point<D>& tp) {
|
||||
auto sec = std::chrono::time_point_cast<seconds>(tp);
|
||||
auto sub = tp - sec;
|
||||
if (sub.count() < 0) {
|
||||
sec -= seconds(1);
|
||||
sub += seconds(1);
|
||||
}
|
||||
return {sec, std::chrono::duration_cast<D>(sub)};
|
||||
}
|
||||
inline std::pair<time_point<seconds>, seconds> split_seconds(
|
||||
const time_point<seconds>& tp) {
|
||||
return {tp, seconds::zero()};
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// cctz::time_zone is an opaque, small, value-type class representing a
|
||||
// geo-political region within which particular rules are used for mapping
|
||||
// between absolute and civil times. Time zones are named using the TZ
|
||||
// identifiers from the IANA Time Zone Database, such as "America/Los_Angeles"
|
||||
// or "Australia/Sydney". Time zones are created from factory functions such
|
||||
// as load_time_zone(). Note: strings like "PST" and "EDT" are not valid TZ
|
||||
// identifiers.
|
||||
//
|
||||
// Example:
|
||||
// cctz::time_zone utc = cctz::utc_time_zone();
|
||||
// cctz::time_zone pst = cctz::fixed_time_zone(std::chrono::hours(-8));
|
||||
// cctz::time_zone loc = cctz::local_time_zone();
|
||||
// cctz::time_zone lax;
|
||||
// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
|
||||
//
|
||||
// See also:
|
||||
// - http://www.iana.org/time-zones
|
||||
// - https://en.wikipedia.org/wiki/Zoneinfo
|
||||
class time_zone {
|
||||
public:
|
||||
time_zone() : time_zone(nullptr) {} // Equivalent to UTC
|
||||
time_zone(const time_zone&) = default;
|
||||
time_zone& operator=(const time_zone&) = default;
|
||||
|
||||
std::string name() const;
|
||||
|
||||
// An absolute_lookup represents the civil time (cctz::civil_second) within
|
||||
// this time_zone at the given absolute time (time_point). There are
|
||||
// additionally a few other fields that may be useful when working with
|
||||
// older APIs, such as std::tm.
|
||||
//
|
||||
// Example:
|
||||
// const cctz::time_zone tz = ...
|
||||
// const auto tp = std::chrono::system_clock::now();
|
||||
// const cctz::time_zone::absolute_lookup al = tz.lookup(tp);
|
||||
struct absolute_lookup {
|
||||
civil_second cs;
|
||||
// Note: The following fields exist for backward compatibility with older
|
||||
// APIs. Accessing these fields directly is a sign of imprudent logic in
|
||||
// the calling code. Modern time-related code should only access this data
|
||||
// indirectly by way of cctz::format().
|
||||
int offset; // civil seconds east of UTC
|
||||
bool is_dst; // is offset non-standard?
|
||||
const char* abbr; // time-zone abbreviation (e.g., "PST")
|
||||
};
|
||||
absolute_lookup lookup(const time_point<seconds>& tp) const;
|
||||
template <typename D>
|
||||
absolute_lookup lookup(const time_point<D>& tp) const {
|
||||
return lookup(detail::split_seconds(tp).first);
|
||||
}
|
||||
|
||||
// A civil_lookup represents the absolute time(s) (time_point) that
|
||||
// correspond to the given civil time (cctz::civil_second) within this
|
||||
// time_zone. Usually the given civil time represents a unique instant
|
||||
// in time, in which case the conversion is unambiguous. However,
|
||||
// within this time zone, the given civil time may be skipped (e.g.,
|
||||
// during a positive UTC offset shift), or repeated (e.g., during a
|
||||
// negative UTC offset shift). To account for these possibilities,
|
||||
// civil_lookup is richer than just a single time_point.
|
||||
//
|
||||
// In all cases the civil_lookup::kind enum will indicate the nature
|
||||
// of the given civil-time argument, and the pre, trans, and post
|
||||
// members will give the absolute time answers using the pre-transition
|
||||
// offset, the transition point itself, and the post-transition offset,
|
||||
// respectively (all three times are equal if kind == UNIQUE). If any
|
||||
// of these three absolute times is outside the representable range of a
|
||||
// time_point<seconds> the field is set to its maximum/minimum value.
|
||||
//
|
||||
// Example:
|
||||
// cctz::time_zone lax;
|
||||
// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
|
||||
//
|
||||
// // A unique civil time.
|
||||
// auto jan01 = lax.lookup(cctz::civil_second(2011, 1, 1, 0, 0, 0));
|
||||
// // jan01.kind == cctz::time_zone::civil_lookup::UNIQUE
|
||||
// // jan01.pre is 2011/01/01 00:00:00 -0800
|
||||
// // jan01.trans is 2011/01/01 00:00:00 -0800
|
||||
// // jan01.post is 2011/01/01 00:00:00 -0800
|
||||
//
|
||||
// // A Spring DST transition, when there is a gap in civil time.
|
||||
// auto mar13 = lax.lookup(cctz::civil_second(2011, 3, 13, 2, 15, 0));
|
||||
// // mar13.kind == cctz::time_zone::civil_lookup::SKIPPED
|
||||
// // mar13.pre is 2011/03/13 03:15:00 -0700
|
||||
// // mar13.trans is 2011/03/13 03:00:00 -0700
|
||||
// // mar13.post is 2011/03/13 01:15:00 -0800
|
||||
//
|
||||
// // A Fall DST transition, when civil times are repeated.
|
||||
// auto nov06 = lax.lookup(cctz::civil_second(2011, 11, 6, 1, 15, 0));
|
||||
// // nov06.kind == cctz::time_zone::civil_lookup::REPEATED
|
||||
// // nov06.pre is 2011/11/06 01:15:00 -0700
|
||||
// // nov06.trans is 2011/11/06 01:00:00 -0800
|
||||
// // nov06.post is 2011/11/06 01:15:00 -0800
|
||||
struct civil_lookup {
|
||||
enum civil_kind {
|
||||
UNIQUE, // the civil time was singular (pre == trans == post)
|
||||
SKIPPED, // the civil time did not exist (pre >= trans > post)
|
||||
REPEATED, // the civil time was ambiguous (pre < trans <= post)
|
||||
} kind;
|
||||
time_point<seconds> pre; // uses the pre-transition offset
|
||||
time_point<seconds> trans; // instant of civil-offset change
|
||||
time_point<seconds> post; // uses the post-transition offset
|
||||
};
|
||||
civil_lookup lookup(const civil_second& cs) const;
|
||||
|
||||
// Finds the time of the next/previous offset change in this time zone.
|
||||
//
|
||||
// By definition, next_transition(tp, &trans) returns false when tp has
|
||||
// its maximum value, and prev_transition(tp, &trans) returns false
|
||||
// when tp has its minimum value. If the zone has no transitions, the
|
||||
// result will also be false no matter what the argument.
|
||||
//
|
||||
// Otherwise, when tp has its minimum value, next_transition(tp, &trans)
|
||||
// returns true and sets trans to the first recorded transition. Chains
|
||||
// of calls to next_transition()/prev_transition() will eventually return
|
||||
// false, but it is unspecified exactly when next_transition(tp, &trans)
|
||||
// jumps to false, or what time is set by prev_transition(tp, &trans) for
|
||||
// a very distant tp.
|
||||
//
|
||||
// Note: Enumeration of time-zone transitions is for informational purposes
|
||||
// only. Modern time-related code should not care about when offset changes
|
||||
// occur.
|
||||
//
|
||||
// Example:
|
||||
// cctz::time_zone nyc;
|
||||
// if (!cctz::load_time_zone("America/New_York", &nyc)) { ... }
|
||||
// const auto now = std::chrono::system_clock::now();
|
||||
// auto tp = cctz::time_point<cctz::seconds>::min();
|
||||
// cctz::time_zone::civil_transition trans;
|
||||
// while (tp <= now && nyc.next_transition(tp, &trans)) {
|
||||
// // transition: trans.from -> trans.to
|
||||
// tp = nyc.lookup(trans.to).trans;
|
||||
// }
|
||||
struct civil_transition {
|
||||
civil_second from; // the civil time we jump from
|
||||
civil_second to; // the civil time we jump to
|
||||
};
|
||||
bool next_transition(const time_point<seconds>& tp,
|
||||
civil_transition* trans) const;
|
||||
template <typename D>
|
||||
bool next_transition(const time_point<D>& tp, civil_transition* trans) const {
|
||||
return next_transition(detail::split_seconds(tp).first, trans);
|
||||
}
|
||||
bool prev_transition(const time_point<seconds>& tp,
|
||||
civil_transition* trans) const;
|
||||
template <typename D>
|
||||
bool prev_transition(const time_point<D>& tp, civil_transition* trans) const {
|
||||
return prev_transition(detail::split_seconds(tp).first, trans);
|
||||
}
|
||||
|
||||
// version() and description() provide additional information about the
|
||||
// time zone. The content of each of the returned strings is unspecified,
|
||||
// however, when the IANA Time Zone Database is the underlying data source
|
||||
// the version() string will be in the familar form (e.g, "2018e") or
|
||||
// empty when unavailable.
|
||||
//
|
||||
// Note: These functions are for informational or testing purposes only.
|
||||
std::string version() const; // empty when unknown
|
||||
std::string description() const;
|
||||
|
||||
// Relational operators.
|
||||
friend bool operator==(time_zone lhs, time_zone rhs) {
|
||||
return &lhs.effective_impl() == &rhs.effective_impl();
|
||||
}
|
||||
friend bool operator!=(time_zone lhs, time_zone rhs) { return !(lhs == rhs); }
|
||||
|
||||
template <typename H>
|
||||
friend H AbslHashValue(H h, time_zone tz) {
|
||||
return H::combine(std::move(h), &tz.effective_impl());
|
||||
}
|
||||
|
||||
class Impl;
|
||||
|
||||
private:
|
||||
explicit time_zone(const Impl* impl) : impl_(impl) {}
|
||||
const Impl& effective_impl() const; // handles implicit UTC
|
||||
const Impl* impl_;
|
||||
};
|
||||
|
||||
// Loads the named time zone. May perform I/O on the initial load.
|
||||
// If the name is invalid, or some other kind of error occurs, returns
|
||||
// false and "*tz" is set to the UTC time zone.
|
||||
bool load_time_zone(const std::string& name, time_zone* tz);
|
||||
|
||||
// Returns a time_zone representing UTC. Cannot fail.
|
||||
time_zone utc_time_zone();
|
||||
|
||||
// Returns a time zone that is a fixed offset (seconds east) from UTC.
|
||||
// Note: If the absolute value of the offset is greater than 24 hours
|
||||
// you'll get UTC (i.e., zero offset) instead.
|
||||
time_zone fixed_time_zone(const seconds& offset);
|
||||
|
||||
// Returns a time zone representing the local time zone. Falls back to UTC.
|
||||
// Note: local_time_zone.name() may only be something like "localtime".
|
||||
time_zone local_time_zone();
|
||||
|
||||
// Returns the civil time (cctz::civil_second) within the given time zone at
|
||||
// the given absolute time (time_point). Since the additional fields provided
|
||||
// by the time_zone::absolute_lookup struct should rarely be needed in modern
|
||||
// code, this convert() function is simpler and should be preferred.
|
||||
template <typename D>
|
||||
inline civil_second convert(const time_point<D>& tp, const time_zone& tz) {
|
||||
return tz.lookup(tp).cs;
|
||||
}
|
||||
|
||||
// Returns the absolute time (time_point) that corresponds to the given civil
|
||||
// time within the given time zone. If the civil time is not unique (i.e., if
|
||||
// it was either repeated or non-existent), then the returned time_point is
|
||||
// the best estimate that preserves relative order. That is, this function
|
||||
// guarantees that if cs1 < cs2, then convert(cs1, tz) <= convert(cs2, tz).
|
||||
inline time_point<seconds> convert(const civil_second& cs,
|
||||
const time_zone& tz) {
|
||||
const time_zone::civil_lookup cl = tz.lookup(cs);
|
||||
if (cl.kind == time_zone::civil_lookup::SKIPPED) return cl.trans;
|
||||
return cl.pre;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
using femtoseconds = std::chrono::duration<std::int_fast64_t, std::femto>;
|
||||
std::string format(const std::string&, const time_point<seconds>&,
|
||||
const femtoseconds&, const time_zone&);
|
||||
bool parse(const std::string&, const std::string&, const time_zone&,
|
||||
time_point<seconds>*, femtoseconds*, std::string* err = nullptr);
|
||||
} // namespace detail
|
||||
|
||||
// Formats the given time_point in the given cctz::time_zone according to
|
||||
// the provided format string. Uses strftime()-like formatting options,
|
||||
// with the following extensions:
|
||||
//
|
||||
// - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
|
||||
// - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
|
||||
// - %E#S - Seconds with # digits of fractional precision
|
||||
// - %E*S - Seconds with full fractional precision (a literal '*')
|
||||
// - %E#f - Fractional seconds with # digits of precision
|
||||
// - %E*f - Fractional seconds with full precision (a literal '*')
|
||||
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
|
||||
//
|
||||
// Note that %E0S behaves like %S, and %E0f produces no characters. In
|
||||
// contrast %E*f always produces at least one digit, which may be '0'.
|
||||
//
|
||||
// Note that %Y produces as many characters as it takes to fully render the
|
||||
// year. A year outside of [-999:9999] when formatted with %E4Y will produce
|
||||
// more than four characters, just like %Y.
|
||||
//
|
||||
// Tip: Format strings should include the UTC offset (e.g., %z, %Ez, or %E*z)
|
||||
// so that the resulting string uniquely identifies an absolute time.
|
||||
//
|
||||
// Example:
|
||||
// cctz::time_zone lax;
|
||||
// if (!cctz::load_time_zone("America/Los_Angeles", &lax)) { ... }
|
||||
// auto tp = cctz::convert(cctz::civil_second(2013, 1, 2, 3, 4, 5), lax);
|
||||
// std::string f = cctz::format("%H:%M:%S", tp, lax); // "03:04:05"
|
||||
// f = cctz::format("%H:%M:%E3S", tp, lax); // "03:04:05.000"
|
||||
template <typename D>
|
||||
inline std::string format(const std::string& fmt, const time_point<D>& tp,
|
||||
const time_zone& tz) {
|
||||
const auto p = detail::split_seconds(tp);
|
||||
const auto n = std::chrono::duration_cast<detail::femtoseconds>(p.second);
|
||||
return detail::format(fmt, p.first, n, tz);
|
||||
}
|
||||
|
||||
// Parses an input string according to the provided format string and
|
||||
// returns the corresponding time_point. Uses strftime()-like formatting
|
||||
// options, with the same extensions as cctz::format(), but with the
|
||||
// exceptions that %E#S is interpreted as %E*S, and %E#f as %E*f. %Ez
|
||||
// and %E*z also accept the same inputs.
|
||||
//
|
||||
// %Y consumes as many numeric characters as it can, so the matching data
|
||||
// should always be terminated with a non-numeric. %E4Y always consumes
|
||||
// exactly four characters, including any sign.
|
||||
//
|
||||
// Unspecified fields are taken from the default date and time of ...
|
||||
//
|
||||
// "1970-01-01 00:00:00.0 +0000"
|
||||
//
|
||||
// For example, parsing a string of "15:45" (%H:%M) will return a time_point
|
||||
// that represents "1970-01-01 15:45:00.0 +0000".
|
||||
//
|
||||
// Note that parse() returns time instants, so it makes most sense to parse
|
||||
// fully-specified date/time strings that include a UTC offset (%z, %Ez, or
|
||||
// %E*z).
|
||||
//
|
||||
// Note also that parse() only heeds the fields year, month, day, hour,
|
||||
// minute, (fractional) second, and UTC offset. Other fields, like weekday (%a
|
||||
// or %A), while parsed for syntactic validity, are ignored in the conversion.
|
||||
//
|
||||
// Date and time fields that are out-of-range will be treated as errors rather
|
||||
// than normalizing them like cctz::civil_second() would do. For example, it
|
||||
// is an error to parse the date "Oct 32, 2013" because 32 is out of range.
|
||||
//
|
||||
// A second of ":60" is normalized to ":00" of the following minute with
|
||||
// fractional seconds discarded. The following table shows how the given
|
||||
// seconds and subseconds will be parsed:
|
||||
//
|
||||
// "59.x" -> 59.x // exact
|
||||
// "60.x" -> 00.0 // normalized
|
||||
// "00.x" -> 00.x // exact
|
||||
//
|
||||
// Errors are indicated by returning false.
|
||||
//
|
||||
// Example:
|
||||
// const cctz::time_zone tz = ...
|
||||
// std::chrono::system_clock::time_point tp;
|
||||
// if (cctz::parse("%Y-%m-%d", "2015-10-09", tz, &tp)) {
|
||||
// ...
|
||||
// }
|
||||
template <typename D>
|
||||
inline bool parse(const std::string& fmt, const std::string& input,
|
||||
const time_zone& tz, time_point<D>* tpp) {
|
||||
time_point<seconds> sec;
|
||||
detail::femtoseconds fs;
|
||||
const bool b = detail::parse(fmt, input, tz, &sec, &fs);
|
||||
if (b) {
|
||||
// TODO: Return false if unrepresentable as a time_point<D>.
|
||||
*tpp = std::chrono::time_point_cast<D>(sec);
|
||||
*tpp += std::chrono::duration_cast<D>(fs);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
|
||||
102
third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h
vendored
Normal file
102
third_party/abseil_cpp/absl/time/internal/cctz/include/cctz/zone_info_source.h
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// A stdio-like interface for providing zoneinfo data for a particular zone.
|
||||
class ZoneInfoSource {
|
||||
public:
|
||||
virtual ~ZoneInfoSource();
|
||||
|
||||
virtual std::size_t Read(void* ptr, std::size_t size) = 0; // like fread()
|
||||
virtual int Skip(std::size_t offset) = 0; // like fseek()
|
||||
|
||||
// Until the zoneinfo data supports versioning information, we provide
|
||||
// a way for a ZoneInfoSource to indicate it out-of-band. The default
|
||||
// implementation returns an empty string.
|
||||
virtual std::string Version() const;
|
||||
};
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz_extension {
|
||||
|
||||
// A function-pointer type for a factory that returns a ZoneInfoSource
|
||||
// given the name of a time zone and a fallback factory. Returns null
|
||||
// when the data for the named zone cannot be found.
|
||||
using ZoneInfoSourceFactory =
|
||||
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> (*)(
|
||||
const std::string&,
|
||||
const std::function<std::unique_ptr<
|
||||
absl::time_internal::cctz::ZoneInfoSource>(const std::string&)>&);
|
||||
|
||||
// The user can control the mapping of zone names to zoneinfo data by
|
||||
// providing a definition for cctz_extension::zone_info_source_factory.
|
||||
// For example, given functions my_factory() and my_other_factory() that
|
||||
// can return a ZoneInfoSource for a named zone, we could inject them into
|
||||
// cctz::load_time_zone() with:
|
||||
//
|
||||
// namespace cctz_extension {
|
||||
// namespace {
|
||||
// std::unique_ptr<cctz::ZoneInfoSource> CustomFactory(
|
||||
// const std::string& name,
|
||||
// const std::function<std::unique_ptr<cctz::ZoneInfoSource>(
|
||||
// const std::string& name)>& fallback_factory) {
|
||||
// if (auto zip = my_factory(name)) return zip;
|
||||
// if (auto zip = fallback_factory(name)) return zip;
|
||||
// if (auto zip = my_other_factory(name)) return zip;
|
||||
// return nullptr;
|
||||
// }
|
||||
// } // namespace
|
||||
// ZoneInfoSourceFactory zone_info_source_factory = CustomFactory;
|
||||
// } // namespace cctz_extension
|
||||
//
|
||||
// This might be used, say, to use zoneinfo data embedded in the program,
|
||||
// or read from a (possibly compressed) file archive, or both.
|
||||
//
|
||||
// cctz_extension::zone_info_source_factory() will be called:
|
||||
// (1) from the same thread as the cctz::load_time_zone() call,
|
||||
// (2) only once for any zone name, and
|
||||
// (3) serially (i.e., no concurrent execution).
|
||||
//
|
||||
// The fallback factory obtains zoneinfo data by reading files in ${TZDIR},
|
||||
// and it is used automatically when no zone_info_source_factory definition
|
||||
// is linked into the program.
|
||||
extern ZoneInfoSourceFactory zone_info_source_factory;
|
||||
|
||||
} // namespace cctz_extension
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_ZONE_INFO_SOURCE_H_
|
||||
1030
third_party/abseil_cpp/absl/time/internal/cctz/src/cctz_benchmark.cc
vendored
Normal file
1030
third_party/abseil_cpp/absl/time/internal/cctz/src/cctz_benchmark.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
94
third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_detail.cc
vendored
Normal file
94
third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_detail.cc
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time_detail.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
namespace detail {
|
||||
|
||||
// Output stream operators output a format matching YYYY-MM-DDThh:mm:ss,
|
||||
// while omitting fields inferior to the type's alignment. For example,
|
||||
// civil_day is formatted only as YYYY-MM-DD.
|
||||
std::ostream& operator<<(std::ostream& os, const civil_year& y) {
|
||||
std::stringstream ss;
|
||||
ss << y.year(); // No padding.
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_month& m) {
|
||||
std::stringstream ss;
|
||||
ss << civil_year(m) << '-';
|
||||
ss << std::setfill('0') << std::setw(2) << m.month();
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_day& d) {
|
||||
std::stringstream ss;
|
||||
ss << civil_month(d) << '-';
|
||||
ss << std::setfill('0') << std::setw(2) << d.day();
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_hour& h) {
|
||||
std::stringstream ss;
|
||||
ss << civil_day(h) << 'T';
|
||||
ss << std::setfill('0') << std::setw(2) << h.hour();
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_minute& m) {
|
||||
std::stringstream ss;
|
||||
ss << civil_hour(m) << ':';
|
||||
ss << std::setfill('0') << std::setw(2) << m.minute();
|
||||
return os << ss.str();
|
||||
}
|
||||
std::ostream& operator<<(std::ostream& os, const civil_second& s) {
|
||||
std::stringstream ss;
|
||||
ss << civil_minute(s) << ':';
|
||||
ss << std::setfill('0') << std::setw(2) << s.second();
|
||||
return os << ss.str();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, weekday wd) {
|
||||
switch (wd) {
|
||||
case weekday::monday:
|
||||
return os << "Monday";
|
||||
case weekday::tuesday:
|
||||
return os << "Tuesday";
|
||||
case weekday::wednesday:
|
||||
return os << "Wednesday";
|
||||
case weekday::thursday:
|
||||
return os << "Thursday";
|
||||
case weekday::friday:
|
||||
return os << "Friday";
|
||||
case weekday::saturday:
|
||||
return os << "Saturday";
|
||||
case weekday::sunday:
|
||||
return os << "Sunday";
|
||||
}
|
||||
return os; // Should never get here, but -Wreturn-type may warn without this.
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
1056
third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_test.cc
vendored
Normal file
1056
third_party/abseil_cpp/absl/time/internal/cctz/src/civil_time_test.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
140
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.cc
vendored
Normal file
140
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.cc
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "time_zone_fixed.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
// The prefix used for the internal names of fixed-offset zones.
|
||||
const char kFixedZonePrefix[] = "Fixed/UTC";
|
||||
|
||||
const char kDigits[] = "0123456789";
|
||||
|
||||
char* Format02d(char* p, int v) {
|
||||
*p++ = kDigits[(v / 10) % 10];
|
||||
*p++ = kDigits[v % 10];
|
||||
return p;
|
||||
}
|
||||
|
||||
int Parse02d(const char* p) {
|
||||
if (const char* ap = std::strchr(kDigits, *p)) {
|
||||
int v = static_cast<int>(ap - kDigits);
|
||||
if (const char* bp = std::strchr(kDigits, *++p)) {
|
||||
return (v * 10) + static_cast<int>(bp - kDigits);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool FixedOffsetFromName(const std::string& name, seconds* offset) {
|
||||
if (name.compare(0, std::string::npos, "UTC", 3) == 0) {
|
||||
*offset = seconds::zero();
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
|
||||
const char* const ep = kFixedZonePrefix + prefix_len;
|
||||
if (name.size() != prefix_len + 9) // <prefix>+99:99:99
|
||||
return false;
|
||||
if (!std::equal(kFixedZonePrefix, ep, name.begin())) return false;
|
||||
const char* np = name.data() + prefix_len;
|
||||
if (np[0] != '+' && np[0] != '-') return false;
|
||||
if (np[3] != ':' || np[6] != ':') // see note below about large offsets
|
||||
return false;
|
||||
|
||||
int hours = Parse02d(np + 1);
|
||||
if (hours == -1) return false;
|
||||
int mins = Parse02d(np + 4);
|
||||
if (mins == -1) return false;
|
||||
int secs = Parse02d(np + 7);
|
||||
if (secs == -1) return false;
|
||||
|
||||
secs += ((hours * 60) + mins) * 60;
|
||||
if (secs > 24 * 60 * 60) return false; // outside supported offset range
|
||||
*offset = seconds(secs * (np[0] == '-' ? -1 : 1)); // "-" means west
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string FixedOffsetToName(const seconds& offset) {
|
||||
if (offset == seconds::zero()) return "UTC";
|
||||
if (offset < std::chrono::hours(-24) || offset > std::chrono::hours(24)) {
|
||||
// We don't support fixed-offset zones more than 24 hours
|
||||
// away from UTC to avoid complications in rendering such
|
||||
// offsets and to (somewhat) limit the total number of zones.
|
||||
return "UTC";
|
||||
}
|
||||
int offset_seconds = static_cast<int>(offset.count());
|
||||
const char sign = (offset_seconds < 0 ? '-' : '+');
|
||||
int offset_minutes = offset_seconds / 60;
|
||||
offset_seconds %= 60;
|
||||
if (sign == '-') {
|
||||
if (offset_seconds > 0) {
|
||||
offset_seconds -= 60;
|
||||
offset_minutes += 1;
|
||||
}
|
||||
offset_seconds = -offset_seconds;
|
||||
offset_minutes = -offset_minutes;
|
||||
}
|
||||
int offset_hours = offset_minutes / 60;
|
||||
offset_minutes %= 60;
|
||||
const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
|
||||
char buf[prefix_len + sizeof("-24:00:00")];
|
||||
char* ep = std::copy(kFixedZonePrefix, kFixedZonePrefix + prefix_len, buf);
|
||||
*ep++ = sign;
|
||||
ep = Format02d(ep, offset_hours);
|
||||
*ep++ = ':';
|
||||
ep = Format02d(ep, offset_minutes);
|
||||
*ep++ = ':';
|
||||
ep = Format02d(ep, offset_seconds);
|
||||
*ep++ = '\0';
|
||||
assert(ep == buf + sizeof(buf));
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::string FixedOffsetToAbbr(const seconds& offset) {
|
||||
std::string abbr = FixedOffsetToName(offset);
|
||||
const std::size_t prefix_len = sizeof(kFixedZonePrefix) - 1;
|
||||
if (abbr.size() == prefix_len + 9) { // <prefix>+99:99:99
|
||||
abbr.erase(0, prefix_len); // +99:99:99
|
||||
abbr.erase(6, 1); // +99:9999
|
||||
abbr.erase(3, 1); // +999999
|
||||
if (abbr[5] == '0' && abbr[6] == '0') { // +999900
|
||||
abbr.erase(5, 2); // +9999
|
||||
if (abbr[3] == '0' && abbr[4] == '0') { // +9900
|
||||
abbr.erase(3, 2); // +99
|
||||
}
|
||||
}
|
||||
}
|
||||
return abbr;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
52
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.h
vendored
Normal file
52
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_fixed.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// Helper functions for dealing with the names and abbreviations
|
||||
// of time zones that are a fixed offset (seconds east) from UTC.
|
||||
// FixedOffsetFromName() extracts the offset from a valid fixed-offset
|
||||
// name, while FixedOffsetToName() and FixedOffsetToAbbr() generate
|
||||
// the canonical zone name and abbreviation respectively for the given
|
||||
// offset.
|
||||
//
|
||||
// A fixed-offset name looks like "Fixed/UTC<+-><hours>:<mins>:<secs>".
|
||||
// Its abbreviation is of the form "UTC(<+->H?H(MM(SS)?)?)?" where the
|
||||
// optional pieces are omitted when their values are zero. (Note that
|
||||
// the sign is the opposite of that used in a POSIX TZ specification.)
|
||||
//
|
||||
// Note: FixedOffsetFromName() fails on syntax errors or when the parsed
|
||||
// offset exceeds 24 hours. FixedOffsetToName() and FixedOffsetToAbbr()
|
||||
// both produce "UTC" when the argument offset exceeds 24 hours.
|
||||
bool FixedOffsetFromName(const std::string& name, seconds* offset);
|
||||
std::string FixedOffsetToName(const seconds& offset);
|
||||
std::string FixedOffsetToAbbr(const seconds& offset);
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_FIXED_H_
|
||||
922
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format.cc
vendored
Normal file
922
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format.cc
vendored
Normal file
|
|
@ -0,0 +1,922 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#if !defined(HAS_STRPTIME)
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#define HAS_STRPTIME 1 // assume everyone has strptime() except windows
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(HAS_STRPTIME) && HAS_STRPTIME
|
||||
#if !defined(_XOPEN_SOURCE)
|
||||
#define _XOPEN_SOURCE // Definedness suffices for strptime.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
// Include time.h directly since, by C++ standards, ctime doesn't have to
|
||||
// declare strptime.
|
||||
#include <time.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#if !HAS_STRPTIME
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "time_zone_if.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
namespace detail {
|
||||
|
||||
namespace {
|
||||
|
||||
#if !HAS_STRPTIME
|
||||
// Build a strptime() using C++11's std::get_time().
|
||||
char* strptime(const char* s, const char* fmt, std::tm* tm) {
|
||||
std::istringstream input(s);
|
||||
input >> std::get_time(tm, fmt);
|
||||
if (input.fail()) return nullptr;
|
||||
return const_cast<char*>(s) +
|
||||
(input.eof() ? strlen(s) : static_cast<std::size_t>(input.tellg()));
|
||||
}
|
||||
#endif
|
||||
|
||||
std::tm ToTM(const time_zone::absolute_lookup& al) {
|
||||
std::tm tm{};
|
||||
tm.tm_sec = al.cs.second();
|
||||
tm.tm_min = al.cs.minute();
|
||||
tm.tm_hour = al.cs.hour();
|
||||
tm.tm_mday = al.cs.day();
|
||||
tm.tm_mon = al.cs.month() - 1;
|
||||
|
||||
// Saturate tm.tm_year is cases of over/underflow.
|
||||
if (al.cs.year() < std::numeric_limits<int>::min() + 1900) {
|
||||
tm.tm_year = std::numeric_limits<int>::min();
|
||||
} else if (al.cs.year() - 1900 > std::numeric_limits<int>::max()) {
|
||||
tm.tm_year = std::numeric_limits<int>::max();
|
||||
} else {
|
||||
tm.tm_year = static_cast<int>(al.cs.year() - 1900);
|
||||
}
|
||||
|
||||
switch (get_weekday(al.cs)) {
|
||||
case weekday::sunday:
|
||||
tm.tm_wday = 0;
|
||||
break;
|
||||
case weekday::monday:
|
||||
tm.tm_wday = 1;
|
||||
break;
|
||||
case weekday::tuesday:
|
||||
tm.tm_wday = 2;
|
||||
break;
|
||||
case weekday::wednesday:
|
||||
tm.tm_wday = 3;
|
||||
break;
|
||||
case weekday::thursday:
|
||||
tm.tm_wday = 4;
|
||||
break;
|
||||
case weekday::friday:
|
||||
tm.tm_wday = 5;
|
||||
break;
|
||||
case weekday::saturday:
|
||||
tm.tm_wday = 6;
|
||||
break;
|
||||
}
|
||||
tm.tm_yday = get_yearday(al.cs) - 1;
|
||||
tm.tm_isdst = al.is_dst ? 1 : 0;
|
||||
return tm;
|
||||
}
|
||||
|
||||
const char kDigits[] = "0123456789";
|
||||
|
||||
// Formats a 64-bit integer in the given field width. Note that it is up
|
||||
// to the caller of Format64() [and Format02d()/FormatOffset()] to ensure
|
||||
// that there is sufficient space before ep to hold the conversion.
|
||||
char* Format64(char* ep, int width, std::int_fast64_t v) {
|
||||
bool neg = false;
|
||||
if (v < 0) {
|
||||
--width;
|
||||
neg = true;
|
||||
if (v == std::numeric_limits<std::int_fast64_t>::min()) {
|
||||
// Avoid negating minimum value.
|
||||
std::int_fast64_t last_digit = -(v % 10);
|
||||
v /= 10;
|
||||
if (last_digit < 0) {
|
||||
++v;
|
||||
last_digit += 10;
|
||||
}
|
||||
--width;
|
||||
*--ep = kDigits[last_digit];
|
||||
}
|
||||
v = -v;
|
||||
}
|
||||
do {
|
||||
--width;
|
||||
*--ep = kDigits[v % 10];
|
||||
} while (v /= 10);
|
||||
while (--width >= 0) *--ep = '0'; // zero pad
|
||||
if (neg) *--ep = '-';
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Formats [0 .. 99] as %02d.
|
||||
char* Format02d(char* ep, int v) {
|
||||
*--ep = kDigits[v % 10];
|
||||
*--ep = kDigits[(v / 10) % 10];
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Formats a UTC offset, like +00:00.
|
||||
char* FormatOffset(char* ep, int offset, const char* mode) {
|
||||
// TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
|
||||
// generate a "negative zero" when we're formatting a zero offset
|
||||
// as the result of a failed load_time_zone().
|
||||
char sign = '+';
|
||||
if (offset < 0) {
|
||||
offset = -offset; // bounded by 24h so no overflow
|
||||
sign = '-';
|
||||
}
|
||||
const int seconds = offset % 60;
|
||||
const int minutes = (offset /= 60) % 60;
|
||||
const int hours = offset /= 60;
|
||||
const char sep = mode[0];
|
||||
const bool ext = (sep != '\0' && mode[1] == '*');
|
||||
const bool ccc = (ext && mode[2] == ':');
|
||||
if (ext && (!ccc || seconds != 0)) {
|
||||
ep = Format02d(ep, seconds);
|
||||
*--ep = sep;
|
||||
} else {
|
||||
// If we're not rendering seconds, sub-minute negative offsets
|
||||
// should get a positive sign (e.g., offset=-10s => "+00:00").
|
||||
if (hours == 0 && minutes == 0) sign = '+';
|
||||
}
|
||||
if (!ccc || minutes != 0 || seconds != 0) {
|
||||
ep = Format02d(ep, minutes);
|
||||
if (sep != '\0') *--ep = sep;
|
||||
}
|
||||
ep = Format02d(ep, hours);
|
||||
*--ep = sign;
|
||||
return ep;
|
||||
}
|
||||
|
||||
// Formats a std::tm using strftime(3).
|
||||
void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) {
|
||||
// strftime(3) returns the number of characters placed in the output
|
||||
// array (which may be 0 characters). It also returns 0 to indicate
|
||||
// an error, like the array wasn't large enough. To accommodate this,
|
||||
// the following code grows the buffer size from 2x the format string
|
||||
// length up to 32x.
|
||||
for (std::size_t i = 2; i != 32; i *= 2) {
|
||||
std::size_t buf_size = fmt.size() * i;
|
||||
std::vector<char> buf(buf_size);
|
||||
if (std::size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) {
|
||||
out->append(&buf[0], len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used for %E#S/%E#f specifiers and for data values in parse().
|
||||
template <typename T>
|
||||
const char* ParseInt(const char* dp, int width, T min, T max, T* vp) {
|
||||
if (dp != nullptr) {
|
||||
const T kmin = std::numeric_limits<T>::min();
|
||||
bool erange = false;
|
||||
bool neg = false;
|
||||
T value = 0;
|
||||
if (*dp == '-') {
|
||||
neg = true;
|
||||
if (width <= 0 || --width != 0) {
|
||||
++dp;
|
||||
} else {
|
||||
dp = nullptr; // width was 1
|
||||
}
|
||||
}
|
||||
if (const char* const bp = dp) {
|
||||
while (const char* cp = strchr(kDigits, *dp)) {
|
||||
int d = static_cast<int>(cp - kDigits);
|
||||
if (d >= 10) break;
|
||||
if (value < kmin / 10) {
|
||||
erange = true;
|
||||
break;
|
||||
}
|
||||
value *= 10;
|
||||
if (value < kmin + d) {
|
||||
erange = true;
|
||||
break;
|
||||
}
|
||||
value -= d;
|
||||
dp += 1;
|
||||
if (width > 0 && --width == 0) break;
|
||||
}
|
||||
if (dp != bp && !erange && (neg || value != kmin)) {
|
||||
if (!neg || value != 0) {
|
||||
if (!neg) value = -value; // make positive
|
||||
if (min <= value && value <= max) {
|
||||
*vp = value;
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
// The number of base-10 digits that can be represented by a signed 64-bit
|
||||
// integer. That is, 10^kDigits10_64 <= 2^63 - 1 < 10^(kDigits10_64 + 1).
|
||||
const int kDigits10_64 = 18;
|
||||
|
||||
// 10^n for everything that can be represented by a signed 64-bit integer.
|
||||
const std::int_fast64_t kExp10[kDigits10_64 + 1] = {
|
||||
1,
|
||||
10,
|
||||
100,
|
||||
1000,
|
||||
10000,
|
||||
100000,
|
||||
1000000,
|
||||
10000000,
|
||||
100000000,
|
||||
1000000000,
|
||||
10000000000,
|
||||
100000000000,
|
||||
1000000000000,
|
||||
10000000000000,
|
||||
100000000000000,
|
||||
1000000000000000,
|
||||
10000000000000000,
|
||||
100000000000000000,
|
||||
1000000000000000000,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// Uses strftime(3) to format the given Time. The following extended format
|
||||
// specifiers are also supported:
|
||||
//
|
||||
// - %Ez - RFC3339-compatible numeric UTC offset (+hh:mm or -hh:mm)
|
||||
// - %E*z - Full-resolution numeric UTC offset (+hh:mm:ss or -hh:mm:ss)
|
||||
// - %E#S - Seconds with # digits of fractional precision
|
||||
// - %E*S - Seconds with full fractional precision (a literal '*')
|
||||
// - %E4Y - Four-character years (-999 ... -001, 0000, 0001 ... 9999)
|
||||
//
|
||||
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
|
||||
// handled internally for performance reasons. strftime(3) is slow due to
|
||||
// a POSIX requirement to respect changes to ${TZ}.
|
||||
//
|
||||
// The TZ/GNU %s extension is handled internally because strftime() has
|
||||
// to use mktime() to generate it, and that assumes the local time zone.
|
||||
//
|
||||
// We also handle the %z and %Z specifiers to accommodate platforms that do
|
||||
// not support the tm_gmtoff and tm_zone extensions to std::tm.
|
||||
//
|
||||
// Requires that zero() <= fs < seconds(1).
|
||||
std::string format(const std::string& format, const time_point<seconds>& tp,
|
||||
const detail::femtoseconds& fs, const time_zone& tz) {
|
||||
std::string result;
|
||||
result.reserve(format.size()); // A reasonable guess for the result size.
|
||||
const time_zone::absolute_lookup al = tz.lookup(tp);
|
||||
const std::tm tm = ToTM(al);
|
||||
|
||||
// Scratch buffer for internal conversions.
|
||||
char buf[3 + kDigits10_64]; // enough for longest conversion
|
||||
char* const ep = buf + sizeof(buf);
|
||||
char* bp; // works back from ep
|
||||
|
||||
// Maintain three, disjoint subsequences that span format.
|
||||
// [format.begin() ... pending) : already formatted into result
|
||||
// [pending ... cur) : formatting pending, but no special cases
|
||||
// [cur ... format.end()) : unexamined
|
||||
// Initially, everything is in the unexamined part.
|
||||
const char* pending = format.c_str(); // NUL terminated
|
||||
const char* cur = pending;
|
||||
const char* end = pending + format.length();
|
||||
|
||||
while (cur != end) { // while something is unexamined
|
||||
// Moves cur to the next percent sign.
|
||||
const char* start = cur;
|
||||
while (cur != end && *cur != '%') ++cur;
|
||||
|
||||
// If the new pending text is all ordinary, copy it out.
|
||||
if (cur != start && pending == start) {
|
||||
result.append(pending, static_cast<std::size_t>(cur - pending));
|
||||
pending = start = cur;
|
||||
}
|
||||
|
||||
// Span the sequential percent signs.
|
||||
const char* percent = cur;
|
||||
while (cur != end && *cur == '%') ++cur;
|
||||
|
||||
// If the new pending text is all percents, copy out one
|
||||
// percent for every matched pair, then skip those pairs.
|
||||
if (cur != start && pending == start) {
|
||||
std::size_t escaped = static_cast<std::size_t>(cur - pending) / 2;
|
||||
result.append(pending, escaped);
|
||||
pending += escaped * 2;
|
||||
// Also copy out a single trailing percent.
|
||||
if (pending != cur && cur == end) {
|
||||
result.push_back(*pending++);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop unless we have an unescaped percent.
|
||||
if (cur == end || (cur - percent) % 2 == 0) continue;
|
||||
|
||||
// Simple specifiers that we handle ourselves.
|
||||
if (strchr("YmdeHMSzZs%", *cur)) {
|
||||
if (cur - 1 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 1), tm);
|
||||
}
|
||||
switch (*cur) {
|
||||
case 'Y':
|
||||
// This avoids the tm.tm_year overflow problem for %Y, however
|
||||
// tm.tm_year will still be used by other specifiers like %D.
|
||||
bp = Format64(ep, 0, al.cs.year());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'm':
|
||||
bp = Format02d(ep, al.cs.month());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'd':
|
||||
case 'e':
|
||||
bp = Format02d(ep, al.cs.day());
|
||||
if (*cur == 'e' && *bp == '0') *bp = ' '; // for Windows
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'H':
|
||||
bp = Format02d(ep, al.cs.hour());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'M':
|
||||
bp = Format02d(ep, al.cs.minute());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'S':
|
||||
bp = Format02d(ep, al.cs.second());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'z':
|
||||
bp = FormatOffset(ep, al.offset, "");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case 'Z':
|
||||
result.append(al.abbr);
|
||||
break;
|
||||
case 's':
|
||||
bp = Format64(ep, 0, ToUnixSeconds(tp));
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
break;
|
||||
case '%':
|
||||
result.push_back('%');
|
||||
break;
|
||||
}
|
||||
pending = ++cur;
|
||||
continue;
|
||||
}
|
||||
|
||||
// More complex specifiers that we handle ourselves.
|
||||
if (*cur == ':' && cur + 1 != end) {
|
||||
if (*(cur + 1) == 'z') {
|
||||
// Formats %:z.
|
||||
if (cur - 1 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 1), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 2;
|
||||
continue;
|
||||
}
|
||||
if (*(cur + 1) == ':' && cur + 2 != end) {
|
||||
if (*(cur + 2) == 'z') {
|
||||
// Formats %::z.
|
||||
if (cur - 1 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 1), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":*");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 3;
|
||||
continue;
|
||||
}
|
||||
if (*(cur + 2) == ':' && cur + 3 != end) {
|
||||
if (*(cur + 3) == 'z') {
|
||||
// Formats %:::z.
|
||||
if (cur - 1 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 1), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":*:");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 4;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop if there is no E modifier.
|
||||
if (*cur != 'E' || ++cur == end) continue;
|
||||
|
||||
// Format our extensions.
|
||||
if (*cur == 'z') {
|
||||
// Formats %Ez.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = ++cur;
|
||||
} else if (*cur == '*' && cur + 1 != end && *(cur + 1) == 'z') {
|
||||
// Formats %E*z.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
bp = FormatOffset(ep, al.offset, ":*");
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 2;
|
||||
} else if (*cur == '*' && cur + 1 != end &&
|
||||
(*(cur + 1) == 'S' || *(cur + 1) == 'f')) {
|
||||
// Formats %E*S or %E*F.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
char* cp = ep;
|
||||
bp = Format64(cp, 15, fs.count());
|
||||
while (cp != bp && cp[-1] == '0') --cp;
|
||||
switch (*(cur + 1)) {
|
||||
case 'S':
|
||||
if (cp != bp) *--bp = '.';
|
||||
bp = Format02d(bp, al.cs.second());
|
||||
break;
|
||||
case 'f':
|
||||
if (cp == bp) *--bp = '0';
|
||||
break;
|
||||
}
|
||||
result.append(bp, static_cast<std::size_t>(cp - bp));
|
||||
pending = cur += 2;
|
||||
} else if (*cur == '4' && cur + 1 != end && *(cur + 1) == 'Y') {
|
||||
// Formats %E4Y.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
bp = Format64(ep, 4, al.cs.year());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur += 2;
|
||||
} else if (std::isdigit(*cur)) {
|
||||
// Possibly found %E#S or %E#f.
|
||||
int n = 0;
|
||||
if (const char* np = ParseInt(cur, 0, 0, 1024, &n)) {
|
||||
if (*np == 'S' || *np == 'f') {
|
||||
// Formats %E#S or %E#f.
|
||||
if (cur - 2 != pending) {
|
||||
FormatTM(&result, std::string(pending, cur - 2), tm);
|
||||
}
|
||||
bp = ep;
|
||||
if (n > 0) {
|
||||
if (n > kDigits10_64) n = kDigits10_64;
|
||||
bp = Format64(bp, n,
|
||||
(n > 15) ? fs.count() * kExp10[n - 15]
|
||||
: fs.count() / kExp10[15 - n]);
|
||||
if (*np == 'S') *--bp = '.';
|
||||
}
|
||||
if (*np == 'S') bp = Format02d(bp, al.cs.second());
|
||||
result.append(bp, static_cast<std::size_t>(ep - bp));
|
||||
pending = cur = ++np;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Formats any remaining data.
|
||||
if (end != pending) {
|
||||
FormatTM(&result, std::string(pending, end), tm);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const char* ParseOffset(const char* dp, const char* mode, int* offset) {
|
||||
if (dp != nullptr) {
|
||||
const char first = *dp++;
|
||||
if (first == '+' || first == '-') {
|
||||
char sep = mode[0];
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int seconds = 0;
|
||||
const char* ap = ParseInt(dp, 2, 0, 23, &hours);
|
||||
if (ap != nullptr && ap - dp == 2) {
|
||||
dp = ap;
|
||||
if (sep != '\0' && *ap == sep) ++ap;
|
||||
const char* bp = ParseInt(ap, 2, 0, 59, &minutes);
|
||||
if (bp != nullptr && bp - ap == 2) {
|
||||
dp = bp;
|
||||
if (sep != '\0' && *bp == sep) ++bp;
|
||||
const char* cp = ParseInt(bp, 2, 0, 59, &seconds);
|
||||
if (cp != nullptr && cp - bp == 2) dp = cp;
|
||||
}
|
||||
*offset = ((hours * 60 + minutes) * 60) + seconds;
|
||||
if (first == '-') *offset = -*offset;
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
} else if (first == 'Z') { // Zulu
|
||||
*offset = 0;
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
const char* ParseZone(const char* dp, std::string* zone) {
|
||||
zone->clear();
|
||||
if (dp != nullptr) {
|
||||
while (*dp != '\0' && !std::isspace(*dp)) zone->push_back(*dp++);
|
||||
if (zone->empty()) dp = nullptr;
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
const char* ParseSubSeconds(const char* dp, detail::femtoseconds* subseconds) {
|
||||
if (dp != nullptr) {
|
||||
std::int_fast64_t v = 0;
|
||||
std::int_fast64_t exp = 0;
|
||||
const char* const bp = dp;
|
||||
while (const char* cp = strchr(kDigits, *dp)) {
|
||||
int d = static_cast<int>(cp - kDigits);
|
||||
if (d >= 10) break;
|
||||
if (exp < 15) {
|
||||
exp += 1;
|
||||
v *= 10;
|
||||
v += d;
|
||||
}
|
||||
++dp;
|
||||
}
|
||||
if (dp != bp) {
|
||||
v *= kExp10[15 - exp];
|
||||
*subseconds = detail::femtoseconds(v);
|
||||
} else {
|
||||
dp = nullptr;
|
||||
}
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
// Parses a string into a std::tm using strptime(3).
|
||||
const char* ParseTM(const char* dp, const char* fmt, std::tm* tm) {
|
||||
if (dp != nullptr) {
|
||||
dp = strptime(dp, fmt, tm);
|
||||
}
|
||||
return dp;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Uses strptime(3) to parse the given input. Supports the same extended
|
||||
// format specifiers as format(), although %E#S and %E*S are treated
|
||||
// identically (and similarly for %E#f and %E*f). %Ez and %E*z also accept
|
||||
// the same inputs.
|
||||
//
|
||||
// The standard specifiers from RFC3339_* (%Y, %m, %d, %H, %M, and %S) are
|
||||
// handled internally so that we can normally avoid strptime() altogether
|
||||
// (which is particularly helpful when the native implementation is broken).
|
||||
//
|
||||
// The TZ/GNU %s extension is handled internally because strptime() has to
|
||||
// use localtime_r() to generate it, and that assumes the local time zone.
|
||||
//
|
||||
// We also handle the %z specifier to accommodate platforms that do not
|
||||
// support the tm_gmtoff extension to std::tm. %Z is parsed but ignored.
|
||||
bool parse(const std::string& format, const std::string& input,
|
||||
const time_zone& tz, time_point<seconds>* sec,
|
||||
detail::femtoseconds* fs, std::string* err) {
|
||||
// The unparsed input.
|
||||
const char* data = input.c_str(); // NUL terminated
|
||||
|
||||
// Skips leading whitespace.
|
||||
while (std::isspace(*data)) ++data;
|
||||
|
||||
const year_t kyearmax = std::numeric_limits<year_t>::max();
|
||||
const year_t kyearmin = std::numeric_limits<year_t>::min();
|
||||
|
||||
// Sets default values for unspecified fields.
|
||||
bool saw_year = false;
|
||||
year_t year = 1970;
|
||||
std::tm tm{};
|
||||
tm.tm_year = 1970 - 1900;
|
||||
tm.tm_mon = 1 - 1; // Jan
|
||||
tm.tm_mday = 1;
|
||||
tm.tm_hour = 0;
|
||||
tm.tm_min = 0;
|
||||
tm.tm_sec = 0;
|
||||
tm.tm_wday = 4; // Thu
|
||||
tm.tm_yday = 0;
|
||||
tm.tm_isdst = 0;
|
||||
auto subseconds = detail::femtoseconds::zero();
|
||||
bool saw_offset = false;
|
||||
int offset = 0; // No offset from passed tz.
|
||||
std::string zone = "UTC";
|
||||
|
||||
const char* fmt = format.c_str(); // NUL terminated
|
||||
bool twelve_hour = false;
|
||||
bool afternoon = false;
|
||||
|
||||
bool saw_percent_s = false;
|
||||
std::int_fast64_t percent_s = 0;
|
||||
|
||||
// Steps through format, one specifier at a time.
|
||||
while (data != nullptr && *fmt != '\0') {
|
||||
if (std::isspace(*fmt)) {
|
||||
while (std::isspace(*data)) ++data;
|
||||
while (std::isspace(*++fmt)) continue;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*fmt != '%') {
|
||||
if (*data == *fmt) {
|
||||
++data;
|
||||
++fmt;
|
||||
} else {
|
||||
data = nullptr;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* percent = fmt;
|
||||
if (*++fmt == '\0') {
|
||||
data = nullptr;
|
||||
continue;
|
||||
}
|
||||
switch (*fmt++) {
|
||||
case 'Y':
|
||||
// Symmetrically with FormatTime(), directly handing %Y avoids the
|
||||
// tm.tm_year overflow problem. However, tm.tm_year will still be
|
||||
// used by other specifiers like %D.
|
||||
data = ParseInt(data, 0, kyearmin, kyearmax, &year);
|
||||
if (data != nullptr) saw_year = true;
|
||||
continue;
|
||||
case 'm':
|
||||
data = ParseInt(data, 2, 1, 12, &tm.tm_mon);
|
||||
if (data != nullptr) tm.tm_mon -= 1;
|
||||
continue;
|
||||
case 'd':
|
||||
case 'e':
|
||||
data = ParseInt(data, 2, 1, 31, &tm.tm_mday);
|
||||
continue;
|
||||
case 'H':
|
||||
data = ParseInt(data, 2, 0, 23, &tm.tm_hour);
|
||||
twelve_hour = false;
|
||||
continue;
|
||||
case 'M':
|
||||
data = ParseInt(data, 2, 0, 59, &tm.tm_min);
|
||||
continue;
|
||||
case 'S':
|
||||
data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
|
||||
continue;
|
||||
case 'I':
|
||||
case 'l':
|
||||
case 'r': // probably uses %I
|
||||
twelve_hour = true;
|
||||
break;
|
||||
case 'R': // uses %H
|
||||
case 'T': // uses %H
|
||||
case 'c': // probably uses %H
|
||||
case 'X': // probably uses %H
|
||||
twelve_hour = false;
|
||||
break;
|
||||
case 'z':
|
||||
data = ParseOffset(data, "", &offset);
|
||||
if (data != nullptr) saw_offset = true;
|
||||
continue;
|
||||
case 'Z': // ignored; zone abbreviations are ambiguous
|
||||
data = ParseZone(data, &zone);
|
||||
continue;
|
||||
case 's':
|
||||
data =
|
||||
ParseInt(data, 0, std::numeric_limits<std::int_fast64_t>::min(),
|
||||
std::numeric_limits<std::int_fast64_t>::max(), &percent_s);
|
||||
if (data != nullptr) saw_percent_s = true;
|
||||
continue;
|
||||
case ':':
|
||||
if (fmt[0] == 'z' ||
|
||||
(fmt[0] == ':' &&
|
||||
(fmt[1] == 'z' || (fmt[1] == ':' && fmt[2] == 'z')))) {
|
||||
data = ParseOffset(data, ":", &offset);
|
||||
if (data != nullptr) saw_offset = true;
|
||||
fmt += (fmt[0] == 'z') ? 1 : (fmt[1] == 'z') ? 2 : 3;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case '%':
|
||||
data = (*data == '%' ? data + 1 : nullptr);
|
||||
continue;
|
||||
case 'E':
|
||||
if (fmt[0] == 'z' || (fmt[0] == '*' && fmt[1] == 'z')) {
|
||||
data = ParseOffset(data, ":", &offset);
|
||||
if (data != nullptr) saw_offset = true;
|
||||
fmt += (fmt[0] == 'z') ? 1 : 2;
|
||||
continue;
|
||||
}
|
||||
if (fmt[0] == '*' && fmt[1] == 'S') {
|
||||
data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
|
||||
if (data != nullptr && *data == '.') {
|
||||
data = ParseSubSeconds(data + 1, &subseconds);
|
||||
}
|
||||
fmt += 2;
|
||||
continue;
|
||||
}
|
||||
if (fmt[0] == '*' && fmt[1] == 'f') {
|
||||
if (data != nullptr && std::isdigit(*data)) {
|
||||
data = ParseSubSeconds(data, &subseconds);
|
||||
}
|
||||
fmt += 2;
|
||||
continue;
|
||||
}
|
||||
if (fmt[0] == '4' && fmt[1] == 'Y') {
|
||||
const char* bp = data;
|
||||
data = ParseInt(data, 4, year_t{-999}, year_t{9999}, &year);
|
||||
if (data != nullptr) {
|
||||
if (data - bp == 4) {
|
||||
saw_year = true;
|
||||
} else {
|
||||
data = nullptr; // stopped too soon
|
||||
}
|
||||
}
|
||||
fmt += 2;
|
||||
continue;
|
||||
}
|
||||
if (std::isdigit(*fmt)) {
|
||||
int n = 0; // value ignored
|
||||
if (const char* np = ParseInt(fmt, 0, 0, 1024, &n)) {
|
||||
if (*np == 'S') {
|
||||
data = ParseInt(data, 2, 0, 60, &tm.tm_sec);
|
||||
if (data != nullptr && *data == '.') {
|
||||
data = ParseSubSeconds(data + 1, &subseconds);
|
||||
}
|
||||
fmt = ++np;
|
||||
continue;
|
||||
}
|
||||
if (*np == 'f') {
|
||||
if (data != nullptr && std::isdigit(*data)) {
|
||||
data = ParseSubSeconds(data, &subseconds);
|
||||
}
|
||||
fmt = ++np;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*fmt == 'c') twelve_hour = false; // probably uses %H
|
||||
if (*fmt == 'X') twelve_hour = false; // probably uses %H
|
||||
if (*fmt != '\0') ++fmt;
|
||||
break;
|
||||
case 'O':
|
||||
if (*fmt == 'H') twelve_hour = false;
|
||||
if (*fmt == 'I') twelve_hour = true;
|
||||
if (*fmt != '\0') ++fmt;
|
||||
break;
|
||||
}
|
||||
|
||||
// Parses the current specifier.
|
||||
const char* orig_data = data;
|
||||
std::string spec(percent, static_cast<std::size_t>(fmt - percent));
|
||||
data = ParseTM(data, spec.c_str(), &tm);
|
||||
|
||||
// If we successfully parsed %p we need to remember whether the result
|
||||
// was AM or PM so that we can adjust tm_hour before time_zone::lookup().
|
||||
// So reparse the input with a known AM hour, and check if it is shifted
|
||||
// to a PM hour.
|
||||
if (spec == "%p" && data != nullptr) {
|
||||
std::string test_input = "1";
|
||||
test_input.append(orig_data, static_cast<std::size_t>(data - orig_data));
|
||||
const char* test_data = test_input.c_str();
|
||||
std::tm tmp{};
|
||||
ParseTM(test_data, "%I%p", &tmp);
|
||||
afternoon = (tmp.tm_hour == 13);
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust a 12-hour tm_hour value if it should be in the afternoon.
|
||||
if (twelve_hour && afternoon && tm.tm_hour < 12) {
|
||||
tm.tm_hour += 12;
|
||||
}
|
||||
|
||||
if (data == nullptr) {
|
||||
if (err != nullptr) *err = "Failed to parse input";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip any remaining whitespace.
|
||||
while (std::isspace(*data)) ++data;
|
||||
|
||||
// parse() must consume the entire input string.
|
||||
if (*data != '\0') {
|
||||
if (err != nullptr) *err = "Illegal trailing data in input string";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we saw %s then we ignore anything else and return that time.
|
||||
if (saw_percent_s) {
|
||||
*sec = FromUnixSeconds(percent_s);
|
||||
*fs = detail::femtoseconds::zero();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we saw %z, %Ez, or %E*z then we want to interpret the parsed fields
|
||||
// in UTC and then shift by that offset. Otherwise we want to interpret
|
||||
// the fields directly in the passed time_zone.
|
||||
time_zone ptz = saw_offset ? utc_time_zone() : tz;
|
||||
|
||||
// Allows a leap second of 60 to normalize forward to the following ":00".
|
||||
if (tm.tm_sec == 60) {
|
||||
tm.tm_sec -= 1;
|
||||
offset -= 1;
|
||||
subseconds = detail::femtoseconds::zero();
|
||||
}
|
||||
|
||||
if (!saw_year) {
|
||||
year = year_t{tm.tm_year};
|
||||
if (year > kyearmax - 1900) {
|
||||
// Platform-dependent, maybe unreachable.
|
||||
if (err != nullptr) *err = "Out-of-range year";
|
||||
return false;
|
||||
}
|
||||
year += 1900;
|
||||
}
|
||||
|
||||
const int month = tm.tm_mon + 1;
|
||||
civil_second cs(year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
|
||||
// parse() should not allow normalization. Due to the restricted field
|
||||
// ranges above (see ParseInt()), the only possibility is for days to roll
|
||||
// into months. That is, parsing "Sep 31" should not produce "Oct 1".
|
||||
if (cs.month() != month || cs.day() != tm.tm_mday) {
|
||||
if (err != nullptr) *err = "Out-of-range field";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Accounts for the offset adjustment before converting to absolute time.
|
||||
if ((offset < 0 && cs > civil_second::max() + offset) ||
|
||||
(offset > 0 && cs < civil_second::min() + offset)) {
|
||||
if (err != nullptr) *err = "Out-of-range field";
|
||||
return false;
|
||||
}
|
||||
cs -= offset;
|
||||
|
||||
const auto tp = ptz.lookup(cs).pre;
|
||||
// Checks for overflow/underflow and returns an error as necessary.
|
||||
if (tp == time_point<seconds>::max()) {
|
||||
const auto al = ptz.lookup(time_point<seconds>::max());
|
||||
if (cs > al.cs) {
|
||||
if (err != nullptr) *err = "Out-of-range field";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (tp == time_point<seconds>::min()) {
|
||||
const auto al = ptz.lookup(time_point<seconds>::min());
|
||||
if (cs < al.cs) {
|
||||
if (err != nullptr) *err = "Out-of-range field";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*sec = tp;
|
||||
*fs = subseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
1500
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format_test.cc
vendored
Normal file
1500
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_format_test.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
45
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.cc
vendored
Normal file
45
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.cc
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "time_zone_if.h"
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "time_zone_info.h"
|
||||
#include "time_zone_libc.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
std::unique_ptr<TimeZoneIf> TimeZoneIf::Load(const std::string& name) {
|
||||
// Support "libc:localtime" and "libc:*" to access the legacy
|
||||
// localtime and UTC support respectively from the C library.
|
||||
if (name.compare(0, 5, "libc:") == 0) {
|
||||
return std::unique_ptr<TimeZoneIf>(new TimeZoneLibC(name.substr(5)));
|
||||
}
|
||||
|
||||
// Otherwise use the "zoneinfo" implementation by default.
|
||||
std::unique_ptr<TimeZoneInfo> tz(new TimeZoneInfo);
|
||||
if (!tz->Load(name)) tz.reset();
|
||||
return std::unique_ptr<TimeZoneIf>(tz.release());
|
||||
}
|
||||
|
||||
// Defined out-of-line to avoid emitting a weak vtable in all TUs.
|
||||
TimeZoneIf::~TimeZoneIf() {}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
76
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.h
vendored
Normal file
76
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_if.h
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// A simple interface used to hide time-zone complexities from time_zone::Impl.
|
||||
// Subclasses implement the functions for civil-time conversions in the zone.
|
||||
class TimeZoneIf {
|
||||
public:
|
||||
// A factory function for TimeZoneIf implementations.
|
||||
static std::unique_ptr<TimeZoneIf> Load(const std::string& name);
|
||||
|
||||
virtual ~TimeZoneIf();
|
||||
|
||||
virtual time_zone::absolute_lookup BreakTime(
|
||||
const time_point<seconds>& tp) const = 0;
|
||||
virtual time_zone::civil_lookup MakeTime(const civil_second& cs) const = 0;
|
||||
|
||||
virtual bool NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const = 0;
|
||||
virtual bool PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const = 0;
|
||||
|
||||
virtual std::string Version() const = 0;
|
||||
virtual std::string Description() const = 0;
|
||||
|
||||
protected:
|
||||
TimeZoneIf() {}
|
||||
};
|
||||
|
||||
// Convert between time_point<seconds> and a count of seconds since the
|
||||
// Unix epoch. We assume that the std::chrono::system_clock and the
|
||||
// Unix clock are second aligned, but not that they share an epoch.
|
||||
inline std::int_fast64_t ToUnixSeconds(const time_point<seconds>& tp) {
|
||||
return (tp - std::chrono::time_point_cast<seconds>(
|
||||
std::chrono::system_clock::from_time_t(0)))
|
||||
.count();
|
||||
}
|
||||
inline time_point<seconds> FromUnixSeconds(std::int_fast64_t t) {
|
||||
return std::chrono::time_point_cast<seconds>(
|
||||
std::chrono::system_clock::from_time_t(0)) +
|
||||
seconds(t);
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IF_H_
|
||||
121
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.cc
vendored
Normal file
121
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.cc
vendored
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "time_zone_impl.h"
|
||||
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "time_zone_fixed.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
// time_zone::Impls are linked into a map to support fast lookup by name.
|
||||
using TimeZoneImplByName =
|
||||
std::unordered_map<std::string, const time_zone::Impl*>;
|
||||
TimeZoneImplByName* time_zone_map = nullptr;
|
||||
|
||||
// Mutual exclusion for time_zone_map.
|
||||
std::mutex& TimeZoneMutex() {
|
||||
// This mutex is intentionally "leaked" to avoid the static deinitialization
|
||||
// order fiasco (std::mutex's destructor is not trivial on many platforms).
|
||||
static std::mutex* time_zone_mutex = new std::mutex;
|
||||
return *time_zone_mutex;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
time_zone time_zone::Impl::UTC() { return time_zone(UTCImpl()); }
|
||||
|
||||
bool time_zone::Impl::LoadTimeZone(const std::string& name, time_zone* tz) {
|
||||
const time_zone::Impl* const utc_impl = UTCImpl();
|
||||
|
||||
// First check for UTC (which is never a key in time_zone_map).
|
||||
auto offset = seconds::zero();
|
||||
if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
|
||||
*tz = time_zone(utc_impl);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then check, under a shared lock, whether the time zone has already
|
||||
// been loaded. This is the common path. TODO: Move to shared_mutex.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(TimeZoneMutex());
|
||||
if (time_zone_map != nullptr) {
|
||||
TimeZoneImplByName::const_iterator itr = time_zone_map->find(name);
|
||||
if (itr != time_zone_map->end()) {
|
||||
*tz = time_zone(itr->second);
|
||||
return itr->second != utc_impl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now check again, under an exclusive lock.
|
||||
std::lock_guard<std::mutex> lock(TimeZoneMutex());
|
||||
if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName;
|
||||
const Impl*& impl = (*time_zone_map)[name];
|
||||
if (impl == nullptr) {
|
||||
// The first thread in loads the new time zone.
|
||||
Impl* new_impl = new Impl(name);
|
||||
new_impl->zone_ = TimeZoneIf::Load(new_impl->name_);
|
||||
if (new_impl->zone_ == nullptr) {
|
||||
delete new_impl; // free the nascent Impl
|
||||
impl = utc_impl; // and fallback to UTC
|
||||
} else {
|
||||
impl = new_impl; // install new time zone
|
||||
}
|
||||
}
|
||||
*tz = time_zone(impl);
|
||||
return impl != utc_impl;
|
||||
}
|
||||
|
||||
void time_zone::Impl::ClearTimeZoneMapTestOnly() {
|
||||
std::lock_guard<std::mutex> lock(TimeZoneMutex());
|
||||
if (time_zone_map != nullptr) {
|
||||
// Existing time_zone::Impl* entries are in the wild, so we can't delete
|
||||
// them. Instead, we move them to a private container, where they are
|
||||
// logically unreachable but not "leaked". Future requests will result
|
||||
// in reloading the data.
|
||||
static auto* cleared = new std::deque<const time_zone::Impl*>;
|
||||
for (const auto& element : *time_zone_map) {
|
||||
cleared->push_back(element.second);
|
||||
}
|
||||
time_zone_map->clear();
|
||||
}
|
||||
}
|
||||
|
||||
time_zone::Impl::Impl(const std::string& name) : name_(name) {}
|
||||
|
||||
const time_zone::Impl* time_zone::Impl::UTCImpl() {
|
||||
static Impl* utc_impl = [] {
|
||||
Impl* impl = new Impl("UTC");
|
||||
impl->zone_ = TimeZoneIf::Load(impl->name_); // never fails
|
||||
return impl;
|
||||
}();
|
||||
return utc_impl;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
93
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.h
vendored
Normal file
93
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_impl.h
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
#include "time_zone_if.h"
|
||||
#include "time_zone_info.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// time_zone::Impl is the internal object referenced by a cctz::time_zone.
|
||||
class time_zone::Impl {
|
||||
public:
|
||||
// The UTC time zone. Also used for other time zones that fail to load.
|
||||
static time_zone UTC();
|
||||
|
||||
// Load a named time zone. Returns false if the name is invalid, or if
|
||||
// some other kind of error occurs. Note that loading "UTC" never fails.
|
||||
static bool LoadTimeZone(const std::string& name, time_zone* tz);
|
||||
|
||||
// Clears the map of cached time zones. Primarily for use in benchmarks
|
||||
// that gauge the performance of loading/parsing the time-zone data.
|
||||
static void ClearTimeZoneMapTestOnly();
|
||||
|
||||
// The primary key is the time-zone ID (e.g., "America/New_York").
|
||||
const std::string& Name() const {
|
||||
// TODO: It would nice if the zoneinfo data included the zone name.
|
||||
return name_;
|
||||
}
|
||||
|
||||
// Breaks a time_point down to civil-time components in this time zone.
|
||||
time_zone::absolute_lookup BreakTime(const time_point<seconds>& tp) const {
|
||||
return zone_->BreakTime(tp);
|
||||
}
|
||||
|
||||
// Converts the civil-time components in this time zone into a time_point.
|
||||
// That is, the opposite of BreakTime(). The requested civil time may be
|
||||
// ambiguous or illegal due to a change of UTC offset.
|
||||
time_zone::civil_lookup MakeTime(const civil_second& cs) const {
|
||||
return zone_->MakeTime(cs);
|
||||
}
|
||||
|
||||
// Finds the time of the next/previous offset change in this time zone.
|
||||
bool NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const {
|
||||
return zone_->NextTransition(tp, trans);
|
||||
}
|
||||
bool PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const {
|
||||
return zone_->PrevTransition(tp, trans);
|
||||
}
|
||||
|
||||
// Returns an implementation-defined version string for this time zone.
|
||||
std::string Version() const { return zone_->Version(); }
|
||||
|
||||
// Returns an implementation-defined description of this time zone.
|
||||
std::string Description() const { return zone_->Description(); }
|
||||
|
||||
private:
|
||||
explicit Impl(const std::string& name);
|
||||
static const Impl* UTCImpl();
|
||||
|
||||
const std::string name_;
|
||||
std::unique_ptr<TimeZoneIf> zone_;
|
||||
};
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_IMPL_H_
|
||||
958
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.cc
vendored
Normal file
958
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.cc
vendored
Normal file
|
|
@ -0,0 +1,958 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This file implements the TimeZoneIf interface using the "zoneinfo"
|
||||
// data provided by the IANA Time Zone Database (i.e., the only real game
|
||||
// in town).
|
||||
//
|
||||
// TimeZoneInfo represents the history of UTC-offset changes within a time
|
||||
// zone. Most changes are due to daylight-saving rules, but occasionally
|
||||
// shifts are made to the time-zone's base offset. The database only attempts
|
||||
// to be definitive for times since 1970, so be wary of local-time conversions
|
||||
// before that. Also, rule and zone-boundary changes are made at the whim
|
||||
// of governments, so the conversion of future times needs to be taken with
|
||||
// a grain of salt.
|
||||
//
|
||||
// For more information see tzfile(5), http://www.iana.org/time-zones, or
|
||||
// https://en.wikipedia.org/wiki/Zoneinfo.
|
||||
//
|
||||
// Note that we assume the proleptic Gregorian calendar and 60-second
|
||||
// minutes throughout.
|
||||
|
||||
#include "time_zone_info.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "time_zone_fixed.h"
|
||||
#include "time_zone_posix.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
inline bool IsLeap(year_t year) {
|
||||
return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0);
|
||||
}
|
||||
|
||||
// The number of days in non-leap and leap years respectively.
|
||||
const std::int_least32_t kDaysPerYear[2] = {365, 366};
|
||||
|
||||
// The day offsets of the beginning of each (1-based) month in non-leap and
|
||||
// leap years respectively (e.g., 335 days before December in a leap year).
|
||||
const std::int_least16_t kMonthOffsets[2][1 + 12 + 1] = {
|
||||
{-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
|
||||
{-1, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366},
|
||||
};
|
||||
|
||||
// We reject leap-second encoded zoneinfo and so assume 60-second minutes.
|
||||
const std::int_least32_t kSecsPerDay = 24 * 60 * 60;
|
||||
|
||||
// 400-year chunks always have 146097 days (20871 weeks).
|
||||
const std::int_least64_t kSecsPer400Years = 146097LL * kSecsPerDay;
|
||||
|
||||
// Like kDaysPerYear[] but scaled up by a factor of kSecsPerDay.
|
||||
const std::int_least32_t kSecsPerYear[2] = {
|
||||
365 * kSecsPerDay,
|
||||
366 * kSecsPerDay,
|
||||
};
|
||||
|
||||
// Single-byte, unsigned numeric values are encoded directly.
|
||||
inline std::uint_fast8_t Decode8(const char* cp) {
|
||||
return static_cast<std::uint_fast8_t>(*cp) & 0xff;
|
||||
}
|
||||
|
||||
// Multi-byte, numeric values are encoded using a MSB first,
|
||||
// twos-complement representation. These helpers decode, from
|
||||
// the given address, 4-byte and 8-byte values respectively.
|
||||
// Note: If int_fastXX_t == intXX_t and this machine is not
|
||||
// twos complement, then there will be at least one input value
|
||||
// we cannot represent.
|
||||
std::int_fast32_t Decode32(const char* cp) {
|
||||
std::uint_fast32_t v = 0;
|
||||
for (int i = 0; i != (32 / 8); ++i) v = (v << 8) | Decode8(cp++);
|
||||
const std::int_fast32_t s32max = 0x7fffffff;
|
||||
const auto s32maxU = static_cast<std::uint_fast32_t>(s32max);
|
||||
if (v <= s32maxU) return static_cast<std::int_fast32_t>(v);
|
||||
return static_cast<std::int_fast32_t>(v - s32maxU - 1) - s32max - 1;
|
||||
}
|
||||
|
||||
std::int_fast64_t Decode64(const char* cp) {
|
||||
std::uint_fast64_t v = 0;
|
||||
for (int i = 0; i != (64 / 8); ++i) v = (v << 8) | Decode8(cp++);
|
||||
const std::int_fast64_t s64max = 0x7fffffffffffffff;
|
||||
const auto s64maxU = static_cast<std::uint_fast64_t>(s64max);
|
||||
if (v <= s64maxU) return static_cast<std::int_fast64_t>(v);
|
||||
return static_cast<std::int_fast64_t>(v - s64maxU - 1) - s64max - 1;
|
||||
}
|
||||
|
||||
// Generate a year-relative offset for a PosixTransition.
|
||||
std::int_fast64_t TransOffset(bool leap_year, int jan1_weekday,
|
||||
const PosixTransition& pt) {
|
||||
std::int_fast64_t days = 0;
|
||||
switch (pt.date.fmt) {
|
||||
case PosixTransition::J: {
|
||||
days = pt.date.j.day;
|
||||
if (!leap_year || days < kMonthOffsets[1][3]) days -= 1;
|
||||
break;
|
||||
}
|
||||
case PosixTransition::N: {
|
||||
days = pt.date.n.day;
|
||||
break;
|
||||
}
|
||||
case PosixTransition::M: {
|
||||
const bool last_week = (pt.date.m.week == 5);
|
||||
days = kMonthOffsets[leap_year][pt.date.m.month + last_week];
|
||||
const std::int_fast64_t weekday = (jan1_weekday + days) % 7;
|
||||
if (last_week) {
|
||||
days -= (weekday + 7 - 1 - pt.date.m.weekday) % 7 + 1;
|
||||
} else {
|
||||
days += (pt.date.m.weekday + 7 - weekday) % 7;
|
||||
days += (pt.date.m.week - 1) * 7;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (days * kSecsPerDay) + pt.time.offset;
|
||||
}
|
||||
|
||||
inline time_zone::civil_lookup MakeUnique(const time_point<seconds>& tp) {
|
||||
time_zone::civil_lookup cl;
|
||||
cl.kind = time_zone::civil_lookup::UNIQUE;
|
||||
cl.pre = cl.trans = cl.post = tp;
|
||||
return cl;
|
||||
}
|
||||
|
||||
inline time_zone::civil_lookup MakeUnique(std::int_fast64_t unix_time) {
|
||||
return MakeUnique(FromUnixSeconds(unix_time));
|
||||
}
|
||||
|
||||
inline time_zone::civil_lookup MakeSkipped(const Transition& tr,
|
||||
const civil_second& cs) {
|
||||
time_zone::civil_lookup cl;
|
||||
cl.kind = time_zone::civil_lookup::SKIPPED;
|
||||
cl.pre = FromUnixSeconds(tr.unix_time - 1 + (cs - tr.prev_civil_sec));
|
||||
cl.trans = FromUnixSeconds(tr.unix_time);
|
||||
cl.post = FromUnixSeconds(tr.unix_time - (tr.civil_sec - cs));
|
||||
return cl;
|
||||
}
|
||||
|
||||
inline time_zone::civil_lookup MakeRepeated(const Transition& tr,
|
||||
const civil_second& cs) {
|
||||
time_zone::civil_lookup cl;
|
||||
cl.kind = time_zone::civil_lookup::REPEATED;
|
||||
cl.pre = FromUnixSeconds(tr.unix_time - 1 - (tr.prev_civil_sec - cs));
|
||||
cl.trans = FromUnixSeconds(tr.unix_time);
|
||||
cl.post = FromUnixSeconds(tr.unix_time + (cs - tr.civil_sec));
|
||||
return cl;
|
||||
}
|
||||
|
||||
inline civil_second YearShift(const civil_second& cs, year_t shift) {
|
||||
return civil_second(cs.year() + shift, cs.month(), cs.day(), cs.hour(),
|
||||
cs.minute(), cs.second());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// What (no leap-seconds) UTC+seconds zoneinfo would look like.
|
||||
bool TimeZoneInfo::ResetToBuiltinUTC(const seconds& offset) {
|
||||
transition_types_.resize(1);
|
||||
TransitionType& tt(transition_types_.back());
|
||||
tt.utc_offset = static_cast<std::int_least32_t>(offset.count());
|
||||
tt.is_dst = false;
|
||||
tt.abbr_index = 0;
|
||||
|
||||
// We temporarily add some redundant, contemporary (2013 through 2023)
|
||||
// transitions for performance reasons. See TimeZoneInfo::LocalTime().
|
||||
// TODO: Fix the performance issue and remove the extra transitions.
|
||||
transitions_.clear();
|
||||
transitions_.reserve(12);
|
||||
for (const std::int_fast64_t unix_time : {
|
||||
-(1LL << 59), // BIG_BANG
|
||||
1356998400LL, // 2013-01-01T00:00:00+00:00
|
||||
1388534400LL, // 2014-01-01T00:00:00+00:00
|
||||
1420070400LL, // 2015-01-01T00:00:00+00:00
|
||||
1451606400LL, // 2016-01-01T00:00:00+00:00
|
||||
1483228800LL, // 2017-01-01T00:00:00+00:00
|
||||
1514764800LL, // 2018-01-01T00:00:00+00:00
|
||||
1546300800LL, // 2019-01-01T00:00:00+00:00
|
||||
1577836800LL, // 2020-01-01T00:00:00+00:00
|
||||
1609459200LL, // 2021-01-01T00:00:00+00:00
|
||||
1640995200LL, // 2022-01-01T00:00:00+00:00
|
||||
1672531200LL, // 2023-01-01T00:00:00+00:00
|
||||
2147483647LL, // 2^31 - 1
|
||||
}) {
|
||||
Transition& tr(*transitions_.emplace(transitions_.end()));
|
||||
tr.unix_time = unix_time;
|
||||
tr.type_index = 0;
|
||||
tr.civil_sec = LocalTime(tr.unix_time, tt).cs;
|
||||
tr.prev_civil_sec = tr.civil_sec - 1;
|
||||
}
|
||||
|
||||
default_transition_type_ = 0;
|
||||
abbreviations_ = FixedOffsetToAbbr(offset);
|
||||
abbreviations_.append(1, '\0'); // add NUL
|
||||
future_spec_.clear(); // never needed for a fixed-offset zone
|
||||
extended_ = false;
|
||||
|
||||
tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
|
||||
tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
|
||||
|
||||
transitions_.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Builds the in-memory header using the raw bytes from the file.
|
||||
bool TimeZoneInfo::Header::Build(const tzhead& tzh) {
|
||||
std::int_fast32_t v;
|
||||
if ((v = Decode32(tzh.tzh_timecnt)) < 0) return false;
|
||||
timecnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_typecnt)) < 0) return false;
|
||||
typecnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_charcnt)) < 0) return false;
|
||||
charcnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_leapcnt)) < 0) return false;
|
||||
leapcnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_ttisstdcnt)) < 0) return false;
|
||||
ttisstdcnt = static_cast<std::size_t>(v);
|
||||
if ((v = Decode32(tzh.tzh_ttisutcnt)) < 0) return false;
|
||||
ttisutcnt = static_cast<std::size_t>(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
// How many bytes of data are associated with this header. The result
|
||||
// depends upon whether this is a section with 4-byte or 8-byte times.
|
||||
std::size_t TimeZoneInfo::Header::DataLength(std::size_t time_len) const {
|
||||
std::size_t len = 0;
|
||||
len += (time_len + 1) * timecnt; // unix_time + type_index
|
||||
len += (4 + 1 + 1) * typecnt; // utc_offset + is_dst + abbr_index
|
||||
len += 1 * charcnt; // abbreviations
|
||||
len += (time_len + 4) * leapcnt; // leap-time + TAI-UTC
|
||||
len += 1 * ttisstdcnt; // UTC/local indicators
|
||||
len += 1 * ttisutcnt; // standard/wall indicators
|
||||
return len;
|
||||
}
|
||||
|
||||
// Check that the TransitionType has the expected offset/is_dst/abbreviation.
|
||||
void TimeZoneInfo::CheckTransition(const std::string& name,
|
||||
const TransitionType& tt,
|
||||
std::int_fast32_t offset, bool is_dst,
|
||||
const std::string& abbr) const {
|
||||
if (tt.utc_offset != offset || tt.is_dst != is_dst ||
|
||||
&abbreviations_[tt.abbr_index] != abbr) {
|
||||
std::clog << name << ": Transition"
|
||||
<< " offset=" << tt.utc_offset << "/"
|
||||
<< (tt.is_dst ? "DST" : "STD")
|
||||
<< "/abbr=" << &abbreviations_[tt.abbr_index]
|
||||
<< " does not match POSIX spec '" << future_spec_ << "'\n";
|
||||
}
|
||||
}
|
||||
|
||||
// zic(8) can generate no-op transitions when a zone changes rules at an
|
||||
// instant when there is actually no discontinuity. So we check whether
|
||||
// two transitions have equivalent types (same offset/is_dst/abbr).
|
||||
bool TimeZoneInfo::EquivTransitions(std::uint_fast8_t tt1_index,
|
||||
std::uint_fast8_t tt2_index) const {
|
||||
if (tt1_index == tt2_index) return true;
|
||||
const TransitionType& tt1(transition_types_[tt1_index]);
|
||||
const TransitionType& tt2(transition_types_[tt2_index]);
|
||||
if (tt1.is_dst != tt2.is_dst) return false;
|
||||
if (tt1.utc_offset != tt2.utc_offset) return false;
|
||||
if (tt1.abbr_index != tt2.abbr_index) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Use the POSIX-TZ-environment-variable-style string to handle times
|
||||
// in years after the last transition stored in the zoneinfo data.
|
||||
void TimeZoneInfo::ExtendTransitions(const std::string& name,
|
||||
const Header& hdr) {
|
||||
extended_ = false;
|
||||
bool extending = !future_spec_.empty();
|
||||
|
||||
PosixTimeZone posix;
|
||||
if (extending && !ParsePosixSpec(future_spec_, &posix)) {
|
||||
std::clog << name << ": Failed to parse '" << future_spec_ << "'\n";
|
||||
extending = false;
|
||||
}
|
||||
|
||||
if (extending && posix.dst_abbr.empty()) { // std only
|
||||
// The future specification should match the last/default transition,
|
||||
// and that means that handling the future will fall out naturally.
|
||||
std::uint_fast8_t index = default_transition_type_;
|
||||
if (hdr.timecnt != 0) index = transitions_[hdr.timecnt - 1].type_index;
|
||||
const TransitionType& tt(transition_types_[index]);
|
||||
CheckTransition(name, tt, posix.std_offset, false, posix.std_abbr);
|
||||
extending = false;
|
||||
}
|
||||
|
||||
if (extending && hdr.timecnt < 2) {
|
||||
std::clog << name << ": Too few transitions for POSIX spec\n";
|
||||
extending = false;
|
||||
}
|
||||
|
||||
if (!extending) {
|
||||
// Ensure that there is always a transition in the second half of the
|
||||
// time line (the BIG_BANG transition is in the first half) so that the
|
||||
// signed difference between a civil_second and the civil_second of its
|
||||
// previous transition is always representable, without overflow.
|
||||
const Transition& last(transitions_.back());
|
||||
if (last.unix_time < 0) {
|
||||
const std::uint_fast8_t type_index = last.type_index;
|
||||
Transition& tr(*transitions_.emplace(transitions_.end()));
|
||||
tr.unix_time = 2147483647; // 2038-01-19T03:14:07+00:00
|
||||
tr.type_index = type_index;
|
||||
}
|
||||
return; // last transition wins
|
||||
}
|
||||
|
||||
// Extend the transitions for an additional 400 years using the
|
||||
// future specification. Years beyond those can be handled by
|
||||
// mapping back to a cycle-equivalent year within that range.
|
||||
// zic(8) should probably do this so that we don't have to.
|
||||
// TODO: Reduce the extension by the number of compatible
|
||||
// transitions already in place.
|
||||
transitions_.reserve(hdr.timecnt + 400 * 2 + 1);
|
||||
transitions_.resize(hdr.timecnt + 400 * 2);
|
||||
extended_ = true;
|
||||
|
||||
// The future specification should match the last two transitions,
|
||||
// and those transitions should have different is_dst flags. Note
|
||||
// that nothing says the UTC offset used by the is_dst transition
|
||||
// must be greater than that used by the !is_dst transition. (See
|
||||
// Europe/Dublin, for example.)
|
||||
const Transition* tr0 = &transitions_[hdr.timecnt - 1];
|
||||
const Transition* tr1 = &transitions_[hdr.timecnt - 2];
|
||||
const TransitionType* tt0 = &transition_types_[tr0->type_index];
|
||||
const TransitionType* tt1 = &transition_types_[tr1->type_index];
|
||||
const TransitionType& dst(tt0->is_dst ? *tt0 : *tt1);
|
||||
const TransitionType& std(tt0->is_dst ? *tt1 : *tt0);
|
||||
CheckTransition(name, dst, posix.dst_offset, true, posix.dst_abbr);
|
||||
CheckTransition(name, std, posix.std_offset, false, posix.std_abbr);
|
||||
|
||||
// Add the transitions to tr1 and back to tr0 for each extra year.
|
||||
last_year_ = LocalTime(tr0->unix_time, *tt0).cs.year();
|
||||
bool leap_year = IsLeap(last_year_);
|
||||
const civil_day jan1(last_year_, 1, 1);
|
||||
std::int_fast64_t jan1_time = civil_second(jan1) - civil_second();
|
||||
int jan1_weekday = (static_cast<int>(get_weekday(jan1)) + 1) % 7;
|
||||
Transition* tr = &transitions_[hdr.timecnt]; // next trans to fill
|
||||
if (LocalTime(tr1->unix_time, *tt1).cs.year() != last_year_) {
|
||||
// Add a single extra transition to align to a calendar year.
|
||||
transitions_.resize(transitions_.size() + 1);
|
||||
assert(tr == &transitions_[hdr.timecnt]); // no reallocation
|
||||
const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start);
|
||||
std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1);
|
||||
tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset;
|
||||
tr++->type_index = tr1->type_index;
|
||||
tr0 = &transitions_[hdr.timecnt];
|
||||
tr1 = &transitions_[hdr.timecnt - 1];
|
||||
tt0 = &transition_types_[tr0->type_index];
|
||||
tt1 = &transition_types_[tr1->type_index];
|
||||
}
|
||||
const PosixTransition& pt1(tt0->is_dst ? posix.dst_end : posix.dst_start);
|
||||
const PosixTransition& pt0(tt0->is_dst ? posix.dst_start : posix.dst_end);
|
||||
for (const year_t limit = last_year_ + 400; last_year_ < limit;) {
|
||||
last_year_ += 1; // an additional year of generated transitions
|
||||
jan1_time += kSecsPerYear[leap_year];
|
||||
jan1_weekday = (jan1_weekday + kDaysPerYear[leap_year]) % 7;
|
||||
leap_year = !leap_year && IsLeap(last_year_);
|
||||
std::int_fast64_t tr1_offset = TransOffset(leap_year, jan1_weekday, pt1);
|
||||
tr->unix_time = jan1_time + tr1_offset - tt0->utc_offset;
|
||||
tr++->type_index = tr1->type_index;
|
||||
std::int_fast64_t tr0_offset = TransOffset(leap_year, jan1_weekday, pt0);
|
||||
tr->unix_time = jan1_time + tr0_offset - tt1->utc_offset;
|
||||
tr++->type_index = tr0->type_index;
|
||||
}
|
||||
assert(tr == &transitions_[0] + transitions_.size());
|
||||
}
|
||||
|
||||
bool TimeZoneInfo::Load(const std::string& name, ZoneInfoSource* zip) {
|
||||
// Read and validate the header.
|
||||
tzhead tzh;
|
||||
if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
|
||||
if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
|
||||
return false;
|
||||
Header hdr;
|
||||
if (!hdr.Build(tzh)) return false;
|
||||
std::size_t time_len = 4;
|
||||
if (tzh.tzh_version[0] != '\0') {
|
||||
// Skip the 4-byte data.
|
||||
if (zip->Skip(hdr.DataLength(time_len)) != 0) return false;
|
||||
// Read and validate the header for the 8-byte data.
|
||||
if (zip->Read(&tzh, sizeof(tzh)) != sizeof(tzh)) return false;
|
||||
if (strncmp(tzh.tzh_magic, TZ_MAGIC, sizeof(tzh.tzh_magic)) != 0)
|
||||
return false;
|
||||
if (tzh.tzh_version[0] == '\0') return false;
|
||||
if (!hdr.Build(tzh)) return false;
|
||||
time_len = 8;
|
||||
}
|
||||
if (hdr.typecnt == 0) return false;
|
||||
if (hdr.leapcnt != 0) {
|
||||
// This code assumes 60-second minutes so we do not want
|
||||
// the leap-second encoded zoneinfo. We could reverse the
|
||||
// compensation, but the "right" encoding is rarely used
|
||||
// so currently we simply reject such data.
|
||||
return false;
|
||||
}
|
||||
if (hdr.ttisstdcnt != 0 && hdr.ttisstdcnt != hdr.typecnt) return false;
|
||||
if (hdr.ttisutcnt != 0 && hdr.ttisutcnt != hdr.typecnt) return false;
|
||||
|
||||
// Read the data into a local buffer.
|
||||
std::size_t len = hdr.DataLength(time_len);
|
||||
std::vector<char> tbuf(len);
|
||||
if (zip->Read(tbuf.data(), len) != len) return false;
|
||||
const char* bp = tbuf.data();
|
||||
|
||||
// Decode and validate the transitions.
|
||||
transitions_.reserve(hdr.timecnt + 2); // We might add a couple.
|
||||
transitions_.resize(hdr.timecnt);
|
||||
for (std::size_t i = 0; i != hdr.timecnt; ++i) {
|
||||
transitions_[i].unix_time = (time_len == 4) ? Decode32(bp) : Decode64(bp);
|
||||
bp += time_len;
|
||||
if (i != 0) {
|
||||
// Check that the transitions are ordered by time (as zic guarantees).
|
||||
if (!Transition::ByUnixTime()(transitions_[i - 1], transitions_[i]))
|
||||
return false; // out of order
|
||||
}
|
||||
}
|
||||
bool seen_type_0 = false;
|
||||
for (std::size_t i = 0; i != hdr.timecnt; ++i) {
|
||||
transitions_[i].type_index = Decode8(bp++);
|
||||
if (transitions_[i].type_index >= hdr.typecnt) return false;
|
||||
if (transitions_[i].type_index == 0) seen_type_0 = true;
|
||||
}
|
||||
|
||||
// Decode and validate the transition types.
|
||||
transition_types_.resize(hdr.typecnt);
|
||||
for (std::size_t i = 0; i != hdr.typecnt; ++i) {
|
||||
transition_types_[i].utc_offset =
|
||||
static_cast<std::int_least32_t>(Decode32(bp));
|
||||
if (transition_types_[i].utc_offset >= kSecsPerDay ||
|
||||
transition_types_[i].utc_offset <= -kSecsPerDay)
|
||||
return false;
|
||||
bp += 4;
|
||||
transition_types_[i].is_dst = (Decode8(bp++) != 0);
|
||||
transition_types_[i].abbr_index = Decode8(bp++);
|
||||
if (transition_types_[i].abbr_index >= hdr.charcnt) return false;
|
||||
}
|
||||
|
||||
// Determine the before-first-transition type.
|
||||
default_transition_type_ = 0;
|
||||
if (seen_type_0 && hdr.timecnt != 0) {
|
||||
std::uint_fast8_t index = 0;
|
||||
if (transition_types_[0].is_dst) {
|
||||
index = transitions_[0].type_index;
|
||||
while (index != 0 && transition_types_[index].is_dst) --index;
|
||||
}
|
||||
while (index != hdr.typecnt && transition_types_[index].is_dst) ++index;
|
||||
if (index != hdr.typecnt) default_transition_type_ = index;
|
||||
}
|
||||
|
||||
// Copy all the abbreviations.
|
||||
abbreviations_.assign(bp, hdr.charcnt);
|
||||
bp += hdr.charcnt;
|
||||
|
||||
// Skip the unused portions. We've already dispensed with leap-second
|
||||
// encoded zoneinfo. The ttisstd/ttisgmt indicators only apply when
|
||||
// interpreting a POSIX spec that does not include start/end rules, and
|
||||
// that isn't the case here (see "zic -p").
|
||||
bp += (8 + 4) * hdr.leapcnt; // leap-time + TAI-UTC
|
||||
bp += 1 * hdr.ttisstdcnt; // UTC/local indicators
|
||||
bp += 1 * hdr.ttisutcnt; // standard/wall indicators
|
||||
assert(bp == tbuf.data() + tbuf.size());
|
||||
|
||||
future_spec_.clear();
|
||||
if (tzh.tzh_version[0] != '\0') {
|
||||
// Snarf up the NL-enclosed future POSIX spec. Note
|
||||
// that version '3' files utilize an extended format.
|
||||
auto get_char = [](ZoneInfoSource* azip) -> int {
|
||||
unsigned char ch; // all non-EOF results are positive
|
||||
return (azip->Read(&ch, 1) == 1) ? ch : EOF;
|
||||
};
|
||||
if (get_char(zip) != '\n') return false;
|
||||
for (int c = get_char(zip); c != '\n'; c = get_char(zip)) {
|
||||
if (c == EOF) return false;
|
||||
future_spec_.push_back(static_cast<char>(c));
|
||||
}
|
||||
}
|
||||
|
||||
// We don't check for EOF so that we're forwards compatible.
|
||||
|
||||
// If we did not find version information during the standard loading
|
||||
// process (as of tzh_version '3' that is unsupported), then ask the
|
||||
// ZoneInfoSource for any out-of-bound version string it may be privy to.
|
||||
if (version_.empty()) {
|
||||
version_ = zip->Version();
|
||||
}
|
||||
|
||||
// Trim redundant transitions. zic may have added these to work around
|
||||
// differences between the glibc and reference implementations (see
|
||||
// zic.c:dontmerge) and the Qt library (see zic.c:WORK_AROUND_QTBUG_53071).
|
||||
// For us, they just get in the way when we do future_spec_ extension.
|
||||
while (hdr.timecnt > 1) {
|
||||
if (!EquivTransitions(transitions_[hdr.timecnt - 1].type_index,
|
||||
transitions_[hdr.timecnt - 2].type_index)) {
|
||||
break;
|
||||
}
|
||||
hdr.timecnt -= 1;
|
||||
}
|
||||
transitions_.resize(hdr.timecnt);
|
||||
|
||||
// Ensure that there is always a transition in the first half of the
|
||||
// time line (the second half is handled in ExtendTransitions()) so that
|
||||
// the signed difference between a civil_second and the civil_second of
|
||||
// its previous transition is always representable, without overflow.
|
||||
// A contemporary zic will usually have already done this for us.
|
||||
if (transitions_.empty() || transitions_.front().unix_time >= 0) {
|
||||
Transition& tr(*transitions_.emplace(transitions_.begin()));
|
||||
tr.unix_time = -(1LL << 59); // see tz/zic.c "BIG_BANG"
|
||||
tr.type_index = default_transition_type_;
|
||||
hdr.timecnt += 1;
|
||||
}
|
||||
|
||||
// Extend the transitions using the future specification.
|
||||
ExtendTransitions(name, hdr);
|
||||
|
||||
// Compute the local civil time for each transition and the preceding
|
||||
// second. These will be used for reverse conversions in MakeTime().
|
||||
const TransitionType* ttp = &transition_types_[default_transition_type_];
|
||||
for (std::size_t i = 0; i != transitions_.size(); ++i) {
|
||||
Transition& tr(transitions_[i]);
|
||||
tr.prev_civil_sec = LocalTime(tr.unix_time, *ttp).cs - 1;
|
||||
ttp = &transition_types_[tr.type_index];
|
||||
tr.civil_sec = LocalTime(tr.unix_time, *ttp).cs;
|
||||
if (i != 0) {
|
||||
// Check that the transitions are ordered by civil time. Essentially
|
||||
// this means that an offset change cannot cross another such change.
|
||||
// No one does this in practice, and we depend on it in MakeTime().
|
||||
if (!Transition::ByCivilTime()(transitions_[i - 1], tr))
|
||||
return false; // out of order
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the maximum/minimum civil times that can be converted to a
|
||||
// time_point<seconds> for each of the zone's transition types.
|
||||
for (auto& tt : transition_types_) {
|
||||
tt.civil_max = LocalTime(seconds::max().count(), tt).cs;
|
||||
tt.civil_min = LocalTime(seconds::min().count(), tt).cs;
|
||||
}
|
||||
|
||||
transitions_.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// fopen(3) adaptor.
|
||||
inline FILE* FOpen(const char* path, const char* mode) {
|
||||
#if defined(_MSC_VER)
|
||||
FILE* fp;
|
||||
if (fopen_s(&fp, path, mode) != 0) fp = nullptr;
|
||||
return fp;
|
||||
#else
|
||||
return fopen(path, mode); // TODO: Enable the close-on-exec flag.
|
||||
#endif
|
||||
}
|
||||
|
||||
// A stdio(3)-backed implementation of ZoneInfoSource.
|
||||
class FileZoneInfoSource : public ZoneInfoSource {
|
||||
public:
|
||||
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
|
||||
|
||||
std::size_t Read(void* ptr, std::size_t size) override {
|
||||
size = std::min(size, len_);
|
||||
std::size_t nread = fread(ptr, 1, size, fp_.get());
|
||||
len_ -= nread;
|
||||
return nread;
|
||||
}
|
||||
int Skip(std::size_t offset) override {
|
||||
offset = std::min(offset, len_);
|
||||
int rc = fseek(fp_.get(), static_cast<long>(offset), SEEK_CUR);
|
||||
if (rc == 0) len_ -= offset;
|
||||
return rc;
|
||||
}
|
||||
std::string Version() const override {
|
||||
// TODO: It would nice if the zoneinfo data included the tzdb version.
|
||||
return std::string();
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit FileZoneInfoSource(
|
||||
FILE* fp, std::size_t len = std::numeric_limits<std::size_t>::max())
|
||||
: fp_(fp, fclose), len_(len) {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<FILE, int (*)(FILE*)> fp_;
|
||||
std::size_t len_;
|
||||
};
|
||||
|
||||
std::unique_ptr<ZoneInfoSource> FileZoneInfoSource::Open(
|
||||
const std::string& name) {
|
||||
// Use of the "file:" prefix is intended for testing purposes only.
|
||||
const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
|
||||
|
||||
// Map the time-zone name to a path name.
|
||||
std::string path;
|
||||
if (pos == name.size() || name[pos] != '/') {
|
||||
const char* tzdir = "/usr/share/zoneinfo";
|
||||
char* tzdir_env = nullptr;
|
||||
#if defined(_MSC_VER)
|
||||
_dupenv_s(&tzdir_env, nullptr, "TZDIR");
|
||||
#else
|
||||
tzdir_env = std::getenv("TZDIR");
|
||||
#endif
|
||||
if (tzdir_env && *tzdir_env) tzdir = tzdir_env;
|
||||
path += tzdir;
|
||||
path += '/';
|
||||
#if defined(_MSC_VER)
|
||||
free(tzdir_env);
|
||||
#endif
|
||||
}
|
||||
path.append(name, pos, std::string::npos);
|
||||
|
||||
// Open the zoneinfo file.
|
||||
FILE* fp = FOpen(path.c_str(), "rb");
|
||||
if (fp == nullptr) return nullptr;
|
||||
std::size_t length = 0;
|
||||
if (fseek(fp, 0, SEEK_END) == 0) {
|
||||
long offset = ftell(fp);
|
||||
if (offset >= 0) {
|
||||
length = static_cast<std::size_t>(offset);
|
||||
}
|
||||
rewind(fp);
|
||||
}
|
||||
return std::unique_ptr<ZoneInfoSource>(new FileZoneInfoSource(fp, length));
|
||||
}
|
||||
|
||||
class AndroidZoneInfoSource : public FileZoneInfoSource {
|
||||
public:
|
||||
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
|
||||
std::string Version() const override { return version_; }
|
||||
|
||||
private:
|
||||
explicit AndroidZoneInfoSource(FILE* fp, std::size_t len, const char* vers)
|
||||
: FileZoneInfoSource(fp, len), version_(vers) {}
|
||||
std::string version_;
|
||||
};
|
||||
|
||||
std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
|
||||
const std::string& name) {
|
||||
// Use of the "file:" prefix is intended for testing purposes only.
|
||||
const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;
|
||||
|
||||
// See Android's libc/tzcode/bionic.cpp for additional information.
|
||||
for (const char* tzdata : {"/data/misc/zoneinfo/current/tzdata",
|
||||
"/system/usr/share/zoneinfo/tzdata"}) {
|
||||
std::unique_ptr<FILE, int (*)(FILE*)> fp(FOpen(tzdata, "rb"), fclose);
|
||||
if (fp.get() == nullptr) continue;
|
||||
|
||||
char hbuf[24]; // covers header.zonetab_offset too
|
||||
if (fread(hbuf, 1, sizeof(hbuf), fp.get()) != sizeof(hbuf)) continue;
|
||||
if (strncmp(hbuf, "tzdata", 6) != 0) continue;
|
||||
const char* vers = (hbuf[11] == '\0') ? hbuf + 6 : "";
|
||||
const std::int_fast32_t index_offset = Decode32(hbuf + 12);
|
||||
const std::int_fast32_t data_offset = Decode32(hbuf + 16);
|
||||
if (index_offset < 0 || data_offset < index_offset) continue;
|
||||
if (fseek(fp.get(), static_cast<long>(index_offset), SEEK_SET) != 0)
|
||||
continue;
|
||||
|
||||
char ebuf[52]; // covers entry.unused too
|
||||
const std::size_t index_size =
|
||||
static_cast<std::size_t>(data_offset - index_offset);
|
||||
const std::size_t zonecnt = index_size / sizeof(ebuf);
|
||||
if (zonecnt * sizeof(ebuf) != index_size) continue;
|
||||
for (std::size_t i = 0; i != zonecnt; ++i) {
|
||||
if (fread(ebuf, 1, sizeof(ebuf), fp.get()) != sizeof(ebuf)) break;
|
||||
const std::int_fast32_t start = data_offset + Decode32(ebuf + 40);
|
||||
const std::int_fast32_t length = Decode32(ebuf + 44);
|
||||
if (start < 0 || length < 0) break;
|
||||
ebuf[40] = '\0'; // ensure zone name is NUL terminated
|
||||
if (strcmp(name.c_str() + pos, ebuf) == 0) {
|
||||
if (fseek(fp.get(), static_cast<long>(start), SEEK_SET) != 0) break;
|
||||
return std::unique_ptr<ZoneInfoSource>(new AndroidZoneInfoSource(
|
||||
fp.release(), static_cast<std::size_t>(length), vers));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool TimeZoneInfo::Load(const std::string& name) {
|
||||
// We can ensure that the loading of UTC or any other fixed-offset
|
||||
// zone never fails because the simple, fixed-offset state can be
|
||||
// internally generated. Note that this depends on our choice to not
|
||||
// accept leap-second encoded ("right") zoneinfo.
|
||||
auto offset = seconds::zero();
|
||||
if (FixedOffsetFromName(name, &offset)) {
|
||||
return ResetToBuiltinUTC(offset);
|
||||
}
|
||||
|
||||
// Find and use a ZoneInfoSource to load the named zone.
|
||||
auto zip = cctz_extension::zone_info_source_factory(
|
||||
name, [](const std::string& name) -> std::unique_ptr<ZoneInfoSource> {
|
||||
if (auto zip = FileZoneInfoSource::Open(name)) return zip;
|
||||
if (auto zip = AndroidZoneInfoSource::Open(name)) return zip;
|
||||
return nullptr;
|
||||
});
|
||||
return zip != nullptr && Load(name, zip.get());
|
||||
}
|
||||
|
||||
// BreakTime() translation for a particular transition type.
|
||||
time_zone::absolute_lookup TimeZoneInfo::LocalTime(
|
||||
std::int_fast64_t unix_time, const TransitionType& tt) const {
|
||||
// A civil time in "+offset" looks like (time+offset) in UTC.
|
||||
// Note: We perform two additions in the civil_second domain to
|
||||
// sidestep the chance of overflow in (unix_time + tt.utc_offset).
|
||||
return {(civil_second() + unix_time) + tt.utc_offset, tt.utc_offset,
|
||||
tt.is_dst, &abbreviations_[tt.abbr_index]};
|
||||
}
|
||||
|
||||
// BreakTime() translation for a particular transition.
|
||||
time_zone::absolute_lookup TimeZoneInfo::LocalTime(std::int_fast64_t unix_time,
|
||||
const Transition& tr) const {
|
||||
const TransitionType& tt = transition_types_[tr.type_index];
|
||||
// Note: (unix_time - tr.unix_time) will never overflow as we
|
||||
// have ensured that there is always a "nearby" transition.
|
||||
return {tr.civil_sec + (unix_time - tr.unix_time), // TODO: Optimize.
|
||||
tt.utc_offset, tt.is_dst, &abbreviations_[tt.abbr_index]};
|
||||
}
|
||||
|
||||
// MakeTime() translation with a conversion-preserving +N * 400-year shift.
|
||||
time_zone::civil_lookup TimeZoneInfo::TimeLocal(const civil_second& cs,
|
||||
year_t c4_shift) const {
|
||||
assert(last_year_ - 400 < cs.year() && cs.year() <= last_year_);
|
||||
time_zone::civil_lookup cl = MakeTime(cs);
|
||||
if (c4_shift > seconds::max().count() / kSecsPer400Years) {
|
||||
cl.pre = cl.trans = cl.post = time_point<seconds>::max();
|
||||
} else {
|
||||
const auto offset = seconds(c4_shift * kSecsPer400Years);
|
||||
const auto limit = time_point<seconds>::max() - offset;
|
||||
for (auto* tp : {&cl.pre, &cl.trans, &cl.post}) {
|
||||
if (*tp > limit) {
|
||||
*tp = time_point<seconds>::max();
|
||||
} else {
|
||||
*tp += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
||||
time_zone::absolute_lookup TimeZoneInfo::BreakTime(
|
||||
const time_point<seconds>& tp) const {
|
||||
std::int_fast64_t unix_time = ToUnixSeconds(tp);
|
||||
const std::size_t timecnt = transitions_.size();
|
||||
assert(timecnt != 0); // We always add a transition.
|
||||
|
||||
if (unix_time < transitions_[0].unix_time) {
|
||||
return LocalTime(unix_time, transition_types_[default_transition_type_]);
|
||||
}
|
||||
if (unix_time >= transitions_[timecnt - 1].unix_time) {
|
||||
// After the last transition. If we extended the transitions using
|
||||
// future_spec_, shift back to a supported year using the 400-year
|
||||
// cycle of calendaric equivalence and then compensate accordingly.
|
||||
if (extended_) {
|
||||
const std::int_fast64_t diff =
|
||||
unix_time - transitions_[timecnt - 1].unix_time;
|
||||
const year_t shift = diff / kSecsPer400Years + 1;
|
||||
const auto d = seconds(shift * kSecsPer400Years);
|
||||
time_zone::absolute_lookup al = BreakTime(tp - d);
|
||||
al.cs = YearShift(al.cs, shift * 400);
|
||||
return al;
|
||||
}
|
||||
return LocalTime(unix_time, transitions_[timecnt - 1]);
|
||||
}
|
||||
|
||||
const std::size_t hint = local_time_hint_.load(std::memory_order_relaxed);
|
||||
if (0 < hint && hint < timecnt) {
|
||||
if (transitions_[hint - 1].unix_time <= unix_time) {
|
||||
if (unix_time < transitions_[hint].unix_time) {
|
||||
return LocalTime(unix_time, transitions_[hint - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Transition target = {unix_time, 0, civil_second(), civil_second()};
|
||||
const Transition* begin = &transitions_[0];
|
||||
const Transition* tr = std::upper_bound(begin, begin + timecnt, target,
|
||||
Transition::ByUnixTime());
|
||||
local_time_hint_.store(static_cast<std::size_t>(tr - begin),
|
||||
std::memory_order_relaxed);
|
||||
return LocalTime(unix_time, *--tr);
|
||||
}
|
||||
|
||||
time_zone::civil_lookup TimeZoneInfo::MakeTime(const civil_second& cs) const {
|
||||
const std::size_t timecnt = transitions_.size();
|
||||
assert(timecnt != 0); // We always add a transition.
|
||||
|
||||
// Find the first transition after our target civil time.
|
||||
const Transition* tr = nullptr;
|
||||
const Transition* begin = &transitions_[0];
|
||||
const Transition* end = begin + timecnt;
|
||||
if (cs < begin->civil_sec) {
|
||||
tr = begin;
|
||||
} else if (cs >= transitions_[timecnt - 1].civil_sec) {
|
||||
tr = end;
|
||||
} else {
|
||||
const std::size_t hint = time_local_hint_.load(std::memory_order_relaxed);
|
||||
if (0 < hint && hint < timecnt) {
|
||||
if (transitions_[hint - 1].civil_sec <= cs) {
|
||||
if (cs < transitions_[hint].civil_sec) {
|
||||
tr = begin + hint;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tr == nullptr) {
|
||||
const Transition target = {0, 0, cs, civil_second()};
|
||||
tr = std::upper_bound(begin, end, target, Transition::ByCivilTime());
|
||||
time_local_hint_.store(static_cast<std::size_t>(tr - begin),
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
if (tr == begin) {
|
||||
if (tr->prev_civil_sec >= cs) {
|
||||
// Before first transition, so use the default offset.
|
||||
const TransitionType& tt(transition_types_[default_transition_type_]);
|
||||
if (cs < tt.civil_min) return MakeUnique(time_point<seconds>::min());
|
||||
return MakeUnique(cs - (civil_second() + tt.utc_offset));
|
||||
}
|
||||
// tr->prev_civil_sec < cs < tr->civil_sec
|
||||
return MakeSkipped(*tr, cs);
|
||||
}
|
||||
|
||||
if (tr == end) {
|
||||
if (cs > (--tr)->prev_civil_sec) {
|
||||
// After the last transition. If we extended the transitions using
|
||||
// future_spec_, shift back to a supported year using the 400-year
|
||||
// cycle of calendaric equivalence and then compensate accordingly.
|
||||
if (extended_ && cs.year() > last_year_) {
|
||||
const year_t shift = (cs.year() - last_year_ - 1) / 400 + 1;
|
||||
return TimeLocal(YearShift(cs, shift * -400), shift);
|
||||
}
|
||||
const TransitionType& tt(transition_types_[tr->type_index]);
|
||||
if (cs > tt.civil_max) return MakeUnique(time_point<seconds>::max());
|
||||
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
|
||||
}
|
||||
// tr->civil_sec <= cs <= tr->prev_civil_sec
|
||||
return MakeRepeated(*tr, cs);
|
||||
}
|
||||
|
||||
if (tr->prev_civil_sec < cs) {
|
||||
// tr->prev_civil_sec < cs < tr->civil_sec
|
||||
return MakeSkipped(*tr, cs);
|
||||
}
|
||||
|
||||
if (cs <= (--tr)->prev_civil_sec) {
|
||||
// tr->civil_sec <= cs <= tr->prev_civil_sec
|
||||
return MakeRepeated(*tr, cs);
|
||||
}
|
||||
|
||||
// In between transitions.
|
||||
return MakeUnique(tr->unix_time + (cs - tr->civil_sec));
|
||||
}
|
||||
|
||||
std::string TimeZoneInfo::Version() const { return version_; }
|
||||
|
||||
std::string TimeZoneInfo::Description() const {
|
||||
std::ostringstream oss;
|
||||
oss << "#trans=" << transitions_.size();
|
||||
oss << " #types=" << transition_types_.size();
|
||||
oss << " spec='" << future_spec_ << "'";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
bool TimeZoneInfo::NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const {
|
||||
if (transitions_.empty()) return false;
|
||||
const Transition* begin = &transitions_[0];
|
||||
const Transition* end = begin + transitions_.size();
|
||||
if (begin->unix_time <= -(1LL << 59)) {
|
||||
// Do not report the BIG_BANG found in recent zoneinfo data as it is
|
||||
// really a sentinel, not a transition. See tz/zic.c.
|
||||
++begin;
|
||||
}
|
||||
std::int_fast64_t unix_time = ToUnixSeconds(tp);
|
||||
const Transition target = {unix_time, 0, civil_second(), civil_second()};
|
||||
const Transition* tr =
|
||||
std::upper_bound(begin, end, target, Transition::ByUnixTime());
|
||||
for (; tr != end; ++tr) { // skip no-op transitions
|
||||
std::uint_fast8_t prev_type_index =
|
||||
(tr == begin) ? default_transition_type_ : tr[-1].type_index;
|
||||
if (!EquivTransitions(prev_type_index, tr[0].type_index)) break;
|
||||
}
|
||||
// When tr == end we return false, ignoring future_spec_.
|
||||
if (tr == end) return false;
|
||||
trans->from = tr->prev_civil_sec + 1;
|
||||
trans->to = tr->civil_sec;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimeZoneInfo::PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const {
|
||||
if (transitions_.empty()) return false;
|
||||
const Transition* begin = &transitions_[0];
|
||||
const Transition* end = begin + transitions_.size();
|
||||
if (begin->unix_time <= -(1LL << 59)) {
|
||||
// Do not report the BIG_BANG found in recent zoneinfo data as it is
|
||||
// really a sentinel, not a transition. See tz/zic.c.
|
||||
++begin;
|
||||
}
|
||||
std::int_fast64_t unix_time = ToUnixSeconds(tp);
|
||||
if (FromUnixSeconds(unix_time) != tp) {
|
||||
if (unix_time == std::numeric_limits<std::int_fast64_t>::max()) {
|
||||
if (end == begin) return false; // Ignore future_spec_.
|
||||
trans->from = (--end)->prev_civil_sec + 1;
|
||||
trans->to = end->civil_sec;
|
||||
return true;
|
||||
}
|
||||
unix_time += 1; // ceils
|
||||
}
|
||||
const Transition target = {unix_time, 0, civil_second(), civil_second()};
|
||||
const Transition* tr =
|
||||
std::lower_bound(begin, end, target, Transition::ByUnixTime());
|
||||
for (; tr != begin; --tr) { // skip no-op transitions
|
||||
std::uint_fast8_t prev_type_index =
|
||||
(tr - 1 == begin) ? default_transition_type_ : tr[-2].type_index;
|
||||
if (!EquivTransitions(prev_type_index, tr[-1].type_index)) break;
|
||||
}
|
||||
// When tr == end we return the "last" transition, ignoring future_spec_.
|
||||
if (tr == begin) return false;
|
||||
trans->from = (--tr)->prev_civil_sec + 1;
|
||||
trans->to = tr->civil_sec;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
138
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.h
vendored
Normal file
138
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_info.h
vendored
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
|
||||
#include "time_zone_if.h"
|
||||
#include "tzfile.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// A transition to a new UTC offset.
|
||||
struct Transition {
|
||||
std::int_least64_t unix_time; // the instant of this transition
|
||||
std::uint_least8_t type_index; // index of the transition type
|
||||
civil_second civil_sec; // local civil time of transition
|
||||
civil_second prev_civil_sec; // local civil time one second earlier
|
||||
|
||||
struct ByUnixTime {
|
||||
inline bool operator()(const Transition& lhs, const Transition& rhs) const {
|
||||
return lhs.unix_time < rhs.unix_time;
|
||||
}
|
||||
};
|
||||
struct ByCivilTime {
|
||||
inline bool operator()(const Transition& lhs, const Transition& rhs) const {
|
||||
return lhs.civil_sec < rhs.civil_sec;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// The characteristics of a particular transition.
|
||||
struct TransitionType {
|
||||
std::int_least32_t utc_offset; // the new prevailing UTC offset
|
||||
civil_second civil_max; // max convertible civil time for offset
|
||||
civil_second civil_min; // min convertible civil time for offset
|
||||
bool is_dst; // did we move into daylight-saving time
|
||||
std::uint_least8_t abbr_index; // index of the new abbreviation
|
||||
};
|
||||
|
||||
// A time zone backed by the IANA Time Zone Database (zoneinfo).
|
||||
class TimeZoneInfo : public TimeZoneIf {
|
||||
public:
|
||||
TimeZoneInfo() = default;
|
||||
TimeZoneInfo(const TimeZoneInfo&) = delete;
|
||||
TimeZoneInfo& operator=(const TimeZoneInfo&) = delete;
|
||||
|
||||
// Loads the zoneinfo for the given name, returning true if successful.
|
||||
bool Load(const std::string& name);
|
||||
|
||||
// TimeZoneIf implementations.
|
||||
time_zone::absolute_lookup BreakTime(
|
||||
const time_point<seconds>& tp) const override;
|
||||
time_zone::civil_lookup MakeTime(const civil_second& cs) const override;
|
||||
bool NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const override;
|
||||
bool PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const override;
|
||||
std::string Version() const override;
|
||||
std::string Description() const override;
|
||||
|
||||
private:
|
||||
struct Header { // counts of:
|
||||
std::size_t timecnt; // transition times
|
||||
std::size_t typecnt; // transition types
|
||||
std::size_t charcnt; // zone abbreviation characters
|
||||
std::size_t leapcnt; // leap seconds (we expect none)
|
||||
std::size_t ttisstdcnt; // UTC/local indicators (unused)
|
||||
std::size_t ttisutcnt; // standard/wall indicators (unused)
|
||||
|
||||
bool Build(const tzhead& tzh);
|
||||
std::size_t DataLength(std::size_t time_len) const;
|
||||
};
|
||||
|
||||
void CheckTransition(const std::string& name, const TransitionType& tt,
|
||||
std::int_fast32_t offset, bool is_dst,
|
||||
const std::string& abbr) const;
|
||||
bool EquivTransitions(std::uint_fast8_t tt1_index,
|
||||
std::uint_fast8_t tt2_index) const;
|
||||
void ExtendTransitions(const std::string& name, const Header& hdr);
|
||||
|
||||
bool ResetToBuiltinUTC(const seconds& offset);
|
||||
bool Load(const std::string& name, ZoneInfoSource* zip);
|
||||
|
||||
// Helpers for BreakTime() and MakeTime().
|
||||
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
|
||||
const TransitionType& tt) const;
|
||||
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
|
||||
const Transition& tr) const;
|
||||
time_zone::civil_lookup TimeLocal(const civil_second& cs,
|
||||
year_t c4_shift) const;
|
||||
|
||||
std::vector<Transition> transitions_; // ordered by unix_time and civil_sec
|
||||
std::vector<TransitionType> transition_types_; // distinct transition types
|
||||
std::uint_fast8_t default_transition_type_; // for before first transition
|
||||
std::string abbreviations_; // all the NUL-terminated abbreviations
|
||||
|
||||
std::string version_; // the tzdata version if available
|
||||
std::string future_spec_; // for after the last zic transition
|
||||
bool extended_; // future_spec_ was used to generate transitions
|
||||
year_t last_year_; // the final year of the generated transitions
|
||||
|
||||
// We remember the transitions found during the last BreakTime() and
|
||||
// MakeTime() calls. If the next request is for the same transition we
|
||||
// will avoid re-searching.
|
||||
mutable std::atomic<std::size_t> local_time_hint_ = {}; // BreakTime() hint
|
||||
mutable std::atomic<std::size_t> time_local_hint_ = {}; // MakeTime() hint
|
||||
};
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_INFO_H_
|
||||
308
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.cc
vendored
Normal file
308
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.cc
vendored
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
#endif
|
||||
|
||||
#include "time_zone_libc.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// Uses the globals: '_timezone', '_dstbias' and '_tzname'.
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + _dstbias) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return _timezone + (is_dst ? _dstbias : 0);
|
||||
}
|
||||
auto tm_zone(const std::tm& tm) -> decltype(_tzname[0]) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return _tzname[is_dst];
|
||||
}
|
||||
#elif defined(__sun)
|
||||
// Uses the globals: 'timezone', 'altzone' and 'tzname'.
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(timezone) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return is_dst ? altzone : timezone;
|
||||
}
|
||||
auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return tzname[is_dst];
|
||||
}
|
||||
#elif defined(__native_client__) || defined(__myriad2__) || \
|
||||
defined(__EMSCRIPTEN__)
|
||||
// Uses the globals: 'timezone' and 'tzname'.
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(_timezone + 0) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return _timezone + (is_dst ? 60 * 60 : 0);
|
||||
}
|
||||
auto tm_zone(const std::tm& tm) -> decltype(tzname[0]) {
|
||||
const bool is_dst = tm.tm_isdst > 0;
|
||||
return tzname[is_dst];
|
||||
}
|
||||
#else
|
||||
// Adapt to different spellings of the struct std::tm extension fields.
|
||||
#if defined(tm_gmtoff)
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(tm.tm_gmtoff) {
|
||||
return tm.tm_gmtoff;
|
||||
}
|
||||
#elif defined(__tm_gmtoff)
|
||||
auto tm_gmtoff(const std::tm& tm) -> decltype(tm.__tm_gmtoff) {
|
||||
return tm.__tm_gmtoff;
|
||||
}
|
||||
#else
|
||||
template <typename T>
|
||||
auto tm_gmtoff(const T& tm) -> decltype(tm.tm_gmtoff) {
|
||||
return tm.tm_gmtoff;
|
||||
}
|
||||
template <typename T>
|
||||
auto tm_gmtoff(const T& tm) -> decltype(tm.__tm_gmtoff) {
|
||||
return tm.__tm_gmtoff;
|
||||
}
|
||||
#endif // tm_gmtoff
|
||||
#if defined(tm_zone)
|
||||
auto tm_zone(const std::tm& tm) -> decltype(tm.tm_zone) { return tm.tm_zone; }
|
||||
#elif defined(__tm_zone)
|
||||
auto tm_zone(const std::tm& tm) -> decltype(tm.__tm_zone) {
|
||||
return tm.__tm_zone;
|
||||
}
|
||||
#else
|
||||
template <typename T>
|
||||
auto tm_zone(const T& tm) -> decltype(tm.tm_zone) {
|
||||
return tm.tm_zone;
|
||||
}
|
||||
template <typename T>
|
||||
auto tm_zone(const T& tm) -> decltype(tm.__tm_zone) {
|
||||
return tm.__tm_zone;
|
||||
}
|
||||
#endif // tm_zone
|
||||
#endif
|
||||
|
||||
inline std::tm* gm_time(const std::time_t* timep, std::tm* result) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return gmtime_s(result, timep) ? nullptr : result;
|
||||
#else
|
||||
return gmtime_r(timep, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline std::tm* local_time(const std::time_t* timep, std::tm* result) {
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return localtime_s(result, timep) ? nullptr : result;
|
||||
#else
|
||||
return localtime_r(timep, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Converts a civil second and "dst" flag into a time_t and UTC offset.
|
||||
// Returns false if time_t cannot represent the requested civil second.
|
||||
// Caller must have already checked that cs.year() will fit into a tm_year.
|
||||
bool make_time(const civil_second& cs, int is_dst, std::time_t* t, int* off) {
|
||||
std::tm tm;
|
||||
tm.tm_year = static_cast<int>(cs.year() - year_t{1900});
|
||||
tm.tm_mon = cs.month() - 1;
|
||||
tm.tm_mday = cs.day();
|
||||
tm.tm_hour = cs.hour();
|
||||
tm.tm_min = cs.minute();
|
||||
tm.tm_sec = cs.second();
|
||||
tm.tm_isdst = is_dst;
|
||||
*t = std::mktime(&tm);
|
||||
if (*t == std::time_t{-1}) {
|
||||
std::tm tm2;
|
||||
const std::tm* tmp = local_time(t, &tm2);
|
||||
if (tmp == nullptr || tmp->tm_year != tm.tm_year ||
|
||||
tmp->tm_mon != tm.tm_mon || tmp->tm_mday != tm.tm_mday ||
|
||||
tmp->tm_hour != tm.tm_hour || tmp->tm_min != tm.tm_min ||
|
||||
tmp->tm_sec != tm.tm_sec) {
|
||||
// A true error (not just one second before the epoch).
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*off = static_cast<int>(tm_gmtoff(tm));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find the least time_t in [lo:hi] where local time matches offset, given:
|
||||
// (1) lo doesn't match, (2) hi does, and (3) there is only one transition.
|
||||
std::time_t find_trans(std::time_t lo, std::time_t hi, int offset) {
|
||||
std::tm tm;
|
||||
while (lo + 1 != hi) {
|
||||
const std::time_t mid = lo + (hi - lo) / 2;
|
||||
if (std::tm* tmp = local_time(&mid, &tm)) {
|
||||
if (tm_gmtoff(*tmp) == offset) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid;
|
||||
}
|
||||
} else {
|
||||
// If std::tm cannot hold some result we resort to a linear search,
|
||||
// ignoring all failed conversions. Slow, but never really happens.
|
||||
while (++lo != hi) {
|
||||
if (std::tm* tmp = local_time(&lo, &tm)) {
|
||||
if (tm_gmtoff(*tmp) == offset) break;
|
||||
}
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
}
|
||||
return hi;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TimeZoneLibC::TimeZoneLibC(const std::string& name)
|
||||
: local_(name == "localtime") {}
|
||||
|
||||
time_zone::absolute_lookup TimeZoneLibC::BreakTime(
|
||||
const time_point<seconds>& tp) const {
|
||||
time_zone::absolute_lookup al;
|
||||
al.offset = 0;
|
||||
al.is_dst = false;
|
||||
al.abbr = "-00";
|
||||
|
||||
const std::int_fast64_t s = ToUnixSeconds(tp);
|
||||
|
||||
// If std::time_t cannot hold the input we saturate the output.
|
||||
if (s < std::numeric_limits<std::time_t>::min()) {
|
||||
al.cs = civil_second::min();
|
||||
return al;
|
||||
}
|
||||
if (s > std::numeric_limits<std::time_t>::max()) {
|
||||
al.cs = civil_second::max();
|
||||
return al;
|
||||
}
|
||||
|
||||
const std::time_t t = static_cast<std::time_t>(s);
|
||||
std::tm tm;
|
||||
std::tm* tmp = local_ ? local_time(&t, &tm) : gm_time(&t, &tm);
|
||||
|
||||
// If std::tm cannot hold the result we saturate the output.
|
||||
if (tmp == nullptr) {
|
||||
al.cs = (s < 0) ? civil_second::min() : civil_second::max();
|
||||
return al;
|
||||
}
|
||||
|
||||
const year_t year = tmp->tm_year + year_t{1900};
|
||||
al.cs = civil_second(year, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour,
|
||||
tmp->tm_min, tmp->tm_sec);
|
||||
al.offset = static_cast<int>(tm_gmtoff(*tmp));
|
||||
al.abbr = local_ ? tm_zone(*tmp) : "UTC"; // as expected by cctz
|
||||
al.is_dst = tmp->tm_isdst > 0;
|
||||
return al;
|
||||
}
|
||||
|
||||
time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
|
||||
if (!local_) {
|
||||
// If time_point<seconds> cannot hold the result we saturate.
|
||||
static const civil_second min_tp_cs =
|
||||
civil_second() + ToUnixSeconds(time_point<seconds>::min());
|
||||
static const civil_second max_tp_cs =
|
||||
civil_second() + ToUnixSeconds(time_point<seconds>::max());
|
||||
const time_point<seconds> tp =
|
||||
(cs < min_tp_cs)
|
||||
? time_point<seconds>::min()
|
||||
: (cs > max_tp_cs) ? time_point<seconds>::max()
|
||||
: FromUnixSeconds(cs - civil_second());
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
|
||||
// If tm_year cannot hold the requested year we saturate the result.
|
||||
if (cs.year() < 0) {
|
||||
if (cs.year() < std::numeric_limits<int>::min() + year_t{1900}) {
|
||||
const time_point<seconds> tp = time_point<seconds>::min();
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
} else {
|
||||
if (cs.year() - year_t{1900} > std::numeric_limits<int>::max()) {
|
||||
const time_point<seconds> tp = time_point<seconds>::max();
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
}
|
||||
|
||||
// We probe with "is_dst" values of 0 and 1 to try to distinguish unique
|
||||
// civil seconds from skipped or repeated ones. This is not always possible
|
||||
// however, as the "dst" flag does not change over some offset transitions.
|
||||
// We are also subject to the vagaries of mktime() implementations.
|
||||
std::time_t t0, t1;
|
||||
int offset0, offset1;
|
||||
if (make_time(cs, 0, &t0, &offset0) && make_time(cs, 1, &t1, &offset1)) {
|
||||
if (t0 == t1) {
|
||||
// The civil time was singular (pre == trans == post).
|
||||
const time_point<seconds> tp = FromUnixSeconds(t0);
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
|
||||
if (t0 > t1) {
|
||||
std::swap(t0, t1);
|
||||
std::swap(offset0, offset1);
|
||||
}
|
||||
const std::time_t tt = find_trans(t0, t1, offset1);
|
||||
const time_point<seconds> trans = FromUnixSeconds(tt);
|
||||
|
||||
if (offset0 < offset1) {
|
||||
// The civil time did not exist (pre >= trans > post).
|
||||
const time_point<seconds> pre = FromUnixSeconds(t1);
|
||||
const time_point<seconds> post = FromUnixSeconds(t0);
|
||||
return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
|
||||
}
|
||||
|
||||
// The civil time was ambiguous (pre < trans <= post).
|
||||
const time_point<seconds> pre = FromUnixSeconds(t0);
|
||||
const time_point<seconds> post = FromUnixSeconds(t1);
|
||||
return {time_zone::civil_lookup::REPEATED, pre, trans, post};
|
||||
}
|
||||
|
||||
// make_time() failed somehow so we saturate the result.
|
||||
const time_point<seconds> tp = (cs < civil_second())
|
||||
? time_point<seconds>::min()
|
||||
: time_point<seconds>::max();
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
|
||||
bool TimeZoneLibC::NextTransition(const time_point<seconds>&,
|
||||
time_zone::civil_transition*) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TimeZoneLibC::PrevTransition(const time_point<seconds>&,
|
||||
time_zone::civil_transition*) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string TimeZoneLibC::Version() const {
|
||||
return std::string(); // unknown
|
||||
}
|
||||
|
||||
std::string TimeZoneLibC::Description() const {
|
||||
return local_ ? "localtime" : "UTC";
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
55
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.h
vendored
Normal file
55
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_libc.h
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "time_zone_if.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// A time zone backed by gmtime_r(3), localtime_r(3), and mktime(3),
|
||||
// and which therefore only supports UTC and the local time zone.
|
||||
// TODO: Add support for fixed offsets from UTC.
|
||||
class TimeZoneLibC : public TimeZoneIf {
|
||||
public:
|
||||
explicit TimeZoneLibC(const std::string& name);
|
||||
|
||||
// TimeZoneIf implementations.
|
||||
time_zone::absolute_lookup BreakTime(
|
||||
const time_point<seconds>& tp) const override;
|
||||
time_zone::civil_lookup MakeTime(const civil_second& cs) const override;
|
||||
bool NextTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const override;
|
||||
bool PrevTransition(const time_point<seconds>& tp,
|
||||
time_zone::civil_transition* trans) const override;
|
||||
std::string Version() const override;
|
||||
std::string Description() const override;
|
||||
|
||||
private:
|
||||
const bool local_; // localtime or UTC
|
||||
};
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_LIBC_H_
|
||||
187
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup.cc
vendored
Normal file
187
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup.cc
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <sys/system_properties.h>
|
||||
#if defined(__ANDROID_API__) && __ANDROID_API__ >= 21
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <CoreFoundation/CFTimeZone.h>
|
||||
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "time_zone_fixed.h"
|
||||
#include "time_zone_impl.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
#if defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21
|
||||
namespace {
|
||||
// Android 'L' removes __system_property_get() from the NDK, however
|
||||
// it is still a hidden symbol in libc so we use dlsym() to access it.
|
||||
// See Chromium's base/sys_info_android.cc for a similar example.
|
||||
|
||||
using property_get_func = int (*)(const char*, char*);
|
||||
|
||||
property_get_func LoadSystemPropertyGet() {
|
||||
int flag = RTLD_LAZY | RTLD_GLOBAL;
|
||||
#if defined(RTLD_NOLOAD)
|
||||
flag |= RTLD_NOLOAD; // libc.so should already be resident
|
||||
#endif
|
||||
if (void* handle = dlopen("libc.so", flag)) {
|
||||
void* sym = dlsym(handle, "__system_property_get");
|
||||
dlclose(handle);
|
||||
return reinterpret_cast<property_get_func>(sym);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int __system_property_get(const char* name, char* value) {
|
||||
static property_get_func system_property_get = LoadSystemPropertyGet();
|
||||
return system_property_get ? system_property_get(name, value) : -1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
std::string time_zone::name() const { return effective_impl().Name(); }
|
||||
|
||||
time_zone::absolute_lookup time_zone::lookup(
|
||||
const time_point<seconds>& tp) const {
|
||||
return effective_impl().BreakTime(tp);
|
||||
}
|
||||
|
||||
time_zone::civil_lookup time_zone::lookup(const civil_second& cs) const {
|
||||
return effective_impl().MakeTime(cs);
|
||||
}
|
||||
|
||||
bool time_zone::next_transition(const time_point<seconds>& tp,
|
||||
civil_transition* trans) const {
|
||||
return effective_impl().NextTransition(tp, trans);
|
||||
}
|
||||
|
||||
bool time_zone::prev_transition(const time_point<seconds>& tp,
|
||||
civil_transition* trans) const {
|
||||
return effective_impl().PrevTransition(tp, trans);
|
||||
}
|
||||
|
||||
std::string time_zone::version() const { return effective_impl().Version(); }
|
||||
|
||||
std::string time_zone::description() const {
|
||||
return effective_impl().Description();
|
||||
}
|
||||
|
||||
const time_zone::Impl& time_zone::effective_impl() const {
|
||||
if (impl_ == nullptr) {
|
||||
// Dereferencing an implicit-UTC time_zone is expected to be
|
||||
// rare, so we don't mind paying a small synchronization cost.
|
||||
return *time_zone::Impl::UTC().impl_;
|
||||
}
|
||||
return *impl_;
|
||||
}
|
||||
|
||||
bool load_time_zone(const std::string& name, time_zone* tz) {
|
||||
return time_zone::Impl::LoadTimeZone(name, tz);
|
||||
}
|
||||
|
||||
time_zone utc_time_zone() {
|
||||
return time_zone::Impl::UTC(); // avoid name lookup
|
||||
}
|
||||
|
||||
time_zone fixed_time_zone(const seconds& offset) {
|
||||
time_zone tz;
|
||||
load_time_zone(FixedOffsetToName(offset), &tz);
|
||||
return tz;
|
||||
}
|
||||
|
||||
time_zone local_time_zone() {
|
||||
const char* zone = ":localtime";
|
||||
#if defined(__ANDROID__)
|
||||
char sysprop[PROP_VALUE_MAX];
|
||||
if (__system_property_get("persist.sys.timezone", sysprop) > 0) {
|
||||
zone = sysprop;
|
||||
}
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
std::vector<char> buffer;
|
||||
CFTimeZoneRef tz_default = CFTimeZoneCopyDefault();
|
||||
if (CFStringRef tz_name = CFTimeZoneGetName(tz_default)) {
|
||||
CFStringEncoding encoding = kCFStringEncodingUTF8;
|
||||
CFIndex length = CFStringGetLength(tz_name);
|
||||
buffer.resize(CFStringGetMaximumSizeForEncoding(length, encoding) + 1);
|
||||
if (CFStringGetCString(tz_name, &buffer[0], buffer.size(), encoding)) {
|
||||
zone = &buffer[0];
|
||||
}
|
||||
}
|
||||
CFRelease(tz_default);
|
||||
#endif
|
||||
|
||||
// Allow ${TZ} to override to default zone.
|
||||
char* tz_env = nullptr;
|
||||
#if defined(_MSC_VER)
|
||||
_dupenv_s(&tz_env, nullptr, "TZ");
|
||||
#else
|
||||
tz_env = std::getenv("TZ");
|
||||
#endif
|
||||
if (tz_env) zone = tz_env;
|
||||
|
||||
// We only support the "[:]<zone-name>" form.
|
||||
if (*zone == ':') ++zone;
|
||||
|
||||
// Map "localtime" to a system-specific name, but
|
||||
// allow ${LOCALTIME} to override the default name.
|
||||
char* localtime_env = nullptr;
|
||||
if (strcmp(zone, "localtime") == 0) {
|
||||
#if defined(_MSC_VER)
|
||||
// System-specific default is just "localtime".
|
||||
_dupenv_s(&localtime_env, nullptr, "LOCALTIME");
|
||||
#else
|
||||
zone = "/etc/localtime"; // System-specific default.
|
||||
localtime_env = std::getenv("LOCALTIME");
|
||||
#endif
|
||||
if (localtime_env) zone = localtime_env;
|
||||
}
|
||||
|
||||
const std::string name = zone;
|
||||
#if defined(_MSC_VER)
|
||||
free(localtime_env);
|
||||
free(tz_env);
|
||||
#endif
|
||||
|
||||
time_zone tz;
|
||||
load_time_zone(name, &tz); // Falls back to UTC.
|
||||
// TODO: Follow the RFC3339 "Unknown Local Offset Convention" and
|
||||
// arrange for %z to generate "-0000" when we don't know the local
|
||||
// offset because the load_time_zone() failed and we're using UTC.
|
||||
return tz;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
1438
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
vendored
Normal file
1438
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
159
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.cc
vendored
Normal file
159
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.cc
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "time_zone_posix.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
|
||||
const char kDigits[] = "0123456789";
|
||||
|
||||
const char* ParseInt(const char* p, int min, int max, int* vp) {
|
||||
int value = 0;
|
||||
const char* op = p;
|
||||
const int kMaxInt = std::numeric_limits<int>::max();
|
||||
for (; const char* dp = strchr(kDigits, *p); ++p) {
|
||||
int d = static_cast<int>(dp - kDigits);
|
||||
if (d >= 10) break; // '\0'
|
||||
if (value > kMaxInt / 10) return nullptr;
|
||||
value *= 10;
|
||||
if (value > kMaxInt - d) return nullptr;
|
||||
value += d;
|
||||
}
|
||||
if (p == op || value < min || value > max) return nullptr;
|
||||
*vp = value;
|
||||
return p;
|
||||
}
|
||||
|
||||
// abbr = <.*?> | [^-+,\d]{3,}
|
||||
const char* ParseAbbr(const char* p, std::string* abbr) {
|
||||
const char* op = p;
|
||||
if (*p == '<') { // special zoneinfo <...> form
|
||||
while (*++p != '>') {
|
||||
if (*p == '\0') return nullptr;
|
||||
}
|
||||
abbr->assign(op + 1, static_cast<std::size_t>(p - op) - 1);
|
||||
return ++p;
|
||||
}
|
||||
while (*p != '\0') {
|
||||
if (strchr("-+,", *p)) break;
|
||||
if (strchr(kDigits, *p)) break;
|
||||
++p;
|
||||
}
|
||||
if (p - op < 3) return nullptr;
|
||||
abbr->assign(op, static_cast<std::size_t>(p - op));
|
||||
return p;
|
||||
}
|
||||
|
||||
// offset = [+|-]hh[:mm[:ss]] (aggregated into single seconds value)
|
||||
const char* ParseOffset(const char* p, int min_hour, int max_hour, int sign,
|
||||
std::int_fast32_t* offset) {
|
||||
if (p == nullptr) return nullptr;
|
||||
if (*p == '+' || *p == '-') {
|
||||
if (*p++ == '-') sign = -sign;
|
||||
}
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int seconds = 0;
|
||||
|
||||
p = ParseInt(p, min_hour, max_hour, &hours);
|
||||
if (p == nullptr) return nullptr;
|
||||
if (*p == ':') {
|
||||
p = ParseInt(p + 1, 0, 59, &minutes);
|
||||
if (p == nullptr) return nullptr;
|
||||
if (*p == ':') {
|
||||
p = ParseInt(p + 1, 0, 59, &seconds);
|
||||
if (p == nullptr) return nullptr;
|
||||
}
|
||||
}
|
||||
*offset = sign * ((((hours * 60) + minutes) * 60) + seconds);
|
||||
return p;
|
||||
}
|
||||
|
||||
// datetime = ( Jn | n | Mm.w.d ) [ / offset ]
|
||||
const char* ParseDateTime(const char* p, PosixTransition* res) {
|
||||
if (p != nullptr && *p == ',') {
|
||||
if (*++p == 'M') {
|
||||
int month = 0;
|
||||
if ((p = ParseInt(p + 1, 1, 12, &month)) != nullptr && *p == '.') {
|
||||
int week = 0;
|
||||
if ((p = ParseInt(p + 1, 1, 5, &week)) != nullptr && *p == '.') {
|
||||
int weekday = 0;
|
||||
if ((p = ParseInt(p + 1, 0, 6, &weekday)) != nullptr) {
|
||||
res->date.fmt = PosixTransition::M;
|
||||
res->date.m.month = static_cast<std::int_fast8_t>(month);
|
||||
res->date.m.week = static_cast<std::int_fast8_t>(week);
|
||||
res->date.m.weekday = static_cast<std::int_fast8_t>(weekday);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (*p == 'J') {
|
||||
int day = 0;
|
||||
if ((p = ParseInt(p + 1, 1, 365, &day)) != nullptr) {
|
||||
res->date.fmt = PosixTransition::J;
|
||||
res->date.j.day = static_cast<std::int_fast16_t>(day);
|
||||
}
|
||||
} else {
|
||||
int day = 0;
|
||||
if ((p = ParseInt(p, 0, 365, &day)) != nullptr) {
|
||||
res->date.fmt = PosixTransition::N;
|
||||
res->date.n.day = static_cast<std::int_fast16_t>(day);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p != nullptr) {
|
||||
res->time.offset = 2 * 60 * 60; // default offset is 02:00:00
|
||||
if (*p == '/') p = ParseOffset(p + 1, -167, 167, 1, &res->time.offset);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// spec = std offset [ dst [ offset ] , datetime , datetime ]
|
||||
bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res) {
|
||||
const char* p = spec.c_str();
|
||||
if (*p == ':') return false;
|
||||
|
||||
p = ParseAbbr(p, &res->std_abbr);
|
||||
p = ParseOffset(p, 0, 24, -1, &res->std_offset);
|
||||
if (p == nullptr) return false;
|
||||
if (*p == '\0') return true;
|
||||
|
||||
p = ParseAbbr(p, &res->dst_abbr);
|
||||
if (p == nullptr) return false;
|
||||
res->dst_offset = res->std_offset + (60 * 60); // default
|
||||
if (*p != ',') p = ParseOffset(p, 0, 24, -1, &res->dst_offset);
|
||||
|
||||
p = ParseDateTime(p, &res->dst_start);
|
||||
p = ParseDateTime(p, &res->dst_end);
|
||||
|
||||
return p != nullptr && *p == '\0';
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
132
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.h
vendored
Normal file
132
third_party/abseil_cpp/absl/time/internal/cctz/src/time_zone_posix.h
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Parsing of a POSIX zone spec as described in the TZ part of section 8.3 in
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html.
|
||||
//
|
||||
// The current POSIX spec for America/Los_Angeles is "PST8PDT,M3.2.0,M11.1.0",
|
||||
// which would be broken down as ...
|
||||
//
|
||||
// PosixTimeZone {
|
||||
// std_abbr = "PST"
|
||||
// std_offset = -28800
|
||||
// dst_abbr = "PDT"
|
||||
// dst_offset = -25200
|
||||
// dst_start = PosixTransition {
|
||||
// date {
|
||||
// m {
|
||||
// month = 3
|
||||
// week = 2
|
||||
// weekday = 0
|
||||
// }
|
||||
// }
|
||||
// time {
|
||||
// offset = 7200
|
||||
// }
|
||||
// }
|
||||
// dst_end = PosixTransition {
|
||||
// date {
|
||||
// m {
|
||||
// month = 11
|
||||
// week = 1
|
||||
// weekday = 0
|
||||
// }
|
||||
// }
|
||||
// time {
|
||||
// offset = 7200
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
|
||||
#define ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// The date/time of the transition. The date is specified as either:
|
||||
// (J) the Nth day of the year (1 <= N <= 365), excluding leap days, or
|
||||
// (N) the Nth day of the year (0 <= N <= 365), including leap days, or
|
||||
// (M) the Nth weekday of a month (e.g., the 2nd Sunday in March).
|
||||
// The time, specified as a day offset, identifies the particular moment
|
||||
// of the transition, and may be negative or >= 24h, and in which case
|
||||
// it would take us to another day, and perhaps week, or even month.
|
||||
struct PosixTransition {
|
||||
enum DateFormat { J, N, M };
|
||||
|
||||
struct Date {
|
||||
struct NonLeapDay {
|
||||
std::int_fast16_t day; // day of non-leap year [1:365]
|
||||
};
|
||||
struct Day {
|
||||
std::int_fast16_t day; // day of year [0:365]
|
||||
};
|
||||
struct MonthWeekWeekday {
|
||||
std::int_fast8_t month; // month of year [1:12]
|
||||
std::int_fast8_t week; // week of month [1:5] (5==last)
|
||||
std::int_fast8_t weekday; // 0==Sun, ..., 6=Sat
|
||||
};
|
||||
|
||||
DateFormat fmt;
|
||||
|
||||
union {
|
||||
NonLeapDay j;
|
||||
Day n;
|
||||
MonthWeekWeekday m;
|
||||
};
|
||||
};
|
||||
|
||||
struct Time {
|
||||
std::int_fast32_t offset; // seconds before/after 00:00:00
|
||||
};
|
||||
|
||||
Date date;
|
||||
Time time;
|
||||
};
|
||||
|
||||
// The entirety of a POSIX-string specified time-zone rule. The standard
|
||||
// abbreviation and offset are always given. If the time zone includes
|
||||
// daylight saving, then the daylight abbrevation is non-empty and the
|
||||
// remaining fields are also valid. Note that the start/end transitions
|
||||
// are not ordered---in the southern hemisphere the transition to end
|
||||
// daylight time occurs first in any particular year.
|
||||
struct PosixTimeZone {
|
||||
std::string std_abbr;
|
||||
std::int_fast32_t std_offset;
|
||||
|
||||
std::string dst_abbr;
|
||||
std::int_fast32_t dst_offset;
|
||||
PosixTransition dst_start;
|
||||
PosixTransition dst_end;
|
||||
};
|
||||
|
||||
// Breaks down a POSIX time-zone specification into its constituent pieces,
|
||||
// filling in any missing values (DST offset, or start/end transition times)
|
||||
// with the standard-defined defaults. Returns false if the specification
|
||||
// could not be parsed (although some fields of *res may have been altered).
|
||||
bool ParsePosixSpec(const std::string& spec, PosixTimeZone* res);
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_POSIX_H_
|
||||
122
third_party/abseil_cpp/absl/time/internal/cctz/src/tzfile.h
vendored
Normal file
122
third_party/abseil_cpp/absl/time/internal/cctz/src/tzfile.h
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* Layout and location of TZif files. */
|
||||
|
||||
#ifndef TZFILE_H
|
||||
|
||||
#define TZFILE_H
|
||||
|
||||
/*
|
||||
** This file is in the public domain, so clarified as of
|
||||
** 1996-06-05 by Arthur David Olson.
|
||||
*/
|
||||
|
||||
/*
|
||||
** This header is for use ONLY with the time conversion code.
|
||||
** There is no guarantee that it will remain unchanged,
|
||||
** or that it will remain at all.
|
||||
** Do NOT copy it to any system include directory.
|
||||
** Thank you!
|
||||
*/
|
||||
|
||||
/*
|
||||
** Information about time zone files.
|
||||
*/
|
||||
|
||||
#ifndef TZDIR
|
||||
#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
|
||||
#endif /* !defined TZDIR */
|
||||
|
||||
#ifndef TZDEFAULT
|
||||
#define TZDEFAULT "/etc/localtime"
|
||||
#endif /* !defined TZDEFAULT */
|
||||
|
||||
#ifndef TZDEFRULES
|
||||
#define TZDEFRULES "posixrules"
|
||||
#endif /* !defined TZDEFRULES */
|
||||
|
||||
/* See Internet RFC 8536 for more details about the following format. */
|
||||
|
||||
/*
|
||||
** Each file begins with. . .
|
||||
*/
|
||||
|
||||
#define TZ_MAGIC "TZif"
|
||||
|
||||
struct tzhead {
|
||||
char tzh_magic[4]; /* TZ_MAGIC */
|
||||
char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
|
||||
char tzh_reserved[15]; /* reserved; must be zero */
|
||||
char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
|
||||
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
|
||||
char tzh_leapcnt[4]; /* coded number of leap seconds */
|
||||
char tzh_timecnt[4]; /* coded number of transition times */
|
||||
char tzh_typecnt[4]; /* coded number of local time types */
|
||||
char tzh_charcnt[4]; /* coded number of abbr. chars */
|
||||
};
|
||||
|
||||
/*
|
||||
** . . .followed by. . .
|
||||
**
|
||||
** tzh_timecnt (char [4])s coded transition times a la time(2)
|
||||
** tzh_timecnt (unsigned char)s types of local time starting at above
|
||||
** tzh_typecnt repetitions of
|
||||
** one (char [4]) coded UT offset in seconds
|
||||
** one (unsigned char) used to set tm_isdst
|
||||
** one (unsigned char) that's an abbreviation list index
|
||||
** tzh_charcnt (char)s '\0'-terminated zone abbreviations
|
||||
** tzh_leapcnt repetitions of
|
||||
** one (char [4]) coded leap second transition times
|
||||
** one (char [4]) total correction after above
|
||||
** tzh_ttisstdcnt (char)s indexed by type; if 1, transition
|
||||
** time is standard time, if 0,
|
||||
** transition time is local (wall clock)
|
||||
** time; if absent, transition times are
|
||||
** assumed to be local time
|
||||
** tzh_ttisutcnt (char)s indexed by type; if 1, transition
|
||||
** time is UT, if 0, transition time is
|
||||
** local time; if absent, transition
|
||||
** times are assumed to be local time.
|
||||
** When this is 1, the corresponding
|
||||
** std/wall indicator must also be 1.
|
||||
*/
|
||||
|
||||
/*
|
||||
** If tzh_version is '2' or greater, the above is followed by a second instance
|
||||
** of tzhead and a second instance of the data in which each coded transition
|
||||
** time uses 8 rather than 4 chars,
|
||||
** then a POSIX-TZ-environment-variable-style string for use in handling
|
||||
** instants after the last transition time stored in the file
|
||||
** (with nothing between the newlines if there is no POSIX representation for
|
||||
** such instants).
|
||||
**
|
||||
** If tz_version is '3' or greater, the above is extended as follows.
|
||||
** First, the POSIX TZ string's hour offset may range from -167
|
||||
** through 167 as compared to the POSIX-required 0 through 24.
|
||||
** Second, its DST start time may be January 1 at 00:00 and its stop
|
||||
** time December 31 at 24:00 plus the difference between DST and
|
||||
** standard time, indicating DST all year.
|
||||
*/
|
||||
|
||||
/*
|
||||
** In the current implementation, "tzset()" refuses to deal with files that
|
||||
** exceed any of the limits below.
|
||||
*/
|
||||
|
||||
#ifndef TZ_MAX_TIMES
|
||||
#define TZ_MAX_TIMES 2000
|
||||
#endif /* !defined TZ_MAX_TIMES */
|
||||
|
||||
#ifndef TZ_MAX_TYPES
|
||||
/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
|
||||
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
|
||||
#endif /* !defined TZ_MAX_TYPES */
|
||||
|
||||
#ifndef TZ_MAX_CHARS
|
||||
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
|
||||
/* (limited by what unsigned chars can hold) */
|
||||
#endif /* !defined TZ_MAX_CHARS */
|
||||
|
||||
#ifndef TZ_MAX_LEAPS
|
||||
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
|
||||
#endif /* !defined TZ_MAX_LEAPS */
|
||||
|
||||
#endif /* !defined TZFILE_H */
|
||||
115
third_party/abseil_cpp/absl/time/internal/cctz/src/zone_info_source.cc
vendored
Normal file
115
third_party/abseil_cpp/absl/time/internal/cctz/src/zone_info_source.cc
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/zone_info_source.h"
|
||||
|
||||
#include "absl/base/config.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
// Defined out-of-line to avoid emitting a weak vtable in all TUs.
|
||||
ZoneInfoSource::~ZoneInfoSource() {}
|
||||
std::string ZoneInfoSource::Version() const { return std::string(); }
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz_extension {
|
||||
|
||||
namespace {
|
||||
|
||||
// A default for cctz_extension::zone_info_source_factory, which simply
|
||||
// defers to the fallback factory.
|
||||
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource> DefaultFactory(
|
||||
const std::string& name,
|
||||
const std::function<
|
||||
std::unique_ptr<absl::time_internal::cctz::ZoneInfoSource>(
|
||||
const std::string& name)>& fallback_factory) {
|
||||
return fallback_factory(name);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// A "weak" definition for cctz_extension::zone_info_source_factory.
|
||||
// The user may override this with their own "strong" definition (see
|
||||
// zone_info_source.h).
|
||||
#if !defined(__has_attribute)
|
||||
#define __has_attribute(x) 0
|
||||
#endif
|
||||
// MinGW is GCC on Windows, so while it asserts __has_attribute(weak), the
|
||||
// Windows linker cannot handle that. Nor does the MinGW compiler know how to
|
||||
// pass "#pragma comment(linker, ...)" to the Windows linker.
|
||||
#if (__has_attribute(weak) || defined(__GNUC__)) && !defined(__MINGW32__)
|
||||
ZoneInfoSourceFactory zone_info_source_factory __attribute__((weak)) =
|
||||
DefaultFactory;
|
||||
#elif defined(_MSC_VER) && !defined(__MINGW32__) && !defined(_LIBCPP_VERSION)
|
||||
extern ZoneInfoSourceFactory zone_info_source_factory;
|
||||
extern ZoneInfoSourceFactory default_factory;
|
||||
ZoneInfoSourceFactory default_factory = DefaultFactory;
|
||||
#if defined(_M_IX86)
|
||||
#pragma comment( \
|
||||
linker, \
|
||||
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@@ZA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@ABV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@@ZA")
|
||||
#elif defined(_M_IA_64) || defined(_M_AMD64) || defined(_M_ARM64)
|
||||
#pragma comment( \
|
||||
linker, \
|
||||
"/alternatename:?zone_info_source_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@@ZEA=?default_factory@cctz_extension@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@3P6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@AEBV?$function@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@U?$default_delete@VZoneInfoSource@cctz@time_internal@" ABSL_INTERNAL_MANGLED_NS \
|
||||
"@@@std@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z@" ABSL_INTERNAL_MANGLED_BACKREFERENCE \
|
||||
"@@ZEA")
|
||||
#else
|
||||
#error Unsupported MSVC platform
|
||||
#endif // _M_<PLATFORM>
|
||||
#else
|
||||
// Make it a "strong" definition if we have no other choice.
|
||||
ZoneInfoSourceFactory zone_info_source_factory = DefaultFactory;
|
||||
#endif
|
||||
|
||||
} // namespace cctz_extension
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
37
third_party/abseil_cpp/absl/time/internal/cctz/testdata/README.zoneinfo
vendored
Normal file
37
third_party/abseil_cpp/absl/time/internal/cctz/testdata/README.zoneinfo
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
testdata/zoneinfo contains time-zone data files that may be used with CCTZ.
|
||||
Install them in a location referenced by the ${TZDIR} environment variable.
|
||||
Symbolic and hard links have been eliminated for portability.
|
||||
|
||||
On Linux systems the distribution's versions of these files can probably
|
||||
already be found in the default ${TZDIR} location, /usr/share/zoneinfo.
|
||||
|
||||
New versions can be generated using the following shell script.
|
||||
|
||||
#!/bin/sh -
|
||||
set -e
|
||||
DESTDIR=$(mktemp -d)
|
||||
trap "rm -fr ${DESTDIR}" 0 2 15
|
||||
(
|
||||
cd ${DESTDIR}
|
||||
git clone https://github.com/eggert/tz.git
|
||||
make --directory=tz \
|
||||
install DESTDIR=${DESTDIR} \
|
||||
DATAFORM=vanguard \
|
||||
TZDIR=/zoneinfo \
|
||||
REDO=posix_only \
|
||||
LOCALTIME=Factory \
|
||||
TZDATA_TEXT= \
|
||||
ZONETABLES=zone1970.tab
|
||||
tar --create --dereference --hard-dereference --file tzfile.tar \
|
||||
--directory=tz tzfile.h
|
||||
tar --create --dereference --hard-dereference --file zoneinfo.tar \
|
||||
--exclude=zoneinfo/posixrules zoneinfo \
|
||||
--directory=tz version
|
||||
)
|
||||
tar --extract --directory src --file ${DESTDIR}/tzfile.tar
|
||||
tar --extract --directory testdata --file ${DESTDIR}/zoneinfo.tar
|
||||
exit 0
|
||||
|
||||
To run the CCTZ tests using the testdata/zoneinfo files, execute:
|
||||
|
||||
bazel test --test_env=TZDIR=${PWD}/testdata/zoneinfo ...
|
||||
1
third_party/abseil_cpp/absl/time/internal/cctz/testdata/version
vendored
Normal file
1
third_party/abseil_cpp/absl/time/internal/cctz/testdata/version
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
2020a
|
||||
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Abidjan
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Accra
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Addis_Ababa
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Algiers
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmara
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Asmera
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bamako
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bangui
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Banjul
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bissau
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Blantyre
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Brazzaville
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Bujumbura
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Cairo
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Casablanca
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ceuta
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Conakry
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dakar
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Dar_es_Salaam
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Djibouti
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Douala
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/El_Aaiun
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Freetown
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Gaborone
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Harare
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Johannesburg
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Juba
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kampala
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Khartoum
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kigali
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Kinshasa
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lagos
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Libreville
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lome
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Luanda
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lubumbashi
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Lusaka
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Malabo
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maputo
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Maseru
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mbabane
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Mogadishu
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Monrovia
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nairobi
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ndjamena
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Niamey
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Nouakchott
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Ouagadougou
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Porto-Novo
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Sao_Tome
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Timbuktu
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tripoli
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Tunis
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/Africa/Windhoek
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adak
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Adak
vendored
Normal file
Binary file not shown.
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
vendored
Normal file
BIN
third_party/abseil_cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Anchorage
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue