Add 'third_party/abseil_cpp/' from commit '768eb2ca28'

git-subtree-dir: third_party/abseil_cpp
git-subtree-mainline: ffb2ae54be
git-subtree-split: 768eb2ca28
This commit is contained in:
Vincent Ambo 2020-05-20 02:32:24 +01:00
commit fc8dc48020
1276 changed files with 208196 additions and 0 deletions

View 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",
],
)

View 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
)

View 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

View 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_

View 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

File diff suppressed because it is too large Load diff

View 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"

View 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_

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load diff

View 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

View 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

View 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

View 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

View 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_

View 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_

View 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_

View 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_

File diff suppressed because it is too large Load diff

View 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

File diff suppressed because it is too large Load diff

View 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

View 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_

View 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

File diff suppressed because it is too large Load diff

View 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

View 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_

View 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

View 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_

View 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

View 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_

View 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

View 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_

View 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

File diff suppressed because it is too large Load diff

View 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

View 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_

View 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 */

View 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

View 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 ...

View file

@ -0,0 +1 @@
2020a

Some files were not shown because too many files have changed in this diff Show more