Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
200
TMessagesProj/jni/voip/webrtc/absl/time/civil_time.cc
Normal file
200
TMessagesProj/jni/voip/webrtc/absl/time/civil_time.cc
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
// 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 <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#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();
|
||||
return StrCat(cs.year(), FormatTime(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-%d%ET%H:%M:%S", c);
|
||||
}
|
||||
std::string FormatCivilTime(CivilMinute c) {
|
||||
return FormatYearAnd("-%m-%d%ET%H:%M", c);
|
||||
}
|
||||
std::string FormatCivilTime(CivilHour c) {
|
||||
return FormatYearAnd("-%m-%d%ET%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-%d%ET%H:%M:%S", s, c);
|
||||
}
|
||||
bool ParseCivilTime(string_view s, CivilMinute* c) {
|
||||
return ParseYearAnd("-%m-%d%ET%H:%M", s, c);
|
||||
}
|
||||
bool ParseCivilTime(string_view s, CivilHour* c) {
|
||||
return ParseYearAnd("-%m-%d%ET%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);
|
||||
}
|
||||
|
||||
bool AbslParseFlag(string_view s, CivilSecond* c, std::string*) {
|
||||
return ParseLenientCivilTime(s, c);
|
||||
}
|
||||
bool AbslParseFlag(string_view s, CivilMinute* c, std::string*) {
|
||||
return ParseLenientCivilTime(s, c);
|
||||
}
|
||||
bool AbslParseFlag(string_view s, CivilHour* c, std::string*) {
|
||||
return ParseLenientCivilTime(s, c);
|
||||
}
|
||||
bool AbslParseFlag(string_view s, CivilDay* c, std::string*) {
|
||||
return ParseLenientCivilTime(s, c);
|
||||
}
|
||||
bool AbslParseFlag(string_view s, CivilMonth* c, std::string*) {
|
||||
return ParseLenientCivilTime(s, c);
|
||||
}
|
||||
bool AbslParseFlag(string_view s, CivilYear* c, std::string*) {
|
||||
return ParseLenientCivilTime(s, c);
|
||||
}
|
||||
std::string AbslUnparseFlag(CivilSecond c) { return FormatCivilTime(c); }
|
||||
std::string AbslUnparseFlag(CivilMinute c) { return FormatCivilTime(c); }
|
||||
std::string AbslUnparseFlag(CivilHour c) { return FormatCivilTime(c); }
|
||||
std::string AbslUnparseFlag(CivilDay c) { return FormatCivilTime(c); }
|
||||
std::string AbslUnparseFlag(CivilMonth c) { return FormatCivilTime(c); }
|
||||
std::string AbslUnparseFlag(CivilYear c) { return FormatCivilTime(c); }
|
||||
|
||||
} // namespace time_internal
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
589
TMessagesProj/jni/voip/webrtc/absl/time/civil_time.h
Normal file
589
TMessagesProj/jni/voip/webrtc/absl/time/civil_time.h
Normal file
|
|
@ -0,0 +1,589 @@
|
|||
// 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, 7, 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, 7, 20);
|
||||
|
||||
#ifndef ABSL_TIME_CIVIL_TIME_H_
|
||||
#define ABSL_TIME_CIVIL_TIME_H_
|
||||
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#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);
|
||||
|
||||
// Support for StrFormat(), StrCat(), etc
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, CivilSecond c) {
|
||||
sink.Append(FormatCivilTime(c));
|
||||
}
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, CivilMinute c) {
|
||||
sink.Append(FormatCivilTime(c));
|
||||
}
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, CivilHour c) {
|
||||
sink.Append(FormatCivilTime(c));
|
||||
}
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, CivilDay c) {
|
||||
sink.Append(FormatCivilTime(c));
|
||||
}
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, CivilMonth c) {
|
||||
sink.Append(FormatCivilTime(c));
|
||||
}
|
||||
template <typename Sink>
|
||||
void AbslStringify(Sink& sink, CivilYear c) {
|
||||
sink.Append(FormatCivilTime(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);
|
||||
|
||||
// AbslParseFlag()
|
||||
//
|
||||
// Parses the command-line flag string representation `s` into a civil-time
|
||||
// value. Flags must be specified in a format that is valid for
|
||||
// `absl::ParseLenientCivilTime()`.
|
||||
bool AbslParseFlag(absl::string_view s, CivilSecond* c, std::string* error);
|
||||
bool AbslParseFlag(absl::string_view s, CivilMinute* c, std::string* error);
|
||||
bool AbslParseFlag(absl::string_view s, CivilHour* c, std::string* error);
|
||||
bool AbslParseFlag(absl::string_view s, CivilDay* c, std::string* error);
|
||||
bool AbslParseFlag(absl::string_view s, CivilMonth* c, std::string* error);
|
||||
bool AbslParseFlag(absl::string_view s, CivilYear* c, std::string* error);
|
||||
|
||||
// AbslUnparseFlag()
|
||||
//
|
||||
// Unparses a civil-time value into a command-line string representation using
|
||||
// the format specified by `absl::ParseCivilTime()`.
|
||||
std::string AbslUnparseFlag(CivilSecond c);
|
||||
std::string AbslUnparseFlag(CivilMinute c);
|
||||
std::string AbslUnparseFlag(CivilHour c);
|
||||
std::string AbslUnparseFlag(CivilDay c);
|
||||
std::string AbslUnparseFlag(CivilMonth c);
|
||||
std::string AbslUnparseFlag(CivilYear c);
|
||||
|
||||
} // namespace time_internal
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_CIVIL_TIME_H_
|
||||
130
TMessagesProj/jni/voip/webrtc/absl/time/civil_time_benchmark.cc
Normal file
130
TMessagesProj/jni/voip/webrtc/absl/time/civil_time_benchmark.cc
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
// 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 <cstddef>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#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()) {
|
||||
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()) {
|
||||
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()) {
|
||||
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()) {
|
||||
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) {
|
||||
size_t hash = absl_hasher(civil_time);
|
||||
benchmark::DoNotOptimize(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
1262
TMessagesProj/jni/voip/webrtc/absl/time/civil_time_test.cc
Normal file
1262
TMessagesProj/jni/voip/webrtc/absl/time/civil_time_test.cc
Normal file
File diff suppressed because it is too large
Load diff
604
TMessagesProj/jni/voip/webrtc/absl/time/clock.cc
Normal file
604
TMessagesProj/jni/voip/webrtc/absl/time/clock.cc
Normal file
|
|
@ -0,0 +1,604 @@
|
|||
// 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"
|
||||
#include "absl/base/optimization.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.
|
||||
// By default, the fast algorithm based on the cyclecount is disabled because in
|
||||
// certain situations, for example, if the OS enters a "sleep" mode, it may
|
||||
// produce incorrect values immediately upon waking.
|
||||
// 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
|
||||
#define ABSL_USE_CYCLECLOCK_FOR_GET_CURRENT_TIME_NANOS 0
|
||||
#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
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
|
||||
// On some processors, consecutive reads of the cycle counter may yield the
|
||||
// same value (weakly-increasing). In debug mode, clear the least significant
|
||||
// bits to discourage depending on a strictly-increasing Now() value.
|
||||
// In x86-64's debug mode, discourage depending on a strictly-increasing Now()
|
||||
// value.
|
||||
#if !defined(NDEBUG) && defined(__x86_64__)
|
||||
constexpr int64_t kCycleClockNowMask = ~int64_t{0xff};
|
||||
#else
|
||||
constexpr int64_t kCycleClockNowMask = ~int64_t{0};
|
||||
#endif
|
||||
|
||||
// This is a friend wrapper around UnscaledCycleClock::Now()
|
||||
// (needed to access UnscaledCycleClock).
|
||||
class UnscaledCycleClockWrapperForGetCurrentTime {
|
||||
public:
|
||||
static int64_t Now() {
|
||||
return base_internal::UnscaledCycleClock::Now() & kCycleClockNowMask;
|
||||
}
|
||||
};
|
||||
} // namespace time_internal
|
||||
|
||||
// uint64_t is used in this module to provide an extra bit in multiplications
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// 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");
|
||||
|
||||
// data from a sample of the kernel's time value
|
||||
struct TimeSampleAtomic {
|
||||
std::atomic<uint64_t> raw_ns{0}; // raw kernel time
|
||||
std::atomic<uint64_t> base_ns{0}; // our estimate of time
|
||||
std::atomic<uint64_t> base_cycles{0}; // cycle counter reading
|
||||
std::atomic<uint64_t> nsscaled_per_cycle{0}; // 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{0};
|
||||
};
|
||||
// Same again, but with non-atomic types
|
||||
struct TimeSample {
|
||||
uint64_t raw_ns = 0; // raw kernel time
|
||||
uint64_t base_ns = 0; // our estimate of time
|
||||
uint64_t base_cycles = 0; // cycle counter reading
|
||||
uint64_t nsscaled_per_cycle = 0; // cycle period
|
||||
uint64_t min_cycles_per_sample = 0; // approx cycles before next sample
|
||||
};
|
||||
|
||||
struct ABSL_CACHELINE_ALIGNED TimeState {
|
||||
std::atomic<uint64_t> seq{0};
|
||||
TimeSampleAtomic last_sample; // the last sample; under seq
|
||||
|
||||
// The following counters are used only by the test code.
|
||||
int64_t stats_initializations{0};
|
||||
int64_t stats_reinitializations{0};
|
||||
int64_t stats_calibrations{0};
|
||||
int64_t stats_slow_paths{0};
|
||||
int64_t stats_fast_slow_paths{0};
|
||||
|
||||
uint64_t last_now_cycles ABSL_GUARDED_BY(lock){0};
|
||||
|
||||
// Used by GetCurrentTimeNanosFromKernel().
|
||||
// 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.
|
||||
std::atomic<uint64_t> approx_syscall_time_in_cycles{10 * 1000};
|
||||
// Number of times in a row we've seen a kernel time call take substantially
|
||||
// less than approx_syscall_time_in_cycles.
|
||||
std::atomic<uint32_t> kernel_time_seen_smaller{0};
|
||||
|
||||
// A reader-writer lock protecting the static locations below.
|
||||
// See SeqAcquire() and SeqRelease() above.
|
||||
absl::base_internal::SpinLock lock{absl::kConstInit,
|
||||
base_internal::SCHEDULE_KERNEL_ONLY};
|
||||
};
|
||||
ABSL_CONST_INIT static TimeState time_state;
|
||||
|
||||
// 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)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(time_state.lock) {
|
||||
uint64_t local_approx_syscall_time_in_cycles = // local copy
|
||||
time_state.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 =
|
||||
static_cast<uint64_t>(GET_CURRENT_TIME_NANOS_CYCLECLOCK_NOW());
|
||||
current_time_nanos_from_system = GET_CURRENT_TIME_NANOS_FROM_SYSTEM();
|
||||
after_cycles =
|
||||
static_cast<uint64_t>(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;
|
||||
}
|
||||
time_state.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));
|
||||
|
||||
// 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
|
||||
time_state.kernel_time_seen_smaller.store(0, std::memory_order_relaxed);
|
||||
} else if (time_state.kernel_time_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);
|
||||
time_state.approx_syscall_time_in_cycles.store(new_approximation,
|
||||
std::memory_order_relaxed);
|
||||
time_state.kernel_time_seen_smaller.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
*cycleclock = after_cycles;
|
||||
return current_time_nanos_from_system;
|
||||
}
|
||||
|
||||
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 =
|
||||
static_cast<uint64_t>(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 = time_state.seq.load(std::memory_order_acquire);
|
||||
|
||||
base_ns = time_state.last_sample.base_ns.load(std::memory_order_relaxed);
|
||||
base_cycles =
|
||||
time_state.last_sample.base_cycles.load(std::memory_order_relaxed);
|
||||
nsscaled_per_cycle =
|
||||
time_state.last_sample.nsscaled_per_cycle.load(std::memory_order_relaxed);
|
||||
min_cycles_per_sample = time_state.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 = time_state.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;
|
||||
if (seq_read0 == seq_read1 && (seq_read0 & 1) == 0 &&
|
||||
(delta_cycles = now_cycles - base_cycles) < min_cycles_per_sample) {
|
||||
return static_cast<int64_t>(
|
||||
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(time_state.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.
|
||||
time_state.lock.Lock();
|
||||
|
||||
// Sample the kernel time base. This is the definition of
|
||||
// "now" if we take the slow path.
|
||||
uint64_t now_cycles;
|
||||
uint64_t now_ns = static_cast<uint64_t>(
|
||||
GetCurrentTimeNanosFromKernel(time_state.last_now_cycles, &now_cycles));
|
||||
time_state.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(&time_state.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);
|
||||
time_state.stats_fast_slow_paths++;
|
||||
} else {
|
||||
estimated_base_ns =
|
||||
UpdateLastSample(now_cycles, now_ns, delta_cycles, &sample);
|
||||
}
|
||||
|
||||
time_state.lock.Unlock();
|
||||
|
||||
return static_cast<int64_t>(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(time_state.lock) {
|
||||
uint64_t estimated_base_ns = now_ns;
|
||||
uint64_t lock_value =
|
||||
SeqAcquire(&time_state.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.
|
||||
time_state.last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
|
||||
time_state.last_sample.base_ns.store(estimated_base_ns,
|
||||
std::memory_order_relaxed);
|
||||
time_state.last_sample.base_cycles.store(now_cycles,
|
||||
std::memory_order_relaxed);
|
||||
time_state.last_sample.nsscaled_per_cycle.store(0,
|
||||
std::memory_order_relaxed);
|
||||
time_state.last_sample.min_cycles_per_sample.store(
|
||||
0, std::memory_order_relaxed);
|
||||
time_state.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);
|
||||
|
||||
// Estimate low by this much.
|
||||
int64_t diff_ns = static_cast<int64_t>(now_ns - estimated_base_ns);
|
||||
|
||||
// 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 = static_cast<uint64_t>(static_cast<int64_t>(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
|
||||
time_state.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);
|
||||
time_state.last_sample.min_cycles_per_sample.store(
|
||||
new_min_cycles_per_sample, std::memory_order_relaxed);
|
||||
time_state.stats_calibrations++;
|
||||
} else { // something went wrong; forget the slope
|
||||
time_state.last_sample.nsscaled_per_cycle.store(
|
||||
0, std::memory_order_relaxed);
|
||||
time_state.last_sample.min_cycles_per_sample.store(
|
||||
0, std::memory_order_relaxed);
|
||||
estimated_base_ns = now_ns;
|
||||
time_state.stats_reinitializations++;
|
||||
}
|
||||
time_state.last_sample.raw_ns.store(now_ns, std::memory_order_relaxed);
|
||||
time_state.last_sample.base_ns.store(estimated_base_ns,
|
||||
std::memory_order_relaxed);
|
||||
time_state.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
|
||||
time_state.stats_slow_paths++;
|
||||
}
|
||||
|
||||
SeqRelease(&time_state.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(static_cast<DWORD>(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 ABSL_INTERNAL_C_SYMBOL(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"
|
||||
78
TMessagesProj/jni/voip/webrtc/absl/time/clock.h
Normal file
78
TMessagesProj/jni/voip/webrtc/absl/time/clock.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// 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 <cstdint>
|
||||
|
||||
#include "absl/base/config.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" {
|
||||
ABSL_DLL void ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(
|
||||
absl::Duration duration);
|
||||
} // extern "C"
|
||||
|
||||
inline void absl::SleepFor(absl::Duration duration) {
|
||||
ABSL_INTERNAL_C_SYMBOL(AbslInternalSleepFor)(duration);
|
||||
}
|
||||
|
||||
#endif // ABSL_TIME_CLOCK_H_
|
||||
74
TMessagesProj/jni/voip/webrtc/absl/time/clock_benchmark.cc
Normal file
74
TMessagesProj/jni/voip/webrtc/absl/time/clock_benchmark.cc
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#endif // _WIN32
|
||||
#include <cstdio>
|
||||
|
||||
#include "absl/base/internal/cycleclock.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
void BM_Clock_Now_AbslTime(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::Now());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_AbslTime);
|
||||
|
||||
void BM_Clock_Now_GetCurrentTimeNanos(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::GetCurrentTimeNanos());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_GetCurrentTimeNanos);
|
||||
|
||||
void BM_Clock_Now_AbslTime_ToUnixNanos(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToUnixNanos(absl::Now()));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_AbslTime_ToUnixNanos);
|
||||
|
||||
void BM_Clock_Now_CycleClock(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::base_internal::CycleClock::Now());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_CycleClock);
|
||||
|
||||
#if !defined(_WIN32)
|
||||
static void BM_Clock_Now_gettimeofday(benchmark::State& state) {
|
||||
struct timeval tv;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(gettimeofday(&tv, nullptr));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_gettimeofday);
|
||||
|
||||
static void BM_Clock_Now_clock_gettime(benchmark::State& state) {
|
||||
struct timespec ts;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(clock_gettime(CLOCK_REALTIME, &ts));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Clock_Now_clock_gettime);
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace
|
||||
122
TMessagesProj/jni/voip/webrtc/absl/time/clock_test.cc
Normal file
122
TMessagesProj/jni/voip/webrtc/absl/time/clock_test.cc
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
// 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>
|
||||
#ifdef _AIX
|
||||
// sig_t is not defined in AIX.
|
||||
typedef void (*sig_t)(int);
|
||||
#endif
|
||||
#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
|
||||
913
TMessagesProj/jni/voip/webrtc/absl/time/duration.cc
Normal file
913
TMessagesProj/jni/voip/webrtc/absl/time/duration.cc
Normal file
|
|
@ -0,0 +1,913 @@
|
|||
// 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 <chrono> // NOLINT(build/c++11)
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
#include "absl/base/casts.h"
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/numeric/int128.h"
|
||||
#include "absl/strings/string_view.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;
|
||||
}
|
||||
|
||||
// *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 > Uint128Max() / b) ? Uint128Max() : 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 = std::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) {
|
||||
if (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_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_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_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 {
|
||||
|
||||
int64_t IDivSlowPath(bool satq, const Duration num, const Duration den,
|
||||
Duration* rem) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
ABSL_ATTRIBUTE_ALWAYS_INLINE inline int64_t IDivDurationImpl(bool satq,
|
||||
const Duration num,
|
||||
const Duration den,
|
||||
Duration* rem) {
|
||||
int64_t q = 0;
|
||||
if (IDivFastPath(num, den, &q, rem)) {
|
||||
return q;
|
||||
}
|
||||
return IDivSlowPath(satq, num, den, rem);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int64_t IDivDuration(Duration num, Duration den, Duration* rem) {
|
||||
return IDivDurationImpl(true, num, den,
|
||||
rem); // trunc towards zero
|
||||
}
|
||||
|
||||
//
|
||||
// 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_.Get();
|
||||
rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) +
|
||||
EncodeTwosComp(rhs.rep_hi_.Get()));
|
||||
if (rep_lo_ >= kTicksPerSecond - rhs.rep_lo_) {
|
||||
rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) + 1);
|
||||
rep_lo_ -= kTicksPerSecond;
|
||||
}
|
||||
rep_lo_ += rhs.rep_lo_;
|
||||
if (rhs.rep_hi_.Get() < 0 ? rep_hi_.Get() > orig_rep_hi
|
||||
: rep_hi_.Get() < orig_rep_hi) {
|
||||
return *this =
|
||||
rhs.rep_hi_.Get() < 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_.Get() >= 0 ? -InfiniteDuration()
|
||||
: InfiniteDuration();
|
||||
}
|
||||
const int64_t orig_rep_hi = rep_hi_.Get();
|
||||
rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) -
|
||||
EncodeTwosComp(rhs.rep_hi_.Get()));
|
||||
if (rep_lo_ < rhs.rep_lo_) {
|
||||
rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) - 1);
|
||||
rep_lo_ += kTicksPerSecond;
|
||||
}
|
||||
rep_lo_ -= rhs.rep_lo_;
|
||||
if (rhs.rep_hi_.Get() < 0 ? rep_hi_.Get() < orig_rep_hi
|
||||
: rep_hi_.Get() > orig_rep_hi) {
|
||||
return *this = rhs.rep_hi_.Get() >= 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_.Get() < 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) != (rep_hi_.Get() < 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_.Get() < 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) != (rep_hi_.Get() < 0);
|
||||
return *this = is_neg ? -InfiniteDuration() : InfiniteDuration();
|
||||
}
|
||||
return *this = ScaleDouble<std::divides>(*this, r);
|
||||
}
|
||||
|
||||
Duration& Duration::operator%=(Duration rhs) {
|
||||
IDivDurationImpl(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.
|
||||
//
|
||||
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 = static_cast<decltype(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 = static_cast<decltype(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 = static_cast<char>('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 {
|
||||
absl::string_view abbr;
|
||||
int prec;
|
||||
double pow10;
|
||||
};
|
||||
constexpr DisplayUnit kDisplayNano = {"ns", 2, 1e2};
|
||||
constexpr DisplayUnit kDisplayMicro = {"us", 5, 1e5};
|
||||
constexpr DisplayUnit kDisplayMilli = {"ms", 8, 1e8};
|
||||
constexpr DisplayUnit kDisplaySec = {"s", 11, 1e11};
|
||||
constexpr DisplayUnit kDisplayMin = {"m", -1, 0.0}; // prec ignored
|
||||
constexpr 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, static_cast<size_t>(ep - bp));
|
||||
out->append(unit.abbr.data(), unit.abbr.size());
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = std::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, static_cast<size_t>(ep - bp));
|
||||
if (frac_part != 0) {
|
||||
out->push_back('.');
|
||||
bp = Format64(ep, prec, frac_part);
|
||||
while (ep[-1] == '0') --ep;
|
||||
out->append(bp, static_cast<size_t>(ep - bp));
|
||||
}
|
||||
out->append(unit.abbr.data(), unit.abbr.size());
|
||||
}
|
||||
}
|
||||
|
||||
} // 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.
|
||||
// Unlike Go, we format the zero duration as 0, with no unit.
|
||||
std::string FormatDuration(Duration d) {
|
||||
constexpr Duration kMinDuration = Seconds(kint64min);
|
||||
std::string s;
|
||||
if (d == kMinDuration) {
|
||||
// Avoid needing to negate kint64min by directly returning what the
|
||||
// following code should produce in that case.
|
||||
s = "-2562047788015215h30m8s";
|
||||
return 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 = static_cast<size_t>(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
|
||||
464
TMessagesProj/jni/voip/webrtc/absl/time/duration_benchmark.cc
Normal file
464
TMessagesProj/jni/voip/webrtc/absl/time/duration_benchmark.cc
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
// 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/flags/flag.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
ABSL_FLAG(absl::Duration, absl_duration_flag_for_benchmark,
|
||||
absl::Milliseconds(1),
|
||||
"Flag to use for benchmarking duration flag access speed.");
|
||||
|
||||
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_Modulo(benchmark::State& state) {
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
auto mod = absl::Seconds(i) % absl::Nanoseconds(12345);
|
||||
benchmark::DoNotOptimize(mod);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Modulo);
|
||||
|
||||
void BM_Duration_Modulo_FastPath(benchmark::State& state) {
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
auto mod = absl::Seconds(i) % absl::Milliseconds(1);
|
||||
benchmark::DoNotOptimize(mod);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_Modulo_FastPath);
|
||||
|
||||
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);
|
||||
|
||||
//
|
||||
// Flag access
|
||||
//
|
||||
void BM_Duration_GetFlag(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(
|
||||
absl::GetFlag(FLAGS_absl_duration_flag_for_benchmark));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Duration_GetFlag);
|
||||
|
||||
} // namespace
|
||||
1905
TMessagesProj/jni/voip/webrtc/absl/time/duration_test.cc
Normal file
1905
TMessagesProj/jni/voip/webrtc/absl/time/duration_test.cc
Normal file
File diff suppressed because it is too large
Load diff
147
TMessagesProj/jni/voip/webrtc/absl/time/flag_test.cc
Normal file
147
TMessagesProj/jni/voip/webrtc/absl/time/flag_test.cc
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// 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/flags/flag.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/flags/reflection.h"
|
||||
#include "absl/time/civil_time.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
ABSL_FLAG(absl::CivilSecond, test_flag_civil_second,
|
||||
absl::CivilSecond(2015, 1, 2, 3, 4, 5), "");
|
||||
ABSL_FLAG(absl::CivilMinute, test_flag_civil_minute,
|
||||
absl::CivilMinute(2015, 1, 2, 3, 4), "");
|
||||
ABSL_FLAG(absl::CivilHour, test_flag_civil_hour, absl::CivilHour(2015, 1, 2, 3),
|
||||
"");
|
||||
ABSL_FLAG(absl::CivilDay, test_flag_civil_day, absl::CivilDay(2015, 1, 2), "");
|
||||
ABSL_FLAG(absl::CivilMonth, test_flag_civil_month, absl::CivilMonth(2015, 1),
|
||||
"");
|
||||
ABSL_FLAG(absl::CivilYear, test_flag_civil_year, absl::CivilYear(2015), "");
|
||||
|
||||
ABSL_FLAG(absl::Duration, test_duration_flag, absl::Seconds(5),
|
||||
"For testing support for Duration flags");
|
||||
ABSL_FLAG(absl::Time, test_time_flag, absl::InfinitePast(),
|
||||
"For testing support for Time flags");
|
||||
|
||||
namespace {
|
||||
|
||||
bool SetFlagValue(absl::string_view flag_name, absl::string_view value) {
|
||||
auto* flag = absl::FindCommandLineFlag(flag_name);
|
||||
if (!flag) return false;
|
||||
std::string err;
|
||||
return flag->ParseFrom(value, &err);
|
||||
}
|
||||
|
||||
bool GetFlagValue(absl::string_view flag_name, std::string& value) {
|
||||
auto* flag = absl::FindCommandLineFlag(flag_name);
|
||||
if (!flag) return false;
|
||||
value = flag->CurrentValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(CivilTime, FlagSupport) {
|
||||
// Tests the default setting of the flags.
|
||||
const absl::CivilSecond kDefaultSec(2015, 1, 2, 3, 4, 5);
|
||||
EXPECT_EQ(absl::CivilSecond(kDefaultSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_second));
|
||||
EXPECT_EQ(absl::CivilMinute(kDefaultSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_minute));
|
||||
EXPECT_EQ(absl::CivilHour(kDefaultSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_hour));
|
||||
EXPECT_EQ(absl::CivilDay(kDefaultSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_day));
|
||||
EXPECT_EQ(absl::CivilMonth(kDefaultSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_month));
|
||||
EXPECT_EQ(absl::CivilYear(kDefaultSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_year));
|
||||
|
||||
// Sets flags to a new value.
|
||||
const absl::CivilSecond kNewSec(2016, 6, 7, 8, 9, 10);
|
||||
absl::SetFlag(&FLAGS_test_flag_civil_second, absl::CivilSecond(kNewSec));
|
||||
absl::SetFlag(&FLAGS_test_flag_civil_minute, absl::CivilMinute(kNewSec));
|
||||
absl::SetFlag(&FLAGS_test_flag_civil_hour, absl::CivilHour(kNewSec));
|
||||
absl::SetFlag(&FLAGS_test_flag_civil_day, absl::CivilDay(kNewSec));
|
||||
absl::SetFlag(&FLAGS_test_flag_civil_month, absl::CivilMonth(kNewSec));
|
||||
absl::SetFlag(&FLAGS_test_flag_civil_year, absl::CivilYear(kNewSec));
|
||||
|
||||
EXPECT_EQ(absl::CivilSecond(kNewSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_second));
|
||||
EXPECT_EQ(absl::CivilMinute(kNewSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_minute));
|
||||
EXPECT_EQ(absl::CivilHour(kNewSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_hour));
|
||||
EXPECT_EQ(absl::CivilDay(kNewSec), absl::GetFlag(FLAGS_test_flag_civil_day));
|
||||
EXPECT_EQ(absl::CivilMonth(kNewSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_month));
|
||||
EXPECT_EQ(absl::CivilYear(kNewSec),
|
||||
absl::GetFlag(FLAGS_test_flag_civil_year));
|
||||
}
|
||||
|
||||
TEST(Duration, FlagSupport) {
|
||||
EXPECT_EQ(absl::Seconds(5), absl::GetFlag(FLAGS_test_duration_flag));
|
||||
|
||||
absl::SetFlag(&FLAGS_test_duration_flag, absl::Seconds(10));
|
||||
EXPECT_EQ(absl::Seconds(10), absl::GetFlag(FLAGS_test_duration_flag));
|
||||
|
||||
EXPECT_TRUE(SetFlagValue("test_duration_flag", "20s"));
|
||||
EXPECT_EQ(absl::Seconds(20), absl::GetFlag(FLAGS_test_duration_flag));
|
||||
|
||||
std::string current_flag_value;
|
||||
EXPECT_TRUE(GetFlagValue("test_duration_flag", current_flag_value));
|
||||
EXPECT_EQ("20s", current_flag_value);
|
||||
}
|
||||
|
||||
TEST(Time, FlagSupport) {
|
||||
EXPECT_EQ(absl::InfinitePast(), absl::GetFlag(FLAGS_test_time_flag));
|
||||
|
||||
const absl::Time t = absl::FromCivil(absl::CivilSecond(2016, 1, 2, 3, 4, 5),
|
||||
absl::UTCTimeZone());
|
||||
absl::SetFlag(&FLAGS_test_time_flag, t);
|
||||
EXPECT_EQ(t, absl::GetFlag(FLAGS_test_time_flag));
|
||||
|
||||
// Successful parse
|
||||
EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:06Z"));
|
||||
EXPECT_EQ(t + absl::Seconds(1), absl::GetFlag(FLAGS_test_time_flag));
|
||||
EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:07.0Z"));
|
||||
EXPECT_EQ(t + absl::Seconds(2), absl::GetFlag(FLAGS_test_time_flag));
|
||||
EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:08.000Z"));
|
||||
EXPECT_EQ(t + absl::Seconds(3), absl::GetFlag(FLAGS_test_time_flag));
|
||||
EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:09+00:00"));
|
||||
EXPECT_EQ(t + absl::Seconds(4), absl::GetFlag(FLAGS_test_time_flag));
|
||||
EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:05.123+00:00"));
|
||||
EXPECT_EQ(t + absl::Milliseconds(123), absl::GetFlag(FLAGS_test_time_flag));
|
||||
EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:05.123+08:00"));
|
||||
EXPECT_EQ(t + absl::Milliseconds(123) - absl::Hours(8),
|
||||
absl::GetFlag(FLAGS_test_time_flag));
|
||||
EXPECT_TRUE(SetFlagValue("test_time_flag", "infinite-future"));
|
||||
EXPECT_EQ(absl::InfiniteFuture(), absl::GetFlag(FLAGS_test_time_flag));
|
||||
EXPECT_TRUE(SetFlagValue("test_time_flag", "infinite-past"));
|
||||
EXPECT_EQ(absl::InfinitePast(), absl::GetFlag(FLAGS_test_time_flag));
|
||||
|
||||
EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-01-02T03:04:06"));
|
||||
EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-01-02"));
|
||||
EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-01-02Z"));
|
||||
EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-01-02+00:00"));
|
||||
EXPECT_FALSE(SetFlagValue("test_time_flag", "2016-99-99T03:04:06Z"));
|
||||
|
||||
EXPECT_TRUE(SetFlagValue("test_time_flag", "2016-01-02T03:04:05Z"));
|
||||
std::string current_flag_value;
|
||||
EXPECT_TRUE(GetFlagValue("test_time_flag", current_flag_value));
|
||||
EXPECT_EQ("2016-01-02T03:04:05+00:00", current_flag_value);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
162
TMessagesProj/jni/voip/webrtc/absl/time/format.cc
Normal file
162
TMessagesProj/jni/voip/webrtc/absl/time/format.cc
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// 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 <utility>
|
||||
|
||||
#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-%d%ET%H:%M:%E*S%Ez";
|
||||
ABSL_DLL extern const char RFC3339_sec[] = "%Y-%m-%d%ET%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 =
|
||||
static_cast<uint32_t>(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 = std::move(error);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// Functions required to support absl::Time flags.
|
||||
bool AbslParseFlag(absl::string_view text, absl::Time* t, std::string* error) {
|
||||
return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error);
|
||||
}
|
||||
|
||||
std::string AbslUnparseFlag(absl::Time t) {
|
||||
return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone());
|
||||
}
|
||||
bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) {
|
||||
return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error);
|
||||
}
|
||||
|
||||
std::string UnparseFlag(absl::Time t) {
|
||||
return absl::FormatTime(RFC3339_full, t, absl::UTCTimeZone());
|
||||
}
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
64
TMessagesProj/jni/voip/webrtc/absl/time/format_benchmark.cc
Normal file
64
TMessagesProj/jni/voip/webrtc/absl/time/format_benchmark.cc
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2018 The Abseil Authors.
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include "absl/time/internal/test_util.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
namespace {
|
||||
const char* const kFormats[] = {
|
||||
absl::RFC1123_full, // 0
|
||||
absl::RFC1123_no_wday, // 1
|
||||
absl::RFC3339_full, // 2
|
||||
absl::RFC3339_sec, // 3
|
||||
"%Y-%m-%d%ET%H:%M:%S", // 4
|
||||
"%Y-%m-%d", // 5
|
||||
};
|
||||
const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
|
||||
} // namespace
|
||||
|
||||
void BM_Format_FormatTime(benchmark::State& state) {
|
||||
const std::string fmt = kFormats[state.range(0)];
|
||||
state.SetLabel(fmt);
|
||||
const absl::TimeZone lax =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
const absl::Time t =
|
||||
absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax) +
|
||||
absl::Nanoseconds(1);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::FormatTime(fmt, t, lax).length());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Format_FormatTime)->DenseRange(0, kNumFormats - 1);
|
||||
|
||||
void BM_Format_ParseTime(benchmark::State& state) {
|
||||
const std::string fmt = kFormats[state.range(0)];
|
||||
state.SetLabel(fmt);
|
||||
const absl::TimeZone lax =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
absl::Time t = absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax) +
|
||||
absl::Nanoseconds(1);
|
||||
const std::string when = absl::FormatTime(fmt, t, lax);
|
||||
std::string err;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ParseTime(fmt, when, lax, &t, &err));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Format_ParseTime)->DenseRange(0, kNumFormats - 1);
|
||||
|
||||
} // namespace
|
||||
441
TMessagesProj/jni/voip/webrtc/absl/time/format_test.cc
Normal file
441
TMessagesProj/jni/voip/webrtc/absl/time/format_test.cc
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
// Copyright 2017 The Abseil Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/internal/test_util.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
using testing::HasSubstr;
|
||||
|
||||
namespace {
|
||||
|
||||
// A helper that tests the given format specifier by itself, and with leading
|
||||
// and trailing characters. For example: TestFormatSpecifier(t, "%a", "Thu").
|
||||
void TestFormatSpecifier(absl::Time t, absl::TimeZone tz,
|
||||
const std::string& fmt, const std::string& ans) {
|
||||
EXPECT_EQ(ans, absl::FormatTime(fmt, t, tz));
|
||||
EXPECT_EQ("xxx " + ans, absl::FormatTime("xxx " + fmt, t, tz));
|
||||
EXPECT_EQ(ans + " yyy", absl::FormatTime(fmt + " yyy", t, tz));
|
||||
EXPECT_EQ("xxx " + ans + " yyy",
|
||||
absl::FormatTime("xxx " + fmt + " yyy", t, tz));
|
||||
}
|
||||
|
||||
//
|
||||
// Testing FormatTime()
|
||||
//
|
||||
|
||||
TEST(FormatTime, Basics) {
|
||||
absl::TimeZone tz = absl::UTCTimeZone();
|
||||
absl::Time t = absl::FromTimeT(0);
|
||||
|
||||
// Starts with a couple basic edge cases.
|
||||
EXPECT_EQ("", absl::FormatTime("", t, tz));
|
||||
EXPECT_EQ(" ", absl::FormatTime(" ", t, tz));
|
||||
EXPECT_EQ(" ", absl::FormatTime(" ", t, tz));
|
||||
EXPECT_EQ("xxx", absl::FormatTime("xxx", t, tz));
|
||||
std::string big(128, 'x');
|
||||
EXPECT_EQ(big, absl::FormatTime(big, t, tz));
|
||||
// Cause the 1024-byte buffer to grow.
|
||||
std::string bigger(100000, 'x');
|
||||
EXPECT_EQ(bigger, absl::FormatTime(bigger, t, tz));
|
||||
|
||||
t += absl::Hours(13) + absl::Minutes(4) + absl::Seconds(5);
|
||||
t += absl::Milliseconds(6) + absl::Microseconds(7) + absl::Nanoseconds(8);
|
||||
EXPECT_EQ("1970-01-01", absl::FormatTime("%Y-%m-%d", t, tz));
|
||||
EXPECT_EQ("13:04:05", absl::FormatTime("%H:%M:%S", t, tz));
|
||||
EXPECT_EQ("13:04:05.006", absl::FormatTime("%H:%M:%E3S", t, tz));
|
||||
EXPECT_EQ("13:04:05.006007", absl::FormatTime("%H:%M:%E6S", t, tz));
|
||||
EXPECT_EQ("13:04:05.006007008", absl::FormatTime("%H:%M:%E9S", t, tz));
|
||||
}
|
||||
|
||||
TEST(FormatTime, LocaleSpecific) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
absl::Time t = absl::FromTimeT(0);
|
||||
|
||||
TestFormatSpecifier(t, tz, "%a", "Thu");
|
||||
TestFormatSpecifier(t, tz, "%A", "Thursday");
|
||||
TestFormatSpecifier(t, tz, "%b", "Jan");
|
||||
TestFormatSpecifier(t, tz, "%B", "January");
|
||||
|
||||
// %c should at least produce the numeric year and time-of-day.
|
||||
const std::string s =
|
||||
absl::FormatTime("%c", absl::FromTimeT(0), absl::UTCTimeZone());
|
||||
EXPECT_THAT(s, HasSubstr("1970"));
|
||||
EXPECT_THAT(s, HasSubstr("00:00:00"));
|
||||
|
||||
TestFormatSpecifier(t, tz, "%p", "AM");
|
||||
TestFormatSpecifier(t, tz, "%x", "01/01/70");
|
||||
TestFormatSpecifier(t, tz, "%X", "00:00:00");
|
||||
}
|
||||
|
||||
TEST(FormatTime, ExtendedSeconds) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
|
||||
// No subseconds.
|
||||
absl::Time t = absl::FromTimeT(0) + absl::Seconds(5);
|
||||
EXPECT_EQ("05", absl::FormatTime("%E*S", t, tz));
|
||||
EXPECT_EQ("05.000000000000000", absl::FormatTime("%E15S", t, tz));
|
||||
|
||||
// With subseconds.
|
||||
t += absl::Milliseconds(6) + absl::Microseconds(7) + absl::Nanoseconds(8);
|
||||
EXPECT_EQ("05.006007008", absl::FormatTime("%E*S", t, tz));
|
||||
EXPECT_EQ("05", absl::FormatTime("%E0S", t, tz));
|
||||
EXPECT_EQ("05.006007008000000", absl::FormatTime("%E15S", t, tz));
|
||||
|
||||
// Times before the Unix epoch.
|
||||
t = absl::FromUnixMicros(-1);
|
||||
EXPECT_EQ("1969-12-31 23:59:59.999999",
|
||||
absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
|
||||
|
||||
// Here is a "%E*S" case we got wrong for a while. While the first
|
||||
// instant below is correctly rendered as "...:07.333304", the second
|
||||
// one used to appear as "...:07.33330499999999999".
|
||||
t = absl::FromUnixMicros(1395024427333304);
|
||||
EXPECT_EQ("2014-03-17 02:47:07.333304",
|
||||
absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
|
||||
t += absl::Microseconds(1);
|
||||
EXPECT_EQ("2014-03-17 02:47:07.333305",
|
||||
absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
|
||||
}
|
||||
|
||||
TEST(FormatTime, RFC1123FormatPadsYear) { // locale specific
|
||||
absl::TimeZone tz = absl::UTCTimeZone();
|
||||
|
||||
// A year of 77 should be padded to 0077.
|
||||
absl::Time t = absl::FromCivil(absl::CivilSecond(77, 6, 28, 9, 8, 7), tz);
|
||||
EXPECT_EQ("Mon, 28 Jun 0077 09:08:07 +0000",
|
||||
absl::FormatTime(absl::RFC1123_full, t, tz));
|
||||
EXPECT_EQ("28 Jun 0077 09:08:07 +0000",
|
||||
absl::FormatTime(absl::RFC1123_no_wday, t, tz));
|
||||
}
|
||||
|
||||
TEST(FormatTime, InfiniteTime) {
|
||||
absl::TimeZone tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
|
||||
// The format and timezone are ignored.
|
||||
EXPECT_EQ("infinite-future",
|
||||
absl::FormatTime("%H:%M blah", absl::InfiniteFuture(), tz));
|
||||
EXPECT_EQ("infinite-past",
|
||||
absl::FormatTime("%H:%M blah", absl::InfinitePast(), tz));
|
||||
}
|
||||
|
||||
//
|
||||
// Testing ParseTime()
|
||||
//
|
||||
|
||||
TEST(ParseTime, Basics) {
|
||||
absl::Time t = absl::FromTimeT(1234567890);
|
||||
std::string err;
|
||||
|
||||
// Simple edge cases.
|
||||
EXPECT_TRUE(absl::ParseTime("", "", &t, &err)) << err;
|
||||
EXPECT_EQ(absl::UnixEpoch(), t); // everything defaulted
|
||||
EXPECT_TRUE(absl::ParseTime(" ", " ", &t, &err)) << err;
|
||||
EXPECT_TRUE(absl::ParseTime(" ", " ", &t, &err)) << err;
|
||||
EXPECT_TRUE(absl::ParseTime("x", "x", &t, &err)) << err;
|
||||
EXPECT_TRUE(absl::ParseTime("xxx", "xxx", &t, &err)) << err;
|
||||
|
||||
EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
|
||||
"2013-06-28 19:08:09 -0800", &t, &err))
|
||||
<< err;
|
||||
const auto ci = absl::FixedTimeZone(-8 * 60 * 60).At(t);
|
||||
EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
|
||||
EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
|
||||
}
|
||||
|
||||
TEST(ParseTime, NullErrorString) {
|
||||
absl::Time t;
|
||||
EXPECT_FALSE(absl::ParseTime("%Q", "invalid format", &t, nullptr));
|
||||
EXPECT_FALSE(absl::ParseTime("%H", "12 trailing data", &t, nullptr));
|
||||
EXPECT_FALSE(
|
||||
absl::ParseTime("%H out of range", "42 out of range", &t, nullptr));
|
||||
}
|
||||
|
||||
TEST(ParseTime, WithTimeZone) {
|
||||
const absl::TimeZone tz =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
absl::Time t;
|
||||
std::string e;
|
||||
|
||||
// We can parse a string without a UTC offset if we supply a timezone.
|
||||
EXPECT_TRUE(
|
||||
absl::ParseTime("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &t, &e))
|
||||
<< e;
|
||||
auto ci = tz.At(t);
|
||||
EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
|
||||
EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
|
||||
|
||||
// But the timezone is ignored when a UTC offset is present.
|
||||
EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
|
||||
"2013-06-28 19:08:09 +0800", tz, &t, &e))
|
||||
<< e;
|
||||
ci = absl::FixedTimeZone(8 * 60 * 60).At(t);
|
||||
EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
|
||||
EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
|
||||
}
|
||||
|
||||
TEST(ParseTime, ErrorCases) {
|
||||
absl::Time t = absl::FromTimeT(0);
|
||||
std::string err;
|
||||
|
||||
EXPECT_FALSE(absl::ParseTime("%S", "123", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// Can't parse an illegal format specifier.
|
||||
err.clear();
|
||||
EXPECT_FALSE(absl::ParseTime("%Q", "x", &t, &err)) << err;
|
||||
// Exact contents of "err" are platform-dependent because of
|
||||
// differences in the strptime implementation between macOS and Linux.
|
||||
EXPECT_FALSE(err.empty());
|
||||
|
||||
// Fails because of trailing, unparsed data "blah".
|
||||
EXPECT_FALSE(absl::ParseTime("%m-%d", "2-3 blah", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// Feb 31 requires normalization.
|
||||
EXPECT_FALSE(absl::ParseTime("%m-%d", "2-31", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Out-of-range"));
|
||||
|
||||
// Check that we cannot have spaces in UTC offsets.
|
||||
EXPECT_TRUE(absl::ParseTime("%z", "-0203", &t, &err)) << err;
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "- 2 3", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_TRUE(absl::ParseTime("%Ez", "-02:03", &t, &err)) << err;
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "- 2: 3", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
|
||||
// Check that we reject other malformed UTC offsets.
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "+-08:00", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-+08:00", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
|
||||
// Check that we do not accept "-0" in fields that allow zero.
|
||||
EXPECT_FALSE(absl::ParseTime("%Y", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%E4Y", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%H", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%M", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%S", "-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "+-000", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "+-0:00", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "-00-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-00:-0", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
}
|
||||
|
||||
TEST(ParseTime, ExtendedSeconds) {
|
||||
std::string err;
|
||||
absl::Time t;
|
||||
|
||||
// Here is a "%E*S" case we got wrong for a while. The fractional
|
||||
// part of the first instant is less than 2^31 and was correctly
|
||||
// parsed, while the second (and any subsecond field >=2^31) failed.
|
||||
t = absl::UnixEpoch();
|
||||
EXPECT_TRUE(absl::ParseTime("%E*S", "0.2147483647", &t, &err)) << err;
|
||||
EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
|
||||
absl::Nanoseconds(1) / 2,
|
||||
t);
|
||||
t = absl::UnixEpoch();
|
||||
EXPECT_TRUE(absl::ParseTime("%E*S", "0.2147483648", &t, &err)) << err;
|
||||
EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
|
||||
absl::Nanoseconds(3) / 4,
|
||||
t);
|
||||
|
||||
// We should also be able to specify long strings of digits far
|
||||
// beyond the current resolution and have them convert the same way.
|
||||
t = absl::UnixEpoch();
|
||||
EXPECT_TRUE(absl::ParseTime(
|
||||
"%E*S", "0.214748364801234567890123456789012345678901234567890123456789",
|
||||
&t, &err))
|
||||
<< err;
|
||||
EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
|
||||
absl::Nanoseconds(3) / 4,
|
||||
t);
|
||||
}
|
||||
|
||||
TEST(ParseTime, ExtendedOffsetErrors) {
|
||||
std::string err;
|
||||
absl::Time t;
|
||||
|
||||
// %z against +-HHMM.
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "-123", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// %z against +-HH.
|
||||
EXPECT_FALSE(absl::ParseTime("%z", "-1", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
|
||||
// %Ez against +-HH:MM.
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-12:3", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// %Ez against +-HHMM.
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-123", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
|
||||
|
||||
// %Ez against +-HH.
|
||||
EXPECT_FALSE(absl::ParseTime("%Ez", "-1", &t, &err)) << err;
|
||||
EXPECT_THAT(err, HasSubstr("Failed to parse"));
|
||||
}
|
||||
|
||||
TEST(ParseTime, InfiniteTime) {
|
||||
absl::Time t;
|
||||
std::string err;
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-future", &t, &err));
|
||||
EXPECT_EQ(absl::InfiniteFuture(), t);
|
||||
|
||||
// Surrounding whitespace.
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-future", &t, &err));
|
||||
EXPECT_EQ(absl::InfiniteFuture(), t);
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-future ", &t, &err));
|
||||
EXPECT_EQ(absl::InfiniteFuture(), t);
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-future ", &t, &err));
|
||||
EXPECT_EQ(absl::InfiniteFuture(), t);
|
||||
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-past", &t, &err));
|
||||
EXPECT_EQ(absl::InfinitePast(), t);
|
||||
|
||||
// Surrounding whitespace.
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-past", &t, &err));
|
||||
EXPECT_EQ(absl::InfinitePast(), t);
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-past ", &t, &err));
|
||||
EXPECT_EQ(absl::InfinitePast(), t);
|
||||
EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-past ", &t, &err));
|
||||
EXPECT_EQ(absl::InfinitePast(), t);
|
||||
|
||||
// "infinite-future" as literal string
|
||||
absl::TimeZone tz = absl::UTCTimeZone();
|
||||
EXPECT_TRUE(absl::ParseTime("infinite-future %H:%M", "infinite-future 03:04",
|
||||
&t, &err));
|
||||
EXPECT_NE(absl::InfiniteFuture(), t);
|
||||
EXPECT_EQ(3, tz.At(t).cs.hour());
|
||||
EXPECT_EQ(4, tz.At(t).cs.minute());
|
||||
|
||||
// "infinite-past" as literal string
|
||||
EXPECT_TRUE(
|
||||
absl::ParseTime("infinite-past %H:%M", "infinite-past 03:04", &t, &err));
|
||||
EXPECT_NE(absl::InfinitePast(), t);
|
||||
EXPECT_EQ(3, tz.At(t).cs.hour());
|
||||
EXPECT_EQ(4, tz.At(t).cs.minute());
|
||||
|
||||
// The input doesn't match the format.
|
||||
EXPECT_FALSE(absl::ParseTime("infinite-future %H:%M", "03:04", &t, &err));
|
||||
EXPECT_FALSE(absl::ParseTime("infinite-past %H:%M", "03:04", &t, &err));
|
||||
}
|
||||
|
||||
TEST(ParseTime, FailsOnUnrepresentableTime) {
|
||||
const absl::TimeZone utc = absl::UTCTimeZone();
|
||||
absl::Time t;
|
||||
EXPECT_FALSE(
|
||||
absl::ParseTime("%Y-%m-%d", "-292277022657-01-27", utc, &t, nullptr));
|
||||
EXPECT_TRUE(
|
||||
absl::ParseTime("%Y-%m-%d", "-292277022657-01-28", utc, &t, nullptr));
|
||||
EXPECT_TRUE(
|
||||
absl::ParseTime("%Y-%m-%d", "292277026596-12-04", utc, &t, nullptr));
|
||||
EXPECT_FALSE(
|
||||
absl::ParseTime("%Y-%m-%d", "292277026596-12-05", utc, &t, nullptr));
|
||||
}
|
||||
|
||||
//
|
||||
// Roundtrip test for FormatTime()/ParseTime().
|
||||
//
|
||||
|
||||
TEST(FormatParse, RoundTrip) {
|
||||
const absl::TimeZone lax =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
const absl::Time in =
|
||||
absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax);
|
||||
const absl::Duration subseconds = absl::Nanoseconds(654321);
|
||||
std::string err;
|
||||
|
||||
// RFC3339, which renders subseconds.
|
||||
{
|
||||
absl::Time out;
|
||||
const std::string s =
|
||||
absl::FormatTime(absl::RFC3339_full, in + subseconds, lax);
|
||||
EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
|
||||
<< s << ": " << err;
|
||||
EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez
|
||||
}
|
||||
|
||||
// RFC1123, which only does whole seconds.
|
||||
{
|
||||
absl::Time out;
|
||||
const std::string s = absl::FormatTime(absl::RFC1123_full, in, lax);
|
||||
EXPECT_TRUE(absl::ParseTime(absl::RFC1123_full, s, &out, &err))
|
||||
<< s << ": " << err;
|
||||
EXPECT_EQ(in, out); // RFC1123_full includes %z
|
||||
}
|
||||
|
||||
// `absl::FormatTime()` falls back to strftime() for "%c", which appears to
|
||||
// work. On Windows, `absl::ParseTime()` falls back to std::get_time() which
|
||||
// appears to fail on "%c" (or at least on the "%c" text produced by
|
||||
// `strftime()`). This makes it fail the round-trip test.
|
||||
//
|
||||
// Under the emscripten compiler `absl::ParseTime() falls back to
|
||||
// `strptime()`, but that ends up using a different definition for "%c"
|
||||
// compared to `strftime()`, also causing the round-trip test to fail
|
||||
// (see https://github.com/kripken/emscripten/pull/7491).
|
||||
#if !defined(_MSC_VER) && !defined(__EMSCRIPTEN__)
|
||||
// Even though we don't know what %c will produce, it should roundtrip,
|
||||
// but only in the 0-offset timezone.
|
||||
{
|
||||
absl::Time out;
|
||||
const std::string s = absl::FormatTime("%c", in, absl::UTCTimeZone());
|
||||
EXPECT_TRUE(absl::ParseTime("%c", s, &out, &err)) << s << ": " << err;
|
||||
EXPECT_EQ(in, out);
|
||||
}
|
||||
#endif // !_MSC_VER && !__EMSCRIPTEN__
|
||||
}
|
||||
|
||||
TEST(FormatParse, RoundTripDistantFuture) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
const absl::Time in =
|
||||
absl::FromUnixSeconds(std::numeric_limits<int64_t>::max());
|
||||
std::string err;
|
||||
|
||||
absl::Time out;
|
||||
const std::string s = absl::FormatTime(absl::RFC3339_full, in, tz);
|
||||
EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
|
||||
<< s << ": " << err;
|
||||
EXPECT_EQ(in, out);
|
||||
}
|
||||
|
||||
TEST(FormatParse, RoundTripDistantPast) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
const absl::Time in =
|
||||
absl::FromUnixSeconds(std::numeric_limits<int64_t>::min());
|
||||
std::string err;
|
||||
|
||||
absl::Time out;
|
||||
const std::string s = absl::FormatTime(absl::RFC3339_full, in, tz);
|
||||
EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
|
||||
<< s << ": " << err;
|
||||
EXPECT_EQ(in, out);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
@ -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_
|
||||
|
|
@ -0,0 +1,632 @@
|
|||
// 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 {
|
||||
const int yi = static_cast<int>((y + (m > 2)) % 400);
|
||||
return yi < 0 ? yi + 400 : yi;
|
||||
}
|
||||
CONSTEXPR_F int days_per_century(int yi) noexcept {
|
||||
return 36524 + (yi == 0 || yi > 300);
|
||||
}
|
||||
CONSTEXPR_F int days_per_4years(int yi) noexcept {
|
||||
return 1460 + (yi == 0 || yi > 300 || (yi - 1) % 100 < 96);
|
||||
}
|
||||
CONSTEXPR_F int days_per_year(year_t y, month_t m) noexcept {
|
||||
return is_leap_year(y + (m > 2)) ? 366 : 365;
|
||||
}
|
||||
CONSTEXPR_F int days_per_month(year_t y, month_t m) noexcept {
|
||||
CONSTEXPR_D int k_days_per_month[1 + 12] = {
|
||||
-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 // non leap year
|
||||
};
|
||||
return k_days_per_month[m] + (m == 2 && is_leap_year(y));
|
||||
}
|
||||
|
||||
CONSTEXPR_F fields n_day(year_t y, month_t m, diff_t d, diff_t cd, hour_t hh,
|
||||
minute_t mm, second_t ss) noexcept {
|
||||
year_t ey = y % 400;
|
||||
const year_t oey = ey;
|
||||
ey += (cd / 146097) * 400;
|
||||
cd %= 146097;
|
||||
if (cd < 0) {
|
||||
ey -= 400;
|
||||
cd += 146097;
|
||||
}
|
||||
ey += (d / 146097) * 400;
|
||||
d = d % 146097 + cd;
|
||||
if (d > 0) {
|
||||
if (d > 146097) {
|
||||
ey += 400;
|
||||
d -= 146097;
|
||||
}
|
||||
} else {
|
||||
if (d > -365) {
|
||||
// We often hit the previous year when stepping a civil time backwards,
|
||||
// so special case it to avoid counting up by 100/4/1-year chunks.
|
||||
ey -= 1;
|
||||
d += days_per_year(ey, m);
|
||||
} else {
|
||||
ey -= 400;
|
||||
d += 146097;
|
||||
}
|
||||
}
|
||||
if (d > 365) {
|
||||
int yi = year_index(ey, m); // Index into Gregorian 400 year cycle.
|
||||
for (;;) {
|
||||
int n = days_per_century(yi);
|
||||
if (d <= n) break;
|
||||
d -= n;
|
||||
ey += 100;
|
||||
yi += 100;
|
||||
if (yi >= 400) yi -= 400;
|
||||
}
|
||||
for (;;) {
|
||||
int n = days_per_4years(yi);
|
||||
if (d <= n) break;
|
||||
d -= n;
|
||||
ey += 4;
|
||||
yi += 4;
|
||||
if (yi >= 400) yi -= 400;
|
||||
}
|
||||
for (;;) {
|
||||
int n = days_per_year(ey, m);
|
||||
if (d <= n) break;
|
||||
d -= n;
|
||||
++ey;
|
||||
}
|
||||
}
|
||||
if (d > 28) {
|
||||
for (;;) {
|
||||
int n = days_per_month(ey, m);
|
||||
if (d <= n) break;
|
||||
d -= n;
|
||||
if (++m > 12) {
|
||||
++ey;
|
||||
m = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fields(y + (ey - oey), m, static_cast<day_t>(d), hh, mm, ss);
|
||||
}
|
||||
CONSTEXPR_F fields n_mon(year_t y, diff_t m, diff_t d, diff_t cd, hour_t hh,
|
||||
minute_t mm, second_t ss) noexcept {
|
||||
if (m != 12) {
|
||||
y += m / 12;
|
||||
m %= 12;
|
||||
if (m <= 0) {
|
||||
y -= 1;
|
||||
m += 12;
|
||||
}
|
||||
}
|
||||
return n_day(y, static_cast<month_t>(m), d, cd, hh, mm, ss);
|
||||
}
|
||||
CONSTEXPR_F fields n_hour(year_t y, diff_t m, diff_t d, diff_t cd, diff_t hh,
|
||||
minute_t mm, second_t ss) noexcept {
|
||||
cd += hh / 24;
|
||||
hh %= 24;
|
||||
if (hh < 0) {
|
||||
cd -= 1;
|
||||
hh += 24;
|
||||
}
|
||||
return n_mon(y, m, d, cd, static_cast<hour_t>(hh), mm, ss);
|
||||
}
|
||||
CONSTEXPR_F fields n_min(year_t y, diff_t m, diff_t d, diff_t hh, diff_t ch,
|
||||
diff_t mm, second_t ss) noexcept {
|
||||
ch += mm / 60;
|
||||
mm %= 60;
|
||||
if (mm < 0) {
|
||||
ch -= 1;
|
||||
mm += 60;
|
||||
}
|
||||
return n_hour(y, m, d, hh / 24 + ch / 24, hh % 24 + ch % 24,
|
||||
static_cast<minute_t>(mm), ss);
|
||||
}
|
||||
CONSTEXPR_F fields n_sec(year_t y, diff_t m, diff_t d, diff_t hh, diff_t mm,
|
||||
diff_t ss) noexcept {
|
||||
// Optimization for when (non-constexpr) fields are already normalized.
|
||||
if (0 <= ss && ss < 60) {
|
||||
const second_t nss = static_cast<second_t>(ss);
|
||||
if (0 <= mm && mm < 60) {
|
||||
const minute_t nmm = static_cast<minute_t>(mm);
|
||||
if (0 <= hh && hh < 24) {
|
||||
const hour_t nhh = static_cast<hour_t>(hh);
|
||||
if (1 <= d && d <= 28 && 1 <= m && m <= 12) {
|
||||
const day_t nd = static_cast<day_t>(d);
|
||||
const month_t nm = static_cast<month_t>(m);
|
||||
return fields(y, nm, nd, nhh, nmm, nss);
|
||||
}
|
||||
return n_mon(y, m, d, 0, nhh, nmm, nss);
|
||||
}
|
||||
return n_hour(y, m, d, hh / 24, hh % 24, nmm, nss);
|
||||
}
|
||||
return n_min(y, m, d, hh, mm / 60, mm % 60, nss);
|
||||
}
|
||||
diff_t cm = ss / 60;
|
||||
ss %= 60;
|
||||
if (ss < 0) {
|
||||
cm -= 1;
|
||||
ss += 60;
|
||||
}
|
||||
return n_min(y, m, d, hh, mm / 60 + cm / 60, mm % 60 + cm % 60,
|
||||
static_cast<second_t>(ss));
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Increments the indicated (normalized) field by "n".
|
||||
CONSTEXPR_F fields step(second_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_sec(f.y, f.m, f.d, f.hh, f.mm + n / 60, f.ss + n % 60);
|
||||
}
|
||||
CONSTEXPR_F fields step(minute_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_min(f.y, f.m, f.d, f.hh + n / 60, 0, f.mm + n % 60, f.ss);
|
||||
}
|
||||
CONSTEXPR_F fields step(hour_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_hour(f.y, f.m, f.d + n / 24, 0, f.hh + n % 24, f.mm, f.ss);
|
||||
}
|
||||
CONSTEXPR_F fields step(day_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_day(f.y, f.m, f.d, n, f.hh, f.mm, f.ss);
|
||||
}
|
||||
CONSTEXPR_F fields step(month_tag, fields f, diff_t n) noexcept {
|
||||
return impl::n_mon(f.y + n / 12, f.m + n % 12, f.d, 0, f.hh, f.mm, f.ss);
|
||||
}
|
||||
CONSTEXPR_F fields step(year_tag, fields f, diff_t n) noexcept {
|
||||
return fields(f.y + n, f.m, f.d, f.hh, f.mm, f.ss);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace impl {
|
||||
|
||||
// Returns (v * f + a) but avoiding intermediate overflow when possible.
|
||||
CONSTEXPR_F diff_t scale_add(diff_t v, diff_t f, diff_t a) noexcept {
|
||||
return (v < 0) ? ((v + 1) * f + a) - f : ((v - 1) * f + a) + f;
|
||||
}
|
||||
|
||||
// Map a (normalized) Y/M/D to the number of days before/after 1970-01-01.
|
||||
// Probably overflows for years outside [-292277022656:292277026595].
|
||||
CONSTEXPR_F diff_t ymd_ord(year_t y, month_t m, day_t d) noexcept {
|
||||
const diff_t eyear = (m <= 2) ? y - 1 : y;
|
||||
const diff_t era = (eyear >= 0 ? eyear : eyear - 399) / 400;
|
||||
const diff_t yoe = eyear - era * 400;
|
||||
const diff_t doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
|
||||
const diff_t doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
||||
return era * 146097 + doe - 719468;
|
||||
}
|
||||
|
||||
// Returns the difference in days between two normalized Y-M-D tuples.
|
||||
// ymd_ord() will encounter integer overflow given extreme year values,
|
||||
// yet the difference between two such extreme values may actually be
|
||||
// small, so we take a little care to avoid overflow when possible by
|
||||
// exploiting the 146097-day cycle.
|
||||
CONSTEXPR_F diff_t day_difference(year_t y1, month_t m1, day_t d1, year_t y2,
|
||||
month_t m2, day_t d2) noexcept {
|
||||
const diff_t a_c4_off = y1 % 400;
|
||||
const diff_t b_c4_off = y2 % 400;
|
||||
diff_t c4_diff = (y1 - a_c4_off) - (y2 - b_c4_off);
|
||||
diff_t delta = ymd_ord(a_c4_off, m1, d1) - ymd_ord(b_c4_off, m2, d2);
|
||||
if (c4_diff > 0 && delta < 0) {
|
||||
delta += 2 * 146097;
|
||||
c4_diff -= 2 * 400;
|
||||
} else if (c4_diff < 0 && delta > 0) {
|
||||
delta -= 2 * 146097;
|
||||
c4_diff += 2 * 400;
|
||||
}
|
||||
return (c4_diff / 400 * 146097) + delta;
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
// Returns the difference between fields structs using the indicated unit.
|
||||
CONSTEXPR_F diff_t difference(year_tag, fields f1, fields f2) noexcept {
|
||||
return f1.y - f2.y;
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(month_tag, fields f1, fields f2) noexcept {
|
||||
return impl::scale_add(difference(year_tag{}, f1, f2), 12, (f1.m - f2.m));
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(day_tag, fields f1, fields f2) noexcept {
|
||||
return impl::day_difference(f1.y, f1.m, f1.d, f2.y, f2.m, f2.d);
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(hour_tag, fields f1, fields f2) noexcept {
|
||||
return impl::scale_add(difference(day_tag{}, f1, f2), 24, (f1.hh - f2.hh));
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(minute_tag, fields f1, fields f2) noexcept {
|
||||
return impl::scale_add(difference(hour_tag{}, f1, f2), 60, (f1.mm - f2.mm));
|
||||
}
|
||||
CONSTEXPR_F diff_t difference(second_tag, fields f1, fields f2) noexcept {
|
||||
return impl::scale_add(difference(minute_tag{}, f1, f2), 60, f1.ss - f2.ss);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Aligns the (normalized) fields struct to the indicated field.
|
||||
CONSTEXPR_F fields align(second_tag, fields f) noexcept { return f; }
|
||||
CONSTEXPR_F fields align(minute_tag, fields f) noexcept {
|
||||
return fields{f.y, f.m, f.d, f.hh, f.mm, 0};
|
||||
}
|
||||
CONSTEXPR_F fields align(hour_tag, fields f) noexcept {
|
||||
return fields{f.y, f.m, f.d, f.hh, 0, 0};
|
||||
}
|
||||
CONSTEXPR_F fields align(day_tag, fields f) noexcept {
|
||||
return fields{f.y, f.m, f.d, 0, 0, 0};
|
||||
}
|
||||
CONSTEXPR_F fields align(month_tag, fields f) noexcept {
|
||||
return fields{f.y, f.m, 1, 0, 0, 0};
|
||||
}
|
||||
CONSTEXPR_F fields align(year_tag, fields f) noexcept {
|
||||
return fields{f.y, 1, 1, 0, 0, 0};
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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 auto(max)() -> civil_time {
|
||||
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 auto(min)() -> civil_time {
|
||||
const auto min_year = (std::numeric_limits<std::int_least64_t>::min)();
|
||||
return civil_time(min_year, 1, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Field accessors. Note: All but year() return an int.
|
||||
CONSTEXPR_M year_t year() const noexcept { return f_.y; }
|
||||
CONSTEXPR_M int month() const noexcept { return f_.m; }
|
||||
CONSTEXPR_M int day() const noexcept { return f_.d; }
|
||||
CONSTEXPR_M int hour() const noexcept { return f_.hh; }
|
||||
CONSTEXPR_M int minute() const noexcept { return f_.mm; }
|
||||
CONSTEXPR_M int second() const noexcept { return f_.ss; }
|
||||
|
||||
// Assigning arithmetic.
|
||||
CONSTEXPR_M civil_time& operator+=(diff_t n) noexcept {
|
||||
return *this = *this + n;
|
||||
}
|
||||
CONSTEXPR_M civil_time& operator-=(diff_t n) noexcept {
|
||||
return *this = *this - n;
|
||||
}
|
||||
CONSTEXPR_M civil_time& operator++() noexcept { return *this += 1; }
|
||||
CONSTEXPR_M civil_time operator++(int) noexcept {
|
||||
const civil_time a = *this;
|
||||
++*this;
|
||||
return a;
|
||||
}
|
||||
CONSTEXPR_M civil_time& operator--() noexcept { return *this -= 1; }
|
||||
CONSTEXPR_M civil_time operator--(int) noexcept {
|
||||
const civil_time a = *this;
|
||||
--*this;
|
||||
return a;
|
||||
}
|
||||
|
||||
// Binary arithmetic operators.
|
||||
friend CONSTEXPR_F civil_time operator+(civil_time a, diff_t n) noexcept {
|
||||
return civil_time(step(T{}, a.f_, n));
|
||||
}
|
||||
friend CONSTEXPR_F civil_time operator+(diff_t n, civil_time a) noexcept {
|
||||
return a + n;
|
||||
}
|
||||
friend CONSTEXPR_F civil_time operator-(civil_time a, diff_t n) noexcept {
|
||||
return n != (std::numeric_limits<diff_t>::min)()
|
||||
? civil_time(step(T{}, a.f_, -n))
|
||||
: civil_time(step(T{}, step(T{}, a.f_, -(n + 1)), 1));
|
||||
}
|
||||
friend CONSTEXPR_F diff_t operator-(civil_time lhs, civil_time rhs) noexcept {
|
||||
return difference(T{}, lhs.f_, rhs.f_);
|
||||
}
|
||||
|
||||
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_
|
||||
|
|
@ -0,0 +1,460 @@
|
|||
// 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 <limits>
|
||||
#include <ratio> // NOLINT: We use std::ratio in this header
|
||||
#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>
|
||||
std::pair<time_point<seconds>, D> split_seconds(const time_point<D>& tp);
|
||||
std::pair<time_point<seconds>, seconds> split_seconds(
|
||||
const time_point<seconds>& tp);
|
||||
} // 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);
|
||||
template <typename Rep, std::intmax_t Denom>
|
||||
bool join_seconds(
|
||||
const time_point<seconds>& sec, const femtoseconds& fs,
|
||||
time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp);
|
||||
template <typename Rep, std::intmax_t Num>
|
||||
bool join_seconds(
|
||||
const time_point<seconds>& sec, const femtoseconds& fs,
|
||||
time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp);
|
||||
template <typename Rep>
|
||||
bool join_seconds(
|
||||
const time_point<seconds>& sec, const femtoseconds& fs,
|
||||
time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp);
|
||||
bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
|
||||
time_point<seconds>* tpp);
|
||||
} // 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)
|
||||
// - %ET - The RFC3339 "date-time" separator "T"
|
||||
//
|
||||
// Note that %E0S behaves like %S, and %E0f produces no characters. In
|
||||
// contrast %E*f always produces at least one digit, which may be '0'.
|
||||
//
|
||||
// Note that %Y produces as many characters as it takes to fully render the
|
||||
// year. A year outside of [-999:9999] when formatted with %E4Y will produce
|
||||
// more than four characters, just like %Y.
|
||||
//
|
||||
// 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, which (along with %z) includes
|
||||
// 'z' and 'Z' as synonyms for +00:00. %ET accepts either 'T' or 't'.
|
||||
//
|
||||
// %Y consumes as many numeric characters as it can, so the matching data
|
||||
// should always be terminated with a non-numeric. %E4Y always consumes
|
||||
// exactly four characters, including any sign.
|
||||
//
|
||||
// Unspecified fields are taken from the default date and time of ...
|
||||
//
|
||||
// "1970-01-01 00:00:00.0 +0000"
|
||||
//
|
||||
// For example, parsing a string of "15:45" (%H:%M) will return a time_point
|
||||
// that represents "1970-01-01 15:45:00.0 +0000".
|
||||
//
|
||||
// Note that parse() returns time instants, so it makes most sense to parse
|
||||
// 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;
|
||||
return detail::parse(fmt, input, tz, &sec, &fs) &&
|
||||
detail::join_seconds(sec, fs, tpp);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Split a time_point<D> into a time_point<seconds> and a D subseconds.
|
||||
// Undefined behavior if time_point<seconds> is not of sufficient range.
|
||||
// Note that this means it is UB to call cctz::time_zone::lookup(tp) or
|
||||
// cctz::format(fmt, tp, tz) with a time_point that is outside the range
|
||||
// of a 64-bit std::time_t.
|
||||
template <typename D>
|
||||
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()};
|
||||
}
|
||||
|
||||
// Join a time_point<seconds> and femto subseconds into a time_point<D>.
|
||||
// Floors to the resolution of time_point<D>. Returns false if time_point<D>
|
||||
// is not of sufficient range.
|
||||
template <typename Rep, std::intmax_t Denom>
|
||||
bool join_seconds(
|
||||
const time_point<seconds>& sec, const femtoseconds& fs,
|
||||
time_point<std::chrono::duration<Rep, std::ratio<1, Denom>>>* tpp) {
|
||||
using D = std::chrono::duration<Rep, std::ratio<1, Denom>>;
|
||||
// TODO(#199): Return false if result unrepresentable as a time_point<D>.
|
||||
*tpp = std::chrono::time_point_cast<D>(sec);
|
||||
*tpp += std::chrono::duration_cast<D>(fs);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Rep, std::intmax_t Num>
|
||||
bool join_seconds(
|
||||
const time_point<seconds>& sec, const femtoseconds&,
|
||||
time_point<std::chrono::duration<Rep, std::ratio<Num, 1>>>* tpp) {
|
||||
using D = std::chrono::duration<Rep, std::ratio<Num, 1>>;
|
||||
auto count = sec.time_since_epoch().count();
|
||||
if (count >= 0 || count % Num == 0) {
|
||||
count /= Num;
|
||||
} else {
|
||||
count /= Num;
|
||||
count -= 1;
|
||||
}
|
||||
if (count > (std::numeric_limits<Rep>::max)()) return false;
|
||||
if (count < (std::numeric_limits<Rep>::min)()) return false;
|
||||
*tpp = time_point<D>() + D{static_cast<Rep>(count)};
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Rep>
|
||||
bool join_seconds(
|
||||
const time_point<seconds>& sec, const femtoseconds&,
|
||||
time_point<std::chrono::duration<Rep, std::ratio<1, 1>>>* tpp) {
|
||||
using D = std::chrono::duration<Rep, std::ratio<1, 1>>;
|
||||
auto count = sec.time_since_epoch().count();
|
||||
if (count > (std::numeric_limits<Rep>::max)()) return false;
|
||||
if (count < (std::numeric_limits<Rep>::min)()) return false;
|
||||
*tpp = time_point<D>() + D{static_cast<Rep>(count)};
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool join_seconds(const time_point<seconds>& sec, const femtoseconds&,
|
||||
time_point<seconds>* tpp) {
|
||||
*tpp = sec;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_CCTZ_TIME_ZONE_H_
|
||||
|
|
@ -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_
|
||||
|
|
@ -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.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "benchmark/benchmark.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/src/time_zone_impl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
namespace cctz = absl::time_internal::cctz;
|
||||
|
||||
void BM_Difference_Days(benchmark::State& state) {
|
||||
const cctz::civil_day c(2014, 8, 22);
|
||||
const cctz::civil_day epoch(1970, 1, 1);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(c - epoch);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Difference_Days);
|
||||
|
||||
void BM_Step_Days(benchmark::State& state) {
|
||||
const cctz::civil_day kStart(2014, 8, 22);
|
||||
cctz::civil_day c = kStart;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(++c);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Step_Days);
|
||||
|
||||
void BM_GetWeekday(benchmark::State& state) {
|
||||
const cctz::civil_day c(2014, 8, 22);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(cctz::get_weekday(c));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_GetWeekday);
|
||||
|
||||
void BM_NextWeekday(benchmark::State& state) {
|
||||
const cctz::civil_day kStart(2014, 8, 22);
|
||||
const cctz::civil_day kDays[7] = {
|
||||
kStart + 0, kStart + 1, kStart + 2, kStart + 3,
|
||||
kStart + 4, kStart + 5, kStart + 6,
|
||||
};
|
||||
const cctz::weekday kWeekdays[7] = {
|
||||
cctz::weekday::monday, cctz::weekday::tuesday, cctz::weekday::wednesday,
|
||||
cctz::weekday::thursday, cctz::weekday::friday, cctz::weekday::saturday,
|
||||
cctz::weekday::sunday,
|
||||
};
|
||||
while (state.KeepRunningBatch(7 * 7)) {
|
||||
for (const auto from : kDays) {
|
||||
for (const auto to : kWeekdays) {
|
||||
benchmark::DoNotOptimize(cctz::next_weekday(from, to));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_NextWeekday);
|
||||
|
||||
void BM_PrevWeekday(benchmark::State& state) {
|
||||
const cctz::civil_day kStart(2014, 8, 22);
|
||||
const cctz::civil_day kDays[7] = {
|
||||
kStart + 0, kStart + 1, kStart + 2, kStart + 3,
|
||||
kStart + 4, kStart + 5, kStart + 6,
|
||||
};
|
||||
const cctz::weekday kWeekdays[7] = {
|
||||
cctz::weekday::monday, cctz::weekday::tuesday, cctz::weekday::wednesday,
|
||||
cctz::weekday::thursday, cctz::weekday::friday, cctz::weekday::saturday,
|
||||
cctz::weekday::sunday,
|
||||
};
|
||||
while (state.KeepRunningBatch(7 * 7)) {
|
||||
for (const auto from : kDays) {
|
||||
for (const auto to : kWeekdays) {
|
||||
benchmark::DoNotOptimize(cctz::prev_weekday(from, to));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_PrevWeekday);
|
||||
|
||||
const char RFC3339_full[] = "%Y-%m-%d%ET%H:%M:%E*S%Ez";
|
||||
const char RFC3339_sec[] = "%Y-%m-%d%ET%H:%M:%S%Ez";
|
||||
|
||||
const char RFC1123_full[] = "%a, %d %b %Y %H:%M:%S %z";
|
||||
const char RFC1123_no_wday[] = "%d %b %Y %H:%M:%S %z";
|
||||
|
||||
// A list of known time-zone names.
|
||||
// TODO: Refactor with src/time_zone_lookup_test.cc.
|
||||
const char* const kTimeZoneNames[] = {"Africa/Abidjan",
|
||||
"Africa/Accra",
|
||||
"Africa/Addis_Ababa",
|
||||
"Africa/Algiers",
|
||||
"Africa/Asmara",
|
||||
"Africa/Bamako",
|
||||
"Africa/Bangui",
|
||||
"Africa/Banjul",
|
||||
"Africa/Bissau",
|
||||
"Africa/Blantyre",
|
||||
"Africa/Brazzaville",
|
||||
"Africa/Bujumbura",
|
||||
"Africa/Cairo",
|
||||
"Africa/Casablanca",
|
||||
"Africa/Ceuta",
|
||||
"Africa/Conakry",
|
||||
"Africa/Dakar",
|
||||
"Africa/Dar_es_Salaam",
|
||||
"Africa/Djibouti",
|
||||
"Africa/Douala",
|
||||
"Africa/El_Aaiun",
|
||||
"Africa/Freetown",
|
||||
"Africa/Gaborone",
|
||||
"Africa/Harare",
|
||||
"Africa/Johannesburg",
|
||||
"Africa/Juba",
|
||||
"Africa/Kampala",
|
||||
"Africa/Khartoum",
|
||||
"Africa/Kigali",
|
||||
"Africa/Kinshasa",
|
||||
"Africa/Lagos",
|
||||
"Africa/Libreville",
|
||||
"Africa/Lome",
|
||||
"Africa/Luanda",
|
||||
"Africa/Lubumbashi",
|
||||
"Africa/Lusaka",
|
||||
"Africa/Malabo",
|
||||
"Africa/Maputo",
|
||||
"Africa/Maseru",
|
||||
"Africa/Mbabane",
|
||||
"Africa/Mogadishu",
|
||||
"Africa/Monrovia",
|
||||
"Africa/Nairobi",
|
||||
"Africa/Ndjamena",
|
||||
"Africa/Niamey",
|
||||
"Africa/Nouakchott",
|
||||
"Africa/Ouagadougou",
|
||||
"Africa/Porto-Novo",
|
||||
"Africa/Sao_Tome",
|
||||
"Africa/Timbuktu",
|
||||
"Africa/Tripoli",
|
||||
"Africa/Tunis",
|
||||
"Africa/Windhoek",
|
||||
"America/Adak",
|
||||
"America/Anchorage",
|
||||
"America/Anguilla",
|
||||
"America/Antigua",
|
||||
"America/Araguaina",
|
||||
"America/Argentina/Buenos_Aires",
|
||||
"America/Argentina/Catamarca",
|
||||
"America/Argentina/Cordoba",
|
||||
"America/Argentina/Jujuy",
|
||||
"America/Argentina/La_Rioja",
|
||||
"America/Argentina/Mendoza",
|
||||
"America/Argentina/Rio_Gallegos",
|
||||
"America/Argentina/Salta",
|
||||
"America/Argentina/San_Juan",
|
||||
"America/Argentina/San_Luis",
|
||||
"America/Argentina/Tucuman",
|
||||
"America/Argentina/Ushuaia",
|
||||
"America/Aruba",
|
||||
"America/Asuncion",
|
||||
"America/Atikokan",
|
||||
"America/Atka",
|
||||
"America/Bahia",
|
||||
"America/Bahia_Banderas",
|
||||
"America/Barbados",
|
||||
"America/Belem",
|
||||
"America/Belize",
|
||||
"America/Blanc-Sablon",
|
||||
"America/Boa_Vista",
|
||||
"America/Bogota",
|
||||
"America/Boise",
|
||||
"America/Cambridge_Bay",
|
||||
"America/Campo_Grande",
|
||||
"America/Cancun",
|
||||
"America/Caracas",
|
||||
"America/Cayenne",
|
||||
"America/Cayman",
|
||||
"America/Chicago",
|
||||
"America/Chihuahua",
|
||||
"America/Ciudad_Juarez",
|
||||
"America/Coral_Harbour",
|
||||
"America/Costa_Rica",
|
||||
"America/Creston",
|
||||
"America/Cuiaba",
|
||||
"America/Curacao",
|
||||
"America/Danmarkshavn",
|
||||
"America/Dawson",
|
||||
"America/Dawson_Creek",
|
||||
"America/Denver",
|
||||
"America/Detroit",
|
||||
"America/Dominica",
|
||||
"America/Edmonton",
|
||||
"America/Eirunepe",
|
||||
"America/El_Salvador",
|
||||
"America/Ensenada",
|
||||
"America/Fort_Nelson",
|
||||
"America/Fortaleza",
|
||||
"America/Glace_Bay",
|
||||
"America/Godthab",
|
||||
"America/Goose_Bay",
|
||||
"America/Grand_Turk",
|
||||
"America/Grenada",
|
||||
"America/Guadeloupe",
|
||||
"America/Guatemala",
|
||||
"America/Guayaquil",
|
||||
"America/Guyana",
|
||||
"America/Halifax",
|
||||
"America/Havana",
|
||||
"America/Hermosillo",
|
||||
"America/Indiana/Indianapolis",
|
||||
"America/Indiana/Knox",
|
||||
"America/Indiana/Marengo",
|
||||
"America/Indiana/Petersburg",
|
||||
"America/Indiana/Tell_City",
|
||||
"America/Indiana/Vevay",
|
||||
"America/Indiana/Vincennes",
|
||||
"America/Indiana/Winamac",
|
||||
"America/Inuvik",
|
||||
"America/Iqaluit",
|
||||
"America/Jamaica",
|
||||
"America/Juneau",
|
||||
"America/Kentucky/Louisville",
|
||||
"America/Kentucky/Monticello",
|
||||
"America/Kralendijk",
|
||||
"America/La_Paz",
|
||||
"America/Lima",
|
||||
"America/Los_Angeles",
|
||||
"America/Lower_Princes",
|
||||
"America/Maceio",
|
||||
"America/Managua",
|
||||
"America/Manaus",
|
||||
"America/Marigot",
|
||||
"America/Martinique",
|
||||
"America/Matamoros",
|
||||
"America/Mazatlan",
|
||||
"America/Menominee",
|
||||
"America/Merida",
|
||||
"America/Metlakatla",
|
||||
"America/Mexico_City",
|
||||
"America/Miquelon",
|
||||
"America/Moncton",
|
||||
"America/Monterrey",
|
||||
"America/Montevideo",
|
||||
"America/Montreal",
|
||||
"America/Montserrat",
|
||||
"America/Nassau",
|
||||
"America/New_York",
|
||||
"America/Nipigon",
|
||||
"America/Nome",
|
||||
"America/Noronha",
|
||||
"America/North_Dakota/Beulah",
|
||||
"America/North_Dakota/Center",
|
||||
"America/North_Dakota/New_Salem",
|
||||
"America/Nuuk",
|
||||
"America/Ojinaga",
|
||||
"America/Panama",
|
||||
"America/Pangnirtung",
|
||||
"America/Paramaribo",
|
||||
"America/Phoenix",
|
||||
"America/Port-au-Prince",
|
||||
"America/Port_of_Spain",
|
||||
"America/Porto_Acre",
|
||||
"America/Porto_Velho",
|
||||
"America/Puerto_Rico",
|
||||
"America/Punta_Arenas",
|
||||
"America/Rainy_River",
|
||||
"America/Rankin_Inlet",
|
||||
"America/Recife",
|
||||
"America/Regina",
|
||||
"America/Resolute",
|
||||
"America/Rio_Branco",
|
||||
"America/Santa_Isabel",
|
||||
"America/Santarem",
|
||||
"America/Santiago",
|
||||
"America/Santo_Domingo",
|
||||
"America/Sao_Paulo",
|
||||
"America/Scoresbysund",
|
||||
"America/Shiprock",
|
||||
"America/Sitka",
|
||||
"America/St_Barthelemy",
|
||||
"America/St_Johns",
|
||||
"America/St_Kitts",
|
||||
"America/St_Lucia",
|
||||
"America/St_Thomas",
|
||||
"America/St_Vincent",
|
||||
"America/Swift_Current",
|
||||
"America/Tegucigalpa",
|
||||
"America/Thule",
|
||||
"America/Thunder_Bay",
|
||||
"America/Tijuana",
|
||||
"America/Toronto",
|
||||
"America/Tortola",
|
||||
"America/Vancouver",
|
||||
"America/Virgin",
|
||||
"America/Whitehorse",
|
||||
"America/Winnipeg",
|
||||
"America/Yakutat",
|
||||
"America/Yellowknife",
|
||||
"Antarctica/Casey",
|
||||
"Antarctica/Davis",
|
||||
"Antarctica/DumontDUrville",
|
||||
"Antarctica/Macquarie",
|
||||
"Antarctica/Mawson",
|
||||
"Antarctica/McMurdo",
|
||||
"Antarctica/Palmer",
|
||||
"Antarctica/Rothera",
|
||||
"Antarctica/Syowa",
|
||||
"Antarctica/Troll",
|
||||
"Antarctica/Vostok",
|
||||
"Arctic/Longyearbyen",
|
||||
"Asia/Aden",
|
||||
"Asia/Almaty",
|
||||
"Asia/Amman",
|
||||
"Asia/Anadyr",
|
||||
"Asia/Aqtau",
|
||||
"Asia/Aqtobe",
|
||||
"Asia/Ashgabat",
|
||||
"Asia/Atyrau",
|
||||
"Asia/Baghdad",
|
||||
"Asia/Bahrain",
|
||||
"Asia/Baku",
|
||||
"Asia/Bangkok",
|
||||
"Asia/Barnaul",
|
||||
"Asia/Beirut",
|
||||
"Asia/Bishkek",
|
||||
"Asia/Brunei",
|
||||
"Asia/Chita",
|
||||
"Asia/Choibalsan",
|
||||
"Asia/Chongqing",
|
||||
"Asia/Colombo",
|
||||
"Asia/Damascus",
|
||||
"Asia/Dhaka",
|
||||
"Asia/Dili",
|
||||
"Asia/Dubai",
|
||||
"Asia/Dushanbe",
|
||||
"Asia/Famagusta",
|
||||
"Asia/Gaza",
|
||||
"Asia/Harbin",
|
||||
"Asia/Hebron",
|
||||
"Asia/Ho_Chi_Minh",
|
||||
"Asia/Hong_Kong",
|
||||
"Asia/Hovd",
|
||||
"Asia/Irkutsk",
|
||||
"Asia/Istanbul",
|
||||
"Asia/Jakarta",
|
||||
"Asia/Jayapura",
|
||||
"Asia/Jerusalem",
|
||||
"Asia/Kabul",
|
||||
"Asia/Kamchatka",
|
||||
"Asia/Karachi",
|
||||
"Asia/Kashgar",
|
||||
"Asia/Kathmandu",
|
||||
"Asia/Khandyga",
|
||||
"Asia/Kolkata",
|
||||
"Asia/Krasnoyarsk",
|
||||
"Asia/Kuala_Lumpur",
|
||||
"Asia/Kuching",
|
||||
"Asia/Kuwait",
|
||||
"Asia/Macau",
|
||||
"Asia/Magadan",
|
||||
"Asia/Makassar",
|
||||
"Asia/Manila",
|
||||
"Asia/Muscat",
|
||||
"Asia/Nicosia",
|
||||
"Asia/Novokuznetsk",
|
||||
"Asia/Novosibirsk",
|
||||
"Asia/Omsk",
|
||||
"Asia/Oral",
|
||||
"Asia/Phnom_Penh",
|
||||
"Asia/Pontianak",
|
||||
"Asia/Pyongyang",
|
||||
"Asia/Qatar",
|
||||
"Asia/Qostanay",
|
||||
"Asia/Qyzylorda",
|
||||
"Asia/Riyadh",
|
||||
"Asia/Sakhalin",
|
||||
"Asia/Samarkand",
|
||||
"Asia/Seoul",
|
||||
"Asia/Shanghai",
|
||||
"Asia/Singapore",
|
||||
"Asia/Srednekolymsk",
|
||||
"Asia/Taipei",
|
||||
"Asia/Tashkent",
|
||||
"Asia/Tbilisi",
|
||||
"Asia/Tehran",
|
||||
"Asia/Tel_Aviv",
|
||||
"Asia/Thimphu",
|
||||
"Asia/Tokyo",
|
||||
"Asia/Tomsk",
|
||||
"Asia/Ulaanbaatar",
|
||||
"Asia/Urumqi",
|
||||
"Asia/Ust-Nera",
|
||||
"Asia/Vientiane",
|
||||
"Asia/Vladivostok",
|
||||
"Asia/Yakutsk",
|
||||
"Asia/Yangon",
|
||||
"Asia/Yekaterinburg",
|
||||
"Asia/Yerevan",
|
||||
"Atlantic/Azores",
|
||||
"Atlantic/Bermuda",
|
||||
"Atlantic/Canary",
|
||||
"Atlantic/Cape_Verde",
|
||||
"Atlantic/Faroe",
|
||||
"Atlantic/Jan_Mayen",
|
||||
"Atlantic/Madeira",
|
||||
"Atlantic/Reykjavik",
|
||||
"Atlantic/South_Georgia",
|
||||
"Atlantic/St_Helena",
|
||||
"Atlantic/Stanley",
|
||||
"Australia/Adelaide",
|
||||
"Australia/Brisbane",
|
||||
"Australia/Broken_Hill",
|
||||
"Australia/Canberra",
|
||||
"Australia/Currie",
|
||||
"Australia/Darwin",
|
||||
"Australia/Eucla",
|
||||
"Australia/Hobart",
|
||||
"Australia/Lindeman",
|
||||
"Australia/Lord_Howe",
|
||||
"Australia/Melbourne",
|
||||
"Australia/Perth",
|
||||
"Australia/Sydney",
|
||||
"Australia/Yancowinna",
|
||||
"Etc/GMT",
|
||||
"Etc/GMT+0",
|
||||
"Etc/GMT+1",
|
||||
"Etc/GMT+10",
|
||||
"Etc/GMT+11",
|
||||
"Etc/GMT+12",
|
||||
"Etc/GMT+2",
|
||||
"Etc/GMT+3",
|
||||
"Etc/GMT+4",
|
||||
"Etc/GMT+5",
|
||||
"Etc/GMT+6",
|
||||
"Etc/GMT+7",
|
||||
"Etc/GMT+8",
|
||||
"Etc/GMT+9",
|
||||
"Etc/GMT-0",
|
||||
"Etc/GMT-1",
|
||||
"Etc/GMT-10",
|
||||
"Etc/GMT-11",
|
||||
"Etc/GMT-12",
|
||||
"Etc/GMT-13",
|
||||
"Etc/GMT-14",
|
||||
"Etc/GMT-2",
|
||||
"Etc/GMT-3",
|
||||
"Etc/GMT-4",
|
||||
"Etc/GMT-5",
|
||||
"Etc/GMT-6",
|
||||
"Etc/GMT-7",
|
||||
"Etc/GMT-8",
|
||||
"Etc/GMT-9",
|
||||
"Etc/GMT0",
|
||||
"Etc/Greenwich",
|
||||
"Etc/UCT",
|
||||
"Etc/UTC",
|
||||
"Etc/Universal",
|
||||
"Etc/Zulu",
|
||||
"Europe/Amsterdam",
|
||||
"Europe/Andorra",
|
||||
"Europe/Astrakhan",
|
||||
"Europe/Athens",
|
||||
"Europe/Belfast",
|
||||
"Europe/Belgrade",
|
||||
"Europe/Berlin",
|
||||
"Europe/Bratislava",
|
||||
"Europe/Brussels",
|
||||
"Europe/Bucharest",
|
||||
"Europe/Budapest",
|
||||
"Europe/Busingen",
|
||||
"Europe/Chisinau",
|
||||
"Europe/Copenhagen",
|
||||
"Europe/Dublin",
|
||||
"Europe/Gibraltar",
|
||||
"Europe/Guernsey",
|
||||
"Europe/Helsinki",
|
||||
"Europe/Isle_of_Man",
|
||||
"Europe/Istanbul",
|
||||
"Europe/Jersey",
|
||||
"Europe/Kaliningrad",
|
||||
"Europe/Kirov",
|
||||
"Europe/Kyiv",
|
||||
"Europe/Lisbon",
|
||||
"Europe/Ljubljana",
|
||||
"Europe/London",
|
||||
"Europe/Luxembourg",
|
||||
"Europe/Madrid",
|
||||
"Europe/Malta",
|
||||
"Europe/Mariehamn",
|
||||
"Europe/Minsk",
|
||||
"Europe/Monaco",
|
||||
"Europe/Moscow",
|
||||
"Europe/Nicosia",
|
||||
"Europe/Oslo",
|
||||
"Europe/Paris",
|
||||
"Europe/Podgorica",
|
||||
"Europe/Prague",
|
||||
"Europe/Riga",
|
||||
"Europe/Rome",
|
||||
"Europe/Samara",
|
||||
"Europe/San_Marino",
|
||||
"Europe/Sarajevo",
|
||||
"Europe/Saratov",
|
||||
"Europe/Simferopol",
|
||||
"Europe/Skopje",
|
||||
"Europe/Sofia",
|
||||
"Europe/Stockholm",
|
||||
"Europe/Tallinn",
|
||||
"Europe/Tirane",
|
||||
"Europe/Tiraspol",
|
||||
"Europe/Ulyanovsk",
|
||||
"Europe/Vaduz",
|
||||
"Europe/Vatican",
|
||||
"Europe/Vienna",
|
||||
"Europe/Vilnius",
|
||||
"Europe/Volgograd",
|
||||
"Europe/Warsaw",
|
||||
"Europe/Zagreb",
|
||||
"Europe/Zurich",
|
||||
"Factory",
|
||||
"Indian/Antananarivo",
|
||||
"Indian/Chagos",
|
||||
"Indian/Christmas",
|
||||
"Indian/Cocos",
|
||||
"Indian/Comoro",
|
||||
"Indian/Kerguelen",
|
||||
"Indian/Mahe",
|
||||
"Indian/Maldives",
|
||||
"Indian/Mauritius",
|
||||
"Indian/Mayotte",
|
||||
"Indian/Reunion",
|
||||
"Pacific/Apia",
|
||||
"Pacific/Auckland",
|
||||
"Pacific/Bougainville",
|
||||
"Pacific/Chatham",
|
||||
"Pacific/Chuuk",
|
||||
"Pacific/Easter",
|
||||
"Pacific/Efate",
|
||||
"Pacific/Fakaofo",
|
||||
"Pacific/Fiji",
|
||||
"Pacific/Funafuti",
|
||||
"Pacific/Galapagos",
|
||||
"Pacific/Gambier",
|
||||
"Pacific/Guadalcanal",
|
||||
"Pacific/Guam",
|
||||
"Pacific/Honolulu",
|
||||
"Pacific/Johnston",
|
||||
"Pacific/Kanton",
|
||||
"Pacific/Kiritimati",
|
||||
"Pacific/Kosrae",
|
||||
"Pacific/Kwajalein",
|
||||
"Pacific/Majuro",
|
||||
"Pacific/Marquesas",
|
||||
"Pacific/Midway",
|
||||
"Pacific/Nauru",
|
||||
"Pacific/Niue",
|
||||
"Pacific/Norfolk",
|
||||
"Pacific/Noumea",
|
||||
"Pacific/Pago_Pago",
|
||||
"Pacific/Palau",
|
||||
"Pacific/Pitcairn",
|
||||
"Pacific/Pohnpei",
|
||||
"Pacific/Port_Moresby",
|
||||
"Pacific/Rarotonga",
|
||||
"Pacific/Saipan",
|
||||
"Pacific/Samoa",
|
||||
"Pacific/Tahiti",
|
||||
"Pacific/Tarawa",
|
||||
"Pacific/Tongatapu",
|
||||
"Pacific/Wake",
|
||||
"Pacific/Wallis",
|
||||
"Pacific/Yap",
|
||||
"UTC",
|
||||
nullptr};
|
||||
|
||||
std::vector<std::string> AllTimeZoneNames() {
|
||||
std::vector<std::string> names;
|
||||
for (const char* const* namep = kTimeZoneNames; *namep != nullptr; ++namep) {
|
||||
names.push_back(std::string("file:") + *namep);
|
||||
}
|
||||
assert(!names.empty());
|
||||
|
||||
std::mt19937 urbg(42); // a UniformRandomBitGenerator with fixed seed
|
||||
std::shuffle(names.begin(), names.end(), urbg);
|
||||
return names;
|
||||
}
|
||||
|
||||
cctz::time_zone TestTimeZone() {
|
||||
cctz::time_zone tz;
|
||||
cctz::load_time_zone("America/Los_Angeles", &tz);
|
||||
return tz;
|
||||
}
|
||||
|
||||
void BM_Zone_LoadUTCTimeZoneFirst(benchmark::State& state) {
|
||||
cctz::time_zone tz;
|
||||
cctz::load_time_zone("UTC", &tz); // in case we're first
|
||||
cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(cctz::load_time_zone("UTC", &tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_LoadUTCTimeZoneFirst);
|
||||
|
||||
void BM_Zone_LoadUTCTimeZoneLast(benchmark::State& state) {
|
||||
cctz::time_zone tz;
|
||||
for (const auto& name : AllTimeZoneNames()) {
|
||||
cctz::load_time_zone(name, &tz); // prime cache
|
||||
}
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(cctz::load_time_zone("UTC", &tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_LoadUTCTimeZoneLast);
|
||||
|
||||
void BM_Zone_LoadTimeZoneFirst(benchmark::State& state) {
|
||||
cctz::time_zone tz = cctz::utc_time_zone(); // in case we're first
|
||||
const std::string name = "file:America/Los_Angeles";
|
||||
while (state.KeepRunning()) {
|
||||
state.PauseTiming();
|
||||
cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
|
||||
state.ResumeTiming();
|
||||
benchmark::DoNotOptimize(cctz::load_time_zone(name, &tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_LoadTimeZoneFirst);
|
||||
|
||||
void BM_Zone_LoadTimeZoneCached(benchmark::State& state) {
|
||||
cctz::time_zone tz = cctz::utc_time_zone(); // in case we're first
|
||||
cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
|
||||
const std::string name = "file:America/Los_Angeles";
|
||||
cctz::load_time_zone(name, &tz); // prime cache
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(cctz::load_time_zone(name, &tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_LoadTimeZoneCached);
|
||||
|
||||
void BM_Zone_LoadLocalTimeZoneCached(benchmark::State& state) {
|
||||
cctz::utc_time_zone(); // in case we're first
|
||||
cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
|
||||
cctz::local_time_zone(); // prime cache
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(cctz::local_time_zone());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_LoadLocalTimeZoneCached);
|
||||
|
||||
void BM_Zone_LoadAllTimeZonesFirst(benchmark::State& state) {
|
||||
cctz::time_zone tz;
|
||||
const std::vector<std::string> names = AllTimeZoneNames();
|
||||
for (auto index = names.size(); state.KeepRunning(); ++index) {
|
||||
if (index == names.size()) {
|
||||
index = 0;
|
||||
}
|
||||
if (index == 0) {
|
||||
state.PauseTiming();
|
||||
cctz::time_zone::Impl::ClearTimeZoneMapTestOnly();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
benchmark::DoNotOptimize(cctz::load_time_zone(names[index], &tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_LoadAllTimeZonesFirst);
|
||||
|
||||
void BM_Zone_LoadAllTimeZonesCached(benchmark::State& state) {
|
||||
cctz::time_zone tz;
|
||||
const std::vector<std::string> names = AllTimeZoneNames();
|
||||
for (const auto& name : names) {
|
||||
cctz::load_time_zone(name, &tz); // prime cache
|
||||
}
|
||||
for (auto index = names.size(); state.KeepRunning(); ++index) {
|
||||
if (index == names.size()) {
|
||||
index = 0;
|
||||
}
|
||||
benchmark::DoNotOptimize(cctz::load_time_zone(names[index], &tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_LoadAllTimeZonesCached);
|
||||
|
||||
void BM_Zone_TimeZoneEqualityImplicit(benchmark::State& state) {
|
||||
cctz::time_zone tz; // implicit UTC
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(tz == tz);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_TimeZoneEqualityImplicit);
|
||||
|
||||
void BM_Zone_TimeZoneEqualityExplicit(benchmark::State& state) {
|
||||
cctz::time_zone tz = cctz::utc_time_zone(); // explicit UTC
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(tz == tz);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_TimeZoneEqualityExplicit);
|
||||
|
||||
void BM_Zone_UTCTimeZone(benchmark::State& state) {
|
||||
cctz::time_zone tz;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(cctz::utc_time_zone());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Zone_UTCTimeZone);
|
||||
|
||||
// In each "ToCivil" benchmark we switch between two instants separated
|
||||
// by at least one transition in order to defeat any internal caching of
|
||||
// previous results (e.g., see local_time_hint_).
|
||||
//
|
||||
// The "UTC" variants use UTC instead of the Google/local time zone.
|
||||
|
||||
void BM_Time_ToCivil_CCTZ(benchmark::State& state) {
|
||||
const cctz::time_zone tz = TestTimeZone();
|
||||
std::chrono::system_clock::time_point tp =
|
||||
std::chrono::system_clock::from_time_t(1384569027);
|
||||
std::chrono::system_clock::time_point tp2 =
|
||||
std::chrono::system_clock::from_time_t(1418962578);
|
||||
while (state.KeepRunning()) {
|
||||
std::swap(tp, tp2);
|
||||
tp += std::chrono::seconds(1);
|
||||
benchmark::DoNotOptimize(cctz::convert(tp, tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToCivil_CCTZ);
|
||||
|
||||
void BM_Time_ToCivil_Libc(benchmark::State& state) {
|
||||
// No timezone support, so just use localtime.
|
||||
time_t t = 1384569027;
|
||||
time_t t2 = 1418962578;
|
||||
struct tm tm;
|
||||
while (state.KeepRunning()) {
|
||||
std::swap(t, t2);
|
||||
t += 1;
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
benchmark::DoNotOptimize(localtime_s(&tm, &t));
|
||||
#else
|
||||
benchmark::DoNotOptimize(localtime_r(&t, &tm));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToCivil_Libc);
|
||||
|
||||
void BM_Time_ToCivilUTC_CCTZ(benchmark::State& state) {
|
||||
const cctz::time_zone tz = cctz::utc_time_zone();
|
||||
std::chrono::system_clock::time_point tp =
|
||||
std::chrono::system_clock::from_time_t(1384569027);
|
||||
while (state.KeepRunning()) {
|
||||
tp += std::chrono::seconds(1);
|
||||
benchmark::DoNotOptimize(cctz::convert(tp, tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToCivilUTC_CCTZ);
|
||||
|
||||
void BM_Time_ToCivilUTC_Libc(benchmark::State& state) {
|
||||
time_t t = 1384569027;
|
||||
struct tm tm;
|
||||
while (state.KeepRunning()) {
|
||||
t += 1;
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
benchmark::DoNotOptimize(gmtime_s(&tm, &t));
|
||||
#else
|
||||
benchmark::DoNotOptimize(gmtime_r(&t, &tm));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToCivilUTC_Libc);
|
||||
|
||||
// In each "FromCivil" benchmark we switch between two YMDhms values
|
||||
// separated by at least one transition in order to defeat any internal
|
||||
// caching of previous results (e.g., see time_local_hint_).
|
||||
//
|
||||
// The "UTC" variants use UTC instead of the Google/local time zone.
|
||||
// The "Day0" variants require normalization of the day of month.
|
||||
|
||||
void BM_Time_FromCivil_CCTZ(benchmark::State& state) {
|
||||
const cctz::time_zone tz = TestTimeZone();
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
if ((i++ & 1) == 0) {
|
||||
benchmark::DoNotOptimize(
|
||||
cctz::convert(cctz::civil_second(2014, 12, 18, 20, 16, 18), tz));
|
||||
} else {
|
||||
benchmark::DoNotOptimize(
|
||||
cctz::convert(cctz::civil_second(2013, 11, 15, 18, 30, 27), tz));
|
||||
}
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivil_CCTZ);
|
||||
|
||||
void BM_Time_FromCivil_Libc(benchmark::State& state) {
|
||||
// No timezone support, so just use localtime.
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
struct tm tm;
|
||||
if ((i++ & 1) == 0) {
|
||||
tm.tm_year = 2014 - 1900;
|
||||
tm.tm_mon = 12 - 1;
|
||||
tm.tm_mday = 18;
|
||||
tm.tm_hour = 20;
|
||||
tm.tm_min = 16;
|
||||
tm.tm_sec = 18;
|
||||
} else {
|
||||
tm.tm_year = 2013 - 1900;
|
||||
tm.tm_mon = 11 - 1;
|
||||
tm.tm_mday = 15;
|
||||
tm.tm_hour = 18;
|
||||
tm.tm_min = 30;
|
||||
tm.tm_sec = 27;
|
||||
}
|
||||
tm.tm_isdst = -1;
|
||||
benchmark::DoNotOptimize(mktime(&tm));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivil_Libc);
|
||||
|
||||
void BM_Time_FromCivilUTC_CCTZ(benchmark::State& state) {
|
||||
const cctz::time_zone tz = cctz::utc_time_zone();
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(
|
||||
cctz::convert(cctz::civil_second(2014, 12, 18, 20, 16, 18), tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivilUTC_CCTZ);
|
||||
|
||||
// There is no BM_Time_FromCivilUTC_Libc.
|
||||
|
||||
void BM_Time_FromCivilDay0_CCTZ(benchmark::State& state) {
|
||||
const cctz::time_zone tz = TestTimeZone();
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
if ((i++ & 1) == 0) {
|
||||
benchmark::DoNotOptimize(
|
||||
cctz::convert(cctz::civil_second(2014, 12, 0, 20, 16, 18), tz));
|
||||
} else {
|
||||
benchmark::DoNotOptimize(
|
||||
cctz::convert(cctz::civil_second(2013, 11, 0, 18, 30, 27), tz));
|
||||
}
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivilDay0_CCTZ);
|
||||
|
||||
void BM_Time_FromCivilDay0_Libc(benchmark::State& state) {
|
||||
// No timezone support, so just use localtime.
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
struct tm tm;
|
||||
if ((i++ & 1) == 0) {
|
||||
tm.tm_year = 2014 - 1900;
|
||||
tm.tm_mon = 12 - 1;
|
||||
tm.tm_mday = 0;
|
||||
tm.tm_hour = 20;
|
||||
tm.tm_min = 16;
|
||||
tm.tm_sec = 18;
|
||||
} else {
|
||||
tm.tm_year = 2013 - 1900;
|
||||
tm.tm_mon = 11 - 1;
|
||||
tm.tm_mday = 0;
|
||||
tm.tm_hour = 18;
|
||||
tm.tm_min = 30;
|
||||
tm.tm_sec = 27;
|
||||
}
|
||||
tm.tm_isdst = -1;
|
||||
benchmark::DoNotOptimize(mktime(&tm));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivilDay0_Libc);
|
||||
|
||||
const char* const kFormats[] = {
|
||||
RFC1123_full, // 0
|
||||
RFC1123_no_wday, // 1
|
||||
RFC3339_full, // 2
|
||||
RFC3339_sec, // 3
|
||||
"%Y-%m-%d%ET%H:%M:%S", // 4
|
||||
"%Y-%m-%d", // 5
|
||||
};
|
||||
const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
|
||||
|
||||
void BM_Format_FormatTime(benchmark::State& state) {
|
||||
const std::string fmt = kFormats[state.range(0)];
|
||||
state.SetLabel(fmt);
|
||||
const cctz::time_zone tz = TestTimeZone();
|
||||
const std::chrono::system_clock::time_point tp =
|
||||
cctz::convert(cctz::civil_second(1977, 6, 28, 9, 8, 7), tz) +
|
||||
std::chrono::microseconds(1);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(cctz::format(fmt, tp, tz));
|
||||
}
|
||||
}
|
||||
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 cctz::time_zone tz = TestTimeZone();
|
||||
std::chrono::system_clock::time_point tp =
|
||||
cctz::convert(cctz::civil_second(1977, 6, 28, 9, 8, 7), tz) +
|
||||
std::chrono::microseconds(1);
|
||||
const std::string when = cctz::format(fmt, tp, tz);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(cctz::parse(fmt, when, tz, &tp));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Format_ParseTime)->DenseRange(0, kNumFormats - 1);
|
||||
|
||||
} // namespace
|
||||
|
|
@ -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
|
|
@ -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 "absl/time/internal/cctz/src/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 == "UTC" || name == "UTC0") {
|
||||
*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_n(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
|
||||
|
|
@ -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_
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,47 @@
|
|||
// 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/src/time_zone_if.h"
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/src/time_zone_info.h"
|
||||
#include "absl/time/internal/cctz/src/time_zone_libc.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
std::unique_ptr<TimeZoneIf> TimeZoneIf::UTC() { return TimeZoneInfo::UTC(); }
|
||||
|
||||
std::unique_ptr<TimeZoneIf> TimeZoneIf::Make(const std::string& name) {
|
||||
// Support "libc:localtime" and "libc:*" to access the legacy
|
||||
// localtime and UTC support respectively from the C library.
|
||||
// NOTE: The "libc:*" zones are internal, test-only interfaces, and
|
||||
// are subject to change/removal without notice. Do not use them.
|
||||
if (name.compare(0, 5, "libc:") == 0) {
|
||||
return TimeZoneLibC::Make(name.substr(5));
|
||||
}
|
||||
|
||||
// Otherwise use the "zoneinfo" implementation.
|
||||
return TimeZoneInfo::Make(name);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
// 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:
|
||||
// Factory functions for TimeZoneIf implementations.
|
||||
static std::unique_ptr<TimeZoneIf> UTC(); // never fails
|
||||
static std::unique_ptr<TimeZoneIf> Make(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() = default;
|
||||
TimeZoneIf(const TimeZoneIf&) = delete;
|
||||
TimeZoneIf& operator=(const TimeZoneIf&) = delete;
|
||||
};
|
||||
|
||||
// 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, and that the results are representable.
|
||||
// (That is, that they share an epoch, which is required since C++20.)
|
||||
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_
|
||||
|
|
@ -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/src/time_zone_impl.h"
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/src/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 Impl* const utc_impl = UTCImpl();
|
||||
|
||||
// Check for UTC (which is never a key in time_zone_map).
|
||||
auto offset = seconds::zero();
|
||||
if (FixedOffsetFromName(name, &offset) && offset == seconds::zero()) {
|
||||
*tz = time_zone(utc_impl);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check whether the time zone has already been loaded.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(TimeZoneMutex());
|
||||
if (time_zone_map != nullptr) {
|
||||
TimeZoneImplByName::const_iterator itr = time_zone_map->find(name);
|
||||
if (itr != time_zone_map->end()) {
|
||||
*tz = time_zone(itr->second);
|
||||
return itr->second != utc_impl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load the new time zone (outside the lock).
|
||||
std::unique_ptr<const Impl> new_impl(new Impl(name));
|
||||
|
||||
// Add the new time zone to the map.
|
||||
std::lock_guard<std::mutex> lock(TimeZoneMutex());
|
||||
if (time_zone_map == nullptr) time_zone_map = new TimeZoneImplByName;
|
||||
const Impl*& impl = (*time_zone_map)[name];
|
||||
if (impl == nullptr) { // this thread won any load race
|
||||
impl = new_impl->zone_ ? new_impl.release() : utc_impl;
|
||||
}
|
||||
*tz = time_zone(impl);
|
||||
return impl != utc_impl;
|
||||
}
|
||||
|
||||
void time_zone::Impl::ClearTimeZoneMapTestOnly() {
|
||||
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() : name_("UTC"), zone_(TimeZoneIf::UTC()) {}
|
||||
|
||||
time_zone::Impl::Impl(const std::string& name)
|
||||
: name_(name), zone_(TimeZoneIf::Make(name_)) {}
|
||||
|
||||
const time_zone::Impl* time_zone::Impl::UTCImpl() {
|
||||
static const Impl* utc_impl = new Impl;
|
||||
return utc_impl;
|
||||
}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// 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 "absl/time/internal/cctz/src/time_zone_if.h"
|
||||
#include "absl/time/internal/cctz/src/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:
|
||||
Impl();
|
||||
explicit Impl(const std::string& name);
|
||||
Impl(const Impl&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
|
||||
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_
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,128 @@
|
|||
// 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 <memory>
|
||||
#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 "absl/time/internal/cctz/src/time_zone_if.h"
|
||||
#include "absl/time/internal/cctz/src/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:
|
||||
// Factories.
|
||||
static std::unique_ptr<TimeZoneInfo> UTC(); // never fails
|
||||
static std::unique_ptr<TimeZoneInfo> Make(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:
|
||||
TimeZoneInfo() = default;
|
||||
TimeZoneInfo(const TimeZoneInfo&) = delete;
|
||||
TimeZoneInfo& operator=(const TimeZoneInfo&) = delete;
|
||||
|
||||
bool GetTransitionType(std::int_fast32_t utc_offset, bool is_dst,
|
||||
const std::string& abbr, std::uint_least8_t* index);
|
||||
bool EquivTransitions(std::uint_fast8_t tt1_index,
|
||||
std::uint_fast8_t tt2_index) const;
|
||||
bool ExtendTransitions();
|
||||
|
||||
bool ResetToBuiltinUTC(const seconds& offset);
|
||||
bool Load(const std::string& name);
|
||||
bool Load(ZoneInfoSource* zip);
|
||||
|
||||
// Helpers for BreakTime() and MakeTime().
|
||||
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
|
||||
const TransitionType& tt) const;
|
||||
time_zone::absolute_lookup LocalTime(std::int_fast64_t unix_time,
|
||||
const Transition& tr) const;
|
||||
time_zone::civil_lookup TimeLocal(const civil_second& cs,
|
||||
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_
|
||||
|
|
@ -0,0 +1,333 @@
|
|||
// 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(_CRT_SECURE_NO_WARNINGS) && defined(_WIN32)
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
#endif
|
||||
|
||||
#include "absl/time/internal/cctz/src/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"
|
||||
|
||||
#if defined(_AIX)
|
||||
extern "C" {
|
||||
extern long altzone;
|
||||
}
|
||||
#endif
|
||||
|
||||
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) || defined(_AIX)
|
||||
// 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];
|
||||
}
|
||||
#elif defined(__VXWORKS__)
|
||||
// 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
|
||||
using tm_gmtoff_t = decltype(tm_gmtoff(std::tm{}));
|
||||
|
||||
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 a struct tm.
|
||||
// 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,
|
||||
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;
|
||||
}
|
||||
}
|
||||
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, tm_gmtoff_t offset) {
|
||||
std::tm tm;
|
||||
while (lo + 1 != hi) {
|
||||
const std::time_t mid = lo + (hi - lo) / 2;
|
||||
std::tm* tmp = local_time(&mid, &tm);
|
||||
if (tmp != nullptr) {
|
||||
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) {
|
||||
tmp = local_time(&lo, &tm);
|
||||
if (tmp != nullptr) {
|
||||
if (tm_gmtoff(*tmp) == offset) break;
|
||||
}
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
}
|
||||
return hi;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<TimeZoneLibC> TimeZoneLibC::Make(const std::string& name) {
|
||||
return std::unique_ptr<TimeZoneLibC>(new TimeZoneLibC(name));
|
||||
}
|
||||
|
||||
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. For
|
||||
// example, some implementations treat "tm_isdst" as a demand (useless),
|
||||
// and some as a disambiguator (useful).
|
||||
std::time_t t0, t1;
|
||||
std::tm tm0, tm1;
|
||||
if (make_time(cs, 0, &t0, &tm0) && make_time(cs, 1, &t1, &tm1)) {
|
||||
if (tm0.tm_isdst == tm1.tm_isdst) {
|
||||
// The civil time was singular (pre == trans == post).
|
||||
const time_point<seconds> tp = FromUnixSeconds(tm0.tm_isdst ? t1 : t0);
|
||||
return {time_zone::civil_lookup::UNIQUE, tp, tp, tp};
|
||||
}
|
||||
|
||||
tm_gmtoff_t offset = tm_gmtoff(tm0);
|
||||
if (t0 < t1) { // negative DST
|
||||
std::swap(t0, t1);
|
||||
offset = tm_gmtoff(tm1);
|
||||
}
|
||||
|
||||
const std::time_t tt = find_trans(t1, t0, offset);
|
||||
const time_point<seconds> trans = FromUnixSeconds(tt);
|
||||
|
||||
if (tm0.tm_isdst) {
|
||||
// The civil time did not exist (pre >= trans > post).
|
||||
const time_point<seconds> pre = FromUnixSeconds(t0);
|
||||
const time_point<seconds> post = FromUnixSeconds(t1);
|
||||
return {time_zone::civil_lookup::SKIPPED, pre, trans, post};
|
||||
}
|
||||
|
||||
// The civil time was ambiguous (pre < trans <= post).
|
||||
const time_point<seconds> pre = FromUnixSeconds(t1);
|
||||
const time_point<seconds> post = FromUnixSeconds(t0);
|
||||
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";
|
||||
}
|
||||
|
||||
TimeZoneLibC::TimeZoneLibC(const std::string& name)
|
||||
: local_(name == "localtime") {}
|
||||
|
||||
} // namespace cctz
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/time/internal/cctz/src/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.
|
||||
class TimeZoneLibC : public TimeZoneIf {
|
||||
public:
|
||||
// Factory.
|
||||
static std::unique_ptr<TimeZoneLibC> Make(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:
|
||||
explicit TimeZoneLibC(const std::string& name);
|
||||
TimeZoneLibC(const TimeZoneLibC&) = delete;
|
||||
TimeZoneLibC& operator=(const TimeZoneLibC&) = delete;
|
||||
|
||||
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_
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
// 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>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <CoreFoundation/CFTimeZone.h>
|
||||
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
#if defined(__Fuchsia__)
|
||||
#include <fuchsia/intl/cpp/fidl.h>
|
||||
#include <lib/async-loop/cpp/loop.h>
|
||||
#include <lib/fdio/directory.h>
|
||||
#include <zircon/types.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <sdkddkver.h>
|
||||
// Include only when the SDK is for Windows 10 (and later), and the binary is
|
||||
// targeted for Windows XP and later.
|
||||
// Note: The Windows SDK added windows.globalization.h file for Windows 10, but
|
||||
// MinGW did not add it until NTDDI_WIN10_NI (SDK version 10.0.22621.0).
|
||||
#if ((defined(_WIN32_WINNT_WIN10) && !defined(__MINGW32__)) || \
|
||||
(defined(NTDDI_WIN10_NI) && NTDDI_VERSION >= NTDDI_WIN10_NI)) && \
|
||||
(_WIN32_WINNT >= _WIN32_WINNT_WINXP)
|
||||
#define USE_WIN32_LOCAL_TIME_ZONE
|
||||
#include <roapi.h>
|
||||
#include <tchar.h>
|
||||
#include <wchar.h>
|
||||
#include <windows.globalization.h>
|
||||
#include <windows.h>
|
||||
#include <winstring.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "absl/time/internal/cctz/src/time_zone_fixed.h"
|
||||
#include "absl/time/internal/cctz/src/time_zone_impl.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
namespace cctz {
|
||||
|
||||
namespace {
|
||||
#if defined(USE_WIN32_LOCAL_TIME_ZONE)
|
||||
// Calls the WinRT Calendar.GetTimeZone method to obtain the IANA ID of the
|
||||
// local time zone. Returns an empty vector in case of an error.
|
||||
std::string win32_local_time_zone(const HMODULE combase) {
|
||||
std::string result;
|
||||
const auto ro_activate_instance =
|
||||
reinterpret_cast<decltype(&RoActivateInstance)>(
|
||||
GetProcAddress(combase, "RoActivateInstance"));
|
||||
if (!ro_activate_instance) {
|
||||
return result;
|
||||
}
|
||||
const auto windows_create_string_reference =
|
||||
reinterpret_cast<decltype(&WindowsCreateStringReference)>(
|
||||
GetProcAddress(combase, "WindowsCreateStringReference"));
|
||||
if (!windows_create_string_reference) {
|
||||
return result;
|
||||
}
|
||||
const auto windows_delete_string =
|
||||
reinterpret_cast<decltype(&WindowsDeleteString)>(
|
||||
GetProcAddress(combase, "WindowsDeleteString"));
|
||||
if (!windows_delete_string) {
|
||||
return result;
|
||||
}
|
||||
const auto windows_get_string_raw_buffer =
|
||||
reinterpret_cast<decltype(&WindowsGetStringRawBuffer)>(
|
||||
GetProcAddress(combase, "WindowsGetStringRawBuffer"));
|
||||
if (!windows_get_string_raw_buffer) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// The string returned by WindowsCreateStringReference doesn't need to be
|
||||
// deleted.
|
||||
HSTRING calendar_class_id;
|
||||
HSTRING_HEADER calendar_class_id_header;
|
||||
HRESULT hr = windows_create_string_reference(
|
||||
RuntimeClass_Windows_Globalization_Calendar,
|
||||
sizeof(RuntimeClass_Windows_Globalization_Calendar) / sizeof(wchar_t) - 1,
|
||||
&calendar_class_id_header, &calendar_class_id);
|
||||
if (FAILED(hr)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
IInspectable* calendar;
|
||||
hr = ro_activate_instance(calendar_class_id, &calendar);
|
||||
if (FAILED(hr)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ABI::Windows::Globalization::ITimeZoneOnCalendar* time_zone;
|
||||
hr = calendar->QueryInterface(IID_PPV_ARGS(&time_zone));
|
||||
if (FAILED(hr)) {
|
||||
calendar->Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
HSTRING tz_hstr;
|
||||
hr = time_zone->GetTimeZone(&tz_hstr);
|
||||
if (SUCCEEDED(hr)) {
|
||||
UINT32 wlen;
|
||||
const PCWSTR tz_wstr = windows_get_string_raw_buffer(tz_hstr, &wlen);
|
||||
if (tz_wstr) {
|
||||
const int size =
|
||||
WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
|
||||
nullptr, 0, nullptr, nullptr);
|
||||
result.resize(static_cast<size_t>(size));
|
||||
WideCharToMultiByte(CP_UTF8, 0, tz_wstr, static_cast<int>(wlen),
|
||||
&result[0], size, nullptr, nullptr);
|
||||
}
|
||||
windows_delete_string(tz_hstr);
|
||||
}
|
||||
time_zone->Release();
|
||||
calendar->Release();
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
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);
|
||||
CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, encoding) + 1;
|
||||
buffer.resize(static_cast<size_t>(max_size));
|
||||
if (CFStringGetCString(tz_name, &buffer[0], max_size, encoding)) {
|
||||
zone = &buffer[0];
|
||||
}
|
||||
}
|
||||
CFRelease(tz_default);
|
||||
#endif
|
||||
#if defined(__Fuchsia__)
|
||||
std::string primary_tz;
|
||||
[&]() {
|
||||
// Note: We can't use the synchronous FIDL API here because it doesn't
|
||||
// allow timeouts; if the FIDL call failed, local_time_zone() would never
|
||||
// return.
|
||||
|
||||
const zx::duration kTimeout = zx::msec(500);
|
||||
|
||||
// Don't attach to the thread because otherwise the thread's dispatcher
|
||||
// would be set to null when the loop is destroyed, causing any other FIDL
|
||||
// code running on the same thread to crash.
|
||||
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
|
||||
|
||||
fuchsia::intl::PropertyProviderHandle handle;
|
||||
zx_status_t status = fdio_service_connect_by_name(
|
||||
fuchsia::intl::PropertyProvider::Name_,
|
||||
handle.NewRequest().TakeChannel().release());
|
||||
if (status != ZX_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
fuchsia::intl::PropertyProviderPtr intl_provider;
|
||||
status = intl_provider.Bind(std::move(handle), loop.dispatcher());
|
||||
if (status != ZX_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
intl_provider->GetProfile(
|
||||
[&loop, &primary_tz](fuchsia::intl::Profile profile) {
|
||||
if (!profile.time_zones().empty()) {
|
||||
primary_tz = profile.time_zones()[0].id;
|
||||
}
|
||||
loop.Quit();
|
||||
});
|
||||
loop.Run(zx::deadline_after(kTimeout));
|
||||
}();
|
||||
|
||||
if (!primary_tz.empty()) {
|
||||
zone = primary_tz.c_str();
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_WIN32_LOCAL_TIME_ZONE)
|
||||
// Use the WinRT Calendar class to get the local time zone. This feature is
|
||||
// available on Windows 10 and later. The library is dynamically linked to
|
||||
// maintain binary compatibility with Windows XP - Windows 7. On Windows 8,
|
||||
// The combase.dll API functions are available but the RoActivateInstance
|
||||
// call will fail for the Calendar class.
|
||||
std::string winrt_tz;
|
||||
const HMODULE combase =
|
||||
LoadLibraryEx(_T("combase.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if (combase) {
|
||||
const auto ro_initialize = reinterpret_cast<decltype(&::RoInitialize)>(
|
||||
GetProcAddress(combase, "RoInitialize"));
|
||||
const auto ro_uninitialize = reinterpret_cast<decltype(&::RoUninitialize)>(
|
||||
GetProcAddress(combase, "RoUninitialize"));
|
||||
if (ro_initialize && ro_uninitialize) {
|
||||
const HRESULT hr = ro_initialize(RO_INIT_MULTITHREADED);
|
||||
// RPC_E_CHANGED_MODE means that a previous RoInitialize call specified
|
||||
// a different concurrency model. The WinRT runtime is initialized and
|
||||
// should work for our purpose here, but we should *not* call
|
||||
// RoUninitialize because it's a failure.
|
||||
if (SUCCEEDED(hr) || hr == RPC_E_CHANGED_MODE) {
|
||||
winrt_tz = win32_local_time_zone(combase);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ro_uninitialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
FreeLibrary(combase);
|
||||
}
|
||||
if (!winrt_tz.empty()) {
|
||||
zone = winrt_tz.c_str();
|
||||
}
|
||||
#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
|
|
@ -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 "absl/time/internal/cctz/src/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
|
||||
|
|
@ -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 abbreviation 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_
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/* 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 TZDEFRULES
|
||||
#define TZDEFRULES "posixrules"
|
||||
#endif /* !defined TZDEFRULES */
|
||||
|
||||
/* See Internet RFC 9636 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'-'4' as of 2021 */
|
||||
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.1-2017 proleptic TZ 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.1-2017
|
||||
** representation for such instants).
|
||||
**
|
||||
** If tz_version is '3' or greater, the TZ string can be any POSIX.1-2024
|
||||
** proleptic TZ string, which means the above is extended as follows.
|
||||
** First, the TZ string's hour offset may range from -167
|
||||
** through 167 as compared to the range 0 through 24 required
|
||||
** by POSIX.1-2017 and earlier.
|
||||
** 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
|
||||
/* This must be at least 242 for Europe/London with 'zic -b fat'. */
|
||||
#define TZ_MAX_TIMES 2000
|
||||
#endif /* !defined TZ_MAX_TIMES */
|
||||
|
||||
#ifndef TZ_MAX_TYPES
|
||||
/* This must be at least 18 for Europe/Vilnius with 'zic -b fat'. */
|
||||
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
|
||||
#endif /* !defined TZ_MAX_TYPES */
|
||||
|
||||
#ifndef TZ_MAX_CHARS
|
||||
/* This must be at least 40 for America/Anchorage. */
|
||||
#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
|
||||
/* This must be at least 27 for leap seconds from 1972 through mid-2023.
|
||||
There's a plan to discontinue leap seconds by 2035. */
|
||||
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
|
||||
#endif /* !defined TZ_MAX_LEAPS */
|
||||
|
||||
#endif /* !defined TZFILE_H */
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
// 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__) && \
|
||||
!defined(__CYGWIN__)
|
||||
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) || defined(_M_ARM)
|
||||
#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
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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 <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
|
||||
static int64_t GetCurrentTimeNanosFromSystem() {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now() -
|
||||
std::chrono::system_clock::from_time_t(0))
|
||||
.count();
|
||||
}
|
||||
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#include "absl/time/clock.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <ctime>
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
|
||||
static int64_t GetCurrentTimeNanosFromSystem() {
|
||||
const int64_t kNanosPerSecond = 1000 * 1000 * 1000;
|
||||
struct timespec ts;
|
||||
ABSL_RAW_CHECK(clock_gettime(CLOCK_REALTIME, &ts) == 0,
|
||||
"Failed to read real-time clock.");
|
||||
return (int64_t{ts.tv_sec} * kNanosPerSecond +
|
||||
int64_t{ts.tv_nsec});
|
||||
}
|
||||
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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/internal/test_util.h"
|
||||
|
||||
#include "absl/base/config.h"
|
||||
#include "absl/base/internal/raw_logging.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
|
||||
TimeZone LoadTimeZone(const std::string& name) {
|
||||
TimeZone tz;
|
||||
ABSL_RAW_CHECK(LoadTimeZone(name, &tz), name.c_str());
|
||||
return tz;
|
||||
}
|
||||
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
33
TMessagesProj/jni/voip/webrtc/absl/time/internal/test_util.h
Normal file
33
TMessagesProj/jni/voip/webrtc/absl/time/internal/test_util.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// 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.
|
||||
|
||||
#ifndef ABSL_TIME_INTERNAL_TEST_UTIL_H_
|
||||
#define ABSL_TIME_INTERNAL_TEST_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace time_internal {
|
||||
|
||||
// Loads the named timezone, but dies on any failure.
|
||||
absl::TimeZone LoadTimeZone(const std::string& name);
|
||||
|
||||
} // namespace time_internal
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_TIME_INTERNAL_TEST_UTIL_H_
|
||||
507
TMessagesProj/jni/voip/webrtc/absl/time/time.cc
Normal file
507
TMessagesProj/jni/voip/webrtc/absl/time/time.cc
Normal file
|
|
@ -0,0 +1,507 @@
|
|||
// 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::Time class, which is declared in
|
||||
// //absl/time.h.
|
||||
//
|
||||
// The representation for an absl::Time is an absl::Duration offset from the
|
||||
// epoch. We use the traditional Unix epoch (1970-01-01 00:00:00 +0000)
|
||||
// for convenience, but this is not exposed in the API and could be changed.
|
||||
//
|
||||
// NOTE: To keep type verbosity to a minimum, the following variable naming
|
||||
// conventions are used throughout this file.
|
||||
//
|
||||
// tz: An absl::TimeZone
|
||||
// ci: An absl::TimeZone::CivilInfo
|
||||
// ti: An absl::TimeZone::TimeInfo
|
||||
// cd: An absl::CivilDay or a cctz::civil_day
|
||||
// cs: An absl::CivilSecond or a cctz::civil_second
|
||||
// bd: An absl::Time::Breakdown
|
||||
// cl: A cctz::time_zone::civil_lookup
|
||||
// al: A cctz::time_zone::absolute_lookup
|
||||
|
||||
#include "absl/time/time.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <winsock2.h> // for timeval
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
|
||||
#include "absl/time/internal/cctz/include/cctz/civil_time.h"
|
||||
#include "absl/time/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
namespace cctz = absl::time_internal::cctz;
|
||||
|
||||
namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
inline cctz::time_point<cctz::seconds> unix_epoch() {
|
||||
return std::chrono::time_point_cast<cctz::seconds>(
|
||||
std::chrono::system_clock::from_time_t(0));
|
||||
}
|
||||
|
||||
// Floors d to the next unit boundary closer to negative infinity.
|
||||
inline int64_t FloorToUnit(absl::Duration d, absl::Duration unit) {
|
||||
absl::Duration rem;
|
||||
int64_t q = absl::IDivDuration(d, unit, &rem);
|
||||
return (q > 0 || rem >= ZeroDuration() ||
|
||||
q == std::numeric_limits<int64_t>::min())
|
||||
? q
|
||||
: q - 1;
|
||||
}
|
||||
|
||||
ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING
|
||||
inline absl::Time::Breakdown InfiniteFutureBreakdown() {
|
||||
absl::Time::Breakdown bd;
|
||||
bd.year = std::numeric_limits<int64_t>::max();
|
||||
bd.month = 12;
|
||||
bd.day = 31;
|
||||
bd.hour = 23;
|
||||
bd.minute = 59;
|
||||
bd.second = 59;
|
||||
bd.subsecond = absl::InfiniteDuration();
|
||||
bd.weekday = 4;
|
||||
bd.yearday = 365;
|
||||
bd.offset = 0;
|
||||
bd.is_dst = false;
|
||||
bd.zone_abbr = "-00";
|
||||
return bd;
|
||||
}
|
||||
|
||||
inline absl::Time::Breakdown InfinitePastBreakdown() {
|
||||
Time::Breakdown bd;
|
||||
bd.year = std::numeric_limits<int64_t>::min();
|
||||
bd.month = 1;
|
||||
bd.day = 1;
|
||||
bd.hour = 0;
|
||||
bd.minute = 0;
|
||||
bd.second = 0;
|
||||
bd.subsecond = -absl::InfiniteDuration();
|
||||
bd.weekday = 7;
|
||||
bd.yearday = 1;
|
||||
bd.offset = 0;
|
||||
bd.is_dst = false;
|
||||
bd.zone_abbr = "-00";
|
||||
return bd;
|
||||
}
|
||||
ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING
|
||||
|
||||
inline absl::TimeZone::CivilInfo InfiniteFutureCivilInfo() {
|
||||
TimeZone::CivilInfo ci;
|
||||
ci.cs = CivilSecond::max();
|
||||
ci.subsecond = InfiniteDuration();
|
||||
ci.offset = 0;
|
||||
ci.is_dst = false;
|
||||
ci.zone_abbr = "-00";
|
||||
return ci;
|
||||
}
|
||||
|
||||
inline absl::TimeZone::CivilInfo InfinitePastCivilInfo() {
|
||||
TimeZone::CivilInfo ci;
|
||||
ci.cs = CivilSecond::min();
|
||||
ci.subsecond = -InfiniteDuration();
|
||||
ci.offset = 0;
|
||||
ci.is_dst = false;
|
||||
ci.zone_abbr = "-00";
|
||||
return ci;
|
||||
}
|
||||
|
||||
ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING
|
||||
inline absl::TimeConversion InfiniteFutureTimeConversion() {
|
||||
absl::TimeConversion tc;
|
||||
tc.pre = tc.trans = tc.post = absl::InfiniteFuture();
|
||||
tc.kind = absl::TimeConversion::UNIQUE;
|
||||
tc.normalized = true;
|
||||
return tc;
|
||||
}
|
||||
|
||||
inline TimeConversion InfinitePastTimeConversion() {
|
||||
absl::TimeConversion tc;
|
||||
tc.pre = tc.trans = tc.post = absl::InfinitePast();
|
||||
tc.kind = absl::TimeConversion::UNIQUE;
|
||||
tc.normalized = true;
|
||||
return tc;
|
||||
}
|
||||
ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING
|
||||
|
||||
// Makes a Time from sec, overflowing to InfiniteFuture/InfinitePast as
|
||||
// necessary. If sec is min/max, then consult cs+tz to check for overflow.
|
||||
Time MakeTimeWithOverflow(const cctz::time_point<cctz::seconds>& sec,
|
||||
const cctz::civil_second& cs,
|
||||
const cctz::time_zone& tz,
|
||||
bool* normalized = nullptr) {
|
||||
const auto max = cctz::time_point<cctz::seconds>::max();
|
||||
const auto min = cctz::time_point<cctz::seconds>::min();
|
||||
if (sec == max) {
|
||||
const auto al = tz.lookup(max);
|
||||
if (cs > al.cs) {
|
||||
if (normalized) *normalized = true;
|
||||
return absl::InfiniteFuture();
|
||||
}
|
||||
}
|
||||
if (sec == min) {
|
||||
const auto al = tz.lookup(min);
|
||||
if (cs < al.cs) {
|
||||
if (normalized) *normalized = true;
|
||||
return absl::InfinitePast();
|
||||
}
|
||||
}
|
||||
const auto hi = (sec - unix_epoch()).count();
|
||||
return time_internal::FromUnixDuration(time_internal::MakeDuration(hi));
|
||||
}
|
||||
|
||||
// Returns Mon=1..Sun=7.
|
||||
inline int MapWeekday(const cctz::weekday& wd) {
|
||||
switch (wd) {
|
||||
case cctz::weekday::monday:
|
||||
return 1;
|
||||
case cctz::weekday::tuesday:
|
||||
return 2;
|
||||
case cctz::weekday::wednesday:
|
||||
return 3;
|
||||
case cctz::weekday::thursday:
|
||||
return 4;
|
||||
case cctz::weekday::friday:
|
||||
return 5;
|
||||
case cctz::weekday::saturday:
|
||||
return 6;
|
||||
case cctz::weekday::sunday:
|
||||
return 7;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool FindTransition(const cctz::time_zone& tz,
|
||||
bool (cctz::time_zone::*find_transition)(
|
||||
const cctz::time_point<cctz::seconds>& tp,
|
||||
cctz::time_zone::civil_transition* trans) const,
|
||||
Time t, TimeZone::CivilTransition* trans) {
|
||||
// Transitions are second-aligned, so we can discard any fractional part.
|
||||
const auto tp = unix_epoch() + cctz::seconds(ToUnixSeconds(t));
|
||||
cctz::time_zone::civil_transition tr;
|
||||
if (!(tz.*find_transition)(tp, &tr)) return false;
|
||||
trans->from = CivilSecond(tr.from);
|
||||
trans->to = CivilSecond(tr.to);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//
|
||||
// Time
|
||||
//
|
||||
|
||||
ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING
|
||||
absl::Time::Breakdown Time::In(absl::TimeZone tz) const {
|
||||
if (*this == absl::InfiniteFuture()) return InfiniteFutureBreakdown();
|
||||
if (*this == absl::InfinitePast()) return InfinitePastBreakdown();
|
||||
|
||||
const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(rep_));
|
||||
const auto al = cctz::time_zone(tz).lookup(tp);
|
||||
const auto cs = al.cs;
|
||||
const auto cd = cctz::civil_day(cs);
|
||||
|
||||
absl::Time::Breakdown bd;
|
||||
bd.year = cs.year();
|
||||
bd.month = cs.month();
|
||||
bd.day = cs.day();
|
||||
bd.hour = cs.hour();
|
||||
bd.minute = cs.minute();
|
||||
bd.second = cs.second();
|
||||
bd.subsecond = time_internal::MakeDuration(0, time_internal::GetRepLo(rep_));
|
||||
bd.weekday = MapWeekday(cctz::get_weekday(cd));
|
||||
bd.yearday = cctz::get_yearday(cd);
|
||||
bd.offset = al.offset;
|
||||
bd.is_dst = al.is_dst;
|
||||
bd.zone_abbr = al.abbr;
|
||||
return bd;
|
||||
}
|
||||
ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING
|
||||
|
||||
//
|
||||
// Conversions from/to other time types.
|
||||
//
|
||||
|
||||
absl::Time FromUDate(double udate) {
|
||||
return time_internal::FromUnixDuration(absl::Milliseconds(udate));
|
||||
}
|
||||
|
||||
absl::Time FromUniversal(int64_t universal) {
|
||||
return absl::UniversalEpoch() + 100 * absl::Nanoseconds(universal);
|
||||
}
|
||||
|
||||
int64_t ToUnixNanos(Time t) {
|
||||
if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
|
||||
time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 33 == 0) {
|
||||
return (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) *
|
||||
1000 * 1000 * 1000) +
|
||||
(time_internal::GetRepLo(time_internal::ToUnixDuration(t)) / 4);
|
||||
}
|
||||
return FloorToUnit(time_internal::ToUnixDuration(t), absl::Nanoseconds(1));
|
||||
}
|
||||
|
||||
int64_t ToUnixMicros(Time t) {
|
||||
if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
|
||||
time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 43 == 0) {
|
||||
return (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) *
|
||||
1000 * 1000) +
|
||||
(time_internal::GetRepLo(time_internal::ToUnixDuration(t)) / 4000);
|
||||
}
|
||||
return FloorToUnit(time_internal::ToUnixDuration(t), absl::Microseconds(1));
|
||||
}
|
||||
|
||||
int64_t ToUnixMillis(Time t) {
|
||||
if (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >= 0 &&
|
||||
time_internal::GetRepHi(time_internal::ToUnixDuration(t)) >> 53 == 0) {
|
||||
return (time_internal::GetRepHi(time_internal::ToUnixDuration(t)) * 1000) +
|
||||
(time_internal::GetRepLo(time_internal::ToUnixDuration(t)) /
|
||||
(4000 * 1000));
|
||||
}
|
||||
return FloorToUnit(time_internal::ToUnixDuration(t), absl::Milliseconds(1));
|
||||
}
|
||||
|
||||
int64_t ToUnixSeconds(Time t) {
|
||||
return time_internal::GetRepHi(time_internal::ToUnixDuration(t));
|
||||
}
|
||||
|
||||
time_t ToTimeT(Time t) { return absl::ToTimespec(t).tv_sec; }
|
||||
|
||||
double ToUDate(Time t) {
|
||||
return absl::FDivDuration(time_internal::ToUnixDuration(t),
|
||||
absl::Milliseconds(1));
|
||||
}
|
||||
|
||||
int64_t ToUniversal(absl::Time t) {
|
||||
return absl::FloorToUnit(t - absl::UniversalEpoch(), absl::Nanoseconds(100));
|
||||
}
|
||||
|
||||
absl::Time TimeFromTimespec(timespec ts) {
|
||||
return time_internal::FromUnixDuration(absl::DurationFromTimespec(ts));
|
||||
}
|
||||
|
||||
absl::Time TimeFromTimeval(timeval tv) {
|
||||
return time_internal::FromUnixDuration(absl::DurationFromTimeval(tv));
|
||||
}
|
||||
|
||||
timespec ToTimespec(Time t) {
|
||||
timespec ts;
|
||||
absl::Duration d = time_internal::ToUnixDuration(t);
|
||||
if (!time_internal::IsInfiniteDuration(d)) {
|
||||
ts.tv_sec = static_cast<decltype(ts.tv_sec)>(time_internal::GetRepHi(d));
|
||||
if (ts.tv_sec == time_internal::GetRepHi(d)) { // no time_t narrowing
|
||||
ts.tv_nsec = time_internal::GetRepLo(d) / 4; // floor
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
if (d >= absl::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(Time t) {
|
||||
timeval tv;
|
||||
timespec ts = absl::ToTimespec(t);
|
||||
tv.tv_sec = static_cast<decltype(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;
|
||||
}
|
||||
|
||||
Time FromChrono(const std::chrono::system_clock::time_point& tp) {
|
||||
return time_internal::FromUnixDuration(time_internal::FromChrono(
|
||||
tp - std::chrono::system_clock::from_time_t(0)));
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point ToChronoTime(absl::Time t) {
|
||||
using D = std::chrono::system_clock::duration;
|
||||
auto d = time_internal::ToUnixDuration(t);
|
||||
if (d < ZeroDuration()) d = Floor(d, FromChrono(D{1}));
|
||||
return std::chrono::system_clock::from_time_t(0) +
|
||||
time_internal::ToChronoDuration<D>(d);
|
||||
}
|
||||
|
||||
//
|
||||
// TimeZone
|
||||
//
|
||||
|
||||
absl::TimeZone::CivilInfo TimeZone::At(Time t) const {
|
||||
if (t == absl::InfiniteFuture()) return InfiniteFutureCivilInfo();
|
||||
if (t == absl::InfinitePast()) return InfinitePastCivilInfo();
|
||||
|
||||
const auto ud = time_internal::ToUnixDuration(t);
|
||||
const auto tp = unix_epoch() + cctz::seconds(time_internal::GetRepHi(ud));
|
||||
const auto al = cz_.lookup(tp);
|
||||
|
||||
TimeZone::CivilInfo ci;
|
||||
ci.cs = CivilSecond(al.cs);
|
||||
ci.subsecond = time_internal::MakeDuration(0, time_internal::GetRepLo(ud));
|
||||
ci.offset = al.offset;
|
||||
ci.is_dst = al.is_dst;
|
||||
ci.zone_abbr = al.abbr;
|
||||
return ci;
|
||||
}
|
||||
|
||||
absl::TimeZone::TimeInfo TimeZone::At(CivilSecond ct) const {
|
||||
const cctz::civil_second cs(ct);
|
||||
const auto cl = cz_.lookup(cs);
|
||||
|
||||
TimeZone::TimeInfo ti;
|
||||
switch (cl.kind) {
|
||||
case cctz::time_zone::civil_lookup::UNIQUE:
|
||||
ti.kind = TimeZone::TimeInfo::UNIQUE;
|
||||
break;
|
||||
case cctz::time_zone::civil_lookup::SKIPPED:
|
||||
ti.kind = TimeZone::TimeInfo::SKIPPED;
|
||||
break;
|
||||
case cctz::time_zone::civil_lookup::REPEATED:
|
||||
ti.kind = TimeZone::TimeInfo::REPEATED;
|
||||
break;
|
||||
}
|
||||
ti.pre = MakeTimeWithOverflow(cl.pre, cs, cz_);
|
||||
ti.trans = MakeTimeWithOverflow(cl.trans, cs, cz_);
|
||||
ti.post = MakeTimeWithOverflow(cl.post, cs, cz_);
|
||||
return ti;
|
||||
}
|
||||
|
||||
bool TimeZone::NextTransition(Time t, CivilTransition* trans) const {
|
||||
return FindTransition(cz_, &cctz::time_zone::next_transition, t, trans);
|
||||
}
|
||||
|
||||
bool TimeZone::PrevTransition(Time t, CivilTransition* trans) const {
|
||||
return FindTransition(cz_, &cctz::time_zone::prev_transition, t, trans);
|
||||
}
|
||||
|
||||
//
|
||||
// Conversions involving time zones.
|
||||
//
|
||||
ABSL_INTERNAL_DISABLE_DEPRECATED_DECLARATION_WARNING
|
||||
absl::TimeConversion ConvertDateTime(int64_t year, int mon, int day, int hour,
|
||||
int min, int sec, TimeZone tz) {
|
||||
// Avoids years that are too extreme for CivilSecond to normalize.
|
||||
if (year > 300000000000) return InfiniteFutureTimeConversion();
|
||||
if (year < -300000000000) return InfinitePastTimeConversion();
|
||||
|
||||
const CivilSecond cs(year, mon, day, hour, min, sec);
|
||||
const auto ti = tz.At(cs);
|
||||
|
||||
TimeConversion tc;
|
||||
tc.pre = ti.pre;
|
||||
tc.trans = ti.trans;
|
||||
tc.post = ti.post;
|
||||
switch (ti.kind) {
|
||||
case TimeZone::TimeInfo::UNIQUE:
|
||||
tc.kind = TimeConversion::UNIQUE;
|
||||
break;
|
||||
case TimeZone::TimeInfo::SKIPPED:
|
||||
tc.kind = TimeConversion::SKIPPED;
|
||||
break;
|
||||
case TimeZone::TimeInfo::REPEATED:
|
||||
tc.kind = TimeConversion::REPEATED;
|
||||
break;
|
||||
}
|
||||
tc.normalized = false;
|
||||
if (year != cs.year() || mon != cs.month() || day != cs.day() ||
|
||||
hour != cs.hour() || min != cs.minute() || sec != cs.second()) {
|
||||
tc.normalized = true;
|
||||
}
|
||||
return tc;
|
||||
}
|
||||
ABSL_INTERNAL_RESTORE_DEPRECATED_DECLARATION_WARNING
|
||||
|
||||
absl::Time FromTM(const struct tm& tm, absl::TimeZone tz) {
|
||||
civil_year_t tm_year = tm.tm_year;
|
||||
// Avoids years that are too extreme for CivilSecond to normalize.
|
||||
if (tm_year > 300000000000ll) return InfiniteFuture();
|
||||
if (tm_year < -300000000000ll) return InfinitePast();
|
||||
int tm_mon = tm.tm_mon;
|
||||
if (tm_mon == std::numeric_limits<int>::max()) {
|
||||
tm_mon -= 12;
|
||||
tm_year += 1;
|
||||
}
|
||||
const auto ti = tz.At(CivilSecond(tm_year + 1900, tm_mon + 1, tm.tm_mday,
|
||||
tm.tm_hour, tm.tm_min, tm.tm_sec));
|
||||
return tm.tm_isdst == 0 ? ti.post : ti.pre;
|
||||
}
|
||||
|
||||
struct tm ToTM(absl::Time t, absl::TimeZone tz) {
|
||||
struct tm tm = {};
|
||||
|
||||
const auto ci = tz.At(t);
|
||||
const auto& cs = ci.cs;
|
||||
tm.tm_sec = cs.second();
|
||||
tm.tm_min = cs.minute();
|
||||
tm.tm_hour = cs.hour();
|
||||
tm.tm_mday = cs.day();
|
||||
tm.tm_mon = cs.month() - 1;
|
||||
|
||||
// Saturates tm.tm_year in cases of over/underflow, accounting for the fact
|
||||
// that tm.tm_year is years since 1900.
|
||||
if (cs.year() < std::numeric_limits<int>::min() + 1900) {
|
||||
tm.tm_year = std::numeric_limits<int>::min();
|
||||
} else if (cs.year() > std::numeric_limits<int>::max()) {
|
||||
tm.tm_year = std::numeric_limits<int>::max() - 1900;
|
||||
} else {
|
||||
tm.tm_year = static_cast<int>(cs.year() - 1900);
|
||||
}
|
||||
|
||||
switch (GetWeekday(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 = GetYearDay(cs) - 1;
|
||||
tm.tm_isdst = ci.is_dst ? 1 : 0;
|
||||
|
||||
return tm;
|
||||
}
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
1920
TMessagesProj/jni/voip/webrtc/absl/time/time.h
Normal file
1920
TMessagesProj/jni/voip/webrtc/absl/time/time.h
Normal file
File diff suppressed because it is too large
Load diff
321
TMessagesProj/jni/voip/webrtc/absl/time/time_benchmark.cc
Normal file
321
TMessagesProj/jni/voip/webrtc/absl/time/time_benchmark.cc
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
// 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/time.h"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <sys/time.h>
|
||||
#endif // _WIN32
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/internal/test_util.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
namespace {
|
||||
|
||||
//
|
||||
// Addition/Subtraction of a duration
|
||||
//
|
||||
|
||||
void BM_Time_Arithmetic(benchmark::State& state) {
|
||||
const absl::Duration nano = absl::Nanoseconds(1);
|
||||
const absl::Duration sec = absl::Seconds(1);
|
||||
absl::Time t = absl::UnixEpoch();
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(t += nano);
|
||||
benchmark::DoNotOptimize(t -= sec);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_Arithmetic);
|
||||
|
||||
//
|
||||
// Time difference
|
||||
//
|
||||
|
||||
void BM_Time_Difference(benchmark::State& state) {
|
||||
absl::Time start = absl::Now();
|
||||
absl::Time end = start + absl::Nanoseconds(1);
|
||||
absl::Duration diff;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(diff += end - start);
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_Difference);
|
||||
|
||||
//
|
||||
// ToDateTime
|
||||
//
|
||||
// In each "ToDateTime" benchmark we switch between two instants
|
||||
// separated by at least one transition in order to defeat any
|
||||
// internal caching of previous results (e.g., see local_time_hint_).
|
||||
//
|
||||
// The "UTC" variants use UTC instead of the Google/local time zone.
|
||||
//
|
||||
|
||||
void BM_Time_ToDateTime_Absl(benchmark::State& state) {
|
||||
const absl::TimeZone tz =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
absl::Time t = absl::FromUnixSeconds(1384569027);
|
||||
absl::Time t2 = absl::FromUnixSeconds(1418962578);
|
||||
while (state.KeepRunning()) {
|
||||
std::swap(t, t2);
|
||||
t += absl::Seconds(1);
|
||||
benchmark::DoNotOptimize(t.In(tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToDateTime_Absl);
|
||||
|
||||
void BM_Time_ToDateTime_Libc(benchmark::State& state) {
|
||||
// No timezone support, so just use localtime.
|
||||
time_t t = 1384569027;
|
||||
time_t t2 = 1418962578;
|
||||
while (state.KeepRunning()) {
|
||||
std::swap(t, t2);
|
||||
t += 1;
|
||||
struct tm tm;
|
||||
#if !defined(_WIN32)
|
||||
benchmark::DoNotOptimize(localtime_r(&t, &tm));
|
||||
#else // _WIN32
|
||||
benchmark::DoNotOptimize(localtime_s(&tm, &t));
|
||||
#endif // _WIN32
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToDateTime_Libc);
|
||||
|
||||
void BM_Time_ToDateTimeUTC_Absl(benchmark::State& state) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
absl::Time t = absl::FromUnixSeconds(1384569027);
|
||||
while (state.KeepRunning()) {
|
||||
t += absl::Seconds(1);
|
||||
benchmark::DoNotOptimize(t.In(tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToDateTimeUTC_Absl);
|
||||
|
||||
void BM_Time_ToDateTimeUTC_Libc(benchmark::State& state) {
|
||||
time_t t = 1384569027;
|
||||
while (state.KeepRunning()) {
|
||||
t += 1;
|
||||
struct tm tm;
|
||||
#if !defined(_WIN32)
|
||||
benchmark::DoNotOptimize(gmtime_r(&t, &tm));
|
||||
#else // _WIN32
|
||||
benchmark::DoNotOptimize(gmtime_s(&tm, &t));
|
||||
#endif // _WIN32
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToDateTimeUTC_Libc);
|
||||
|
||||
//
|
||||
// FromUnixMicros
|
||||
//
|
||||
|
||||
void BM_Time_FromUnixMicros(benchmark::State& state) {
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::FromUnixMicros(i));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromUnixMicros);
|
||||
|
||||
void BM_Time_ToUnixNanos(benchmark::State& state) {
|
||||
const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(ToUnixNanos(t));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToUnixNanos);
|
||||
|
||||
void BM_Time_ToUnixMicros(benchmark::State& state) {
|
||||
const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(ToUnixMicros(t));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToUnixMicros);
|
||||
|
||||
void BM_Time_ToUnixMillis(benchmark::State& state) {
|
||||
const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(ToUnixMillis(t));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToUnixMillis);
|
||||
|
||||
void BM_Time_ToUnixSeconds(benchmark::State& state) {
|
||||
const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToUnixSeconds(t));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToUnixSeconds);
|
||||
|
||||
//
|
||||
// FromCivil
|
||||
//
|
||||
// In each "FromCivil" benchmark we switch between two YMDhms values
|
||||
// separated by at least one transition in order to defeat any internal
|
||||
// caching of previous results (e.g., see time_local_hint_).
|
||||
//
|
||||
// The "UTC" variants use UTC instead of the Google/local time zone.
|
||||
// The "Day0" variants require normalization of the day of month.
|
||||
//
|
||||
|
||||
void BM_Time_FromCivil_Absl(benchmark::State& state) {
|
||||
const absl::TimeZone tz =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
if ((i & 1) == 0) {
|
||||
benchmark::DoNotOptimize(
|
||||
absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz));
|
||||
} else {
|
||||
benchmark::DoNotOptimize(
|
||||
absl::FromCivil(absl::CivilSecond(2013, 11, 15, 18, 30, 27), tz));
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivil_Absl);
|
||||
|
||||
void BM_Time_FromCivil_Libc(benchmark::State& state) {
|
||||
// No timezone support, so just use localtime.
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
struct tm tm;
|
||||
if ((i & 1) == 0) {
|
||||
tm.tm_year = 2014 - 1900;
|
||||
tm.tm_mon = 12 - 1;
|
||||
tm.tm_mday = 18;
|
||||
tm.tm_hour = 20;
|
||||
tm.tm_min = 16;
|
||||
tm.tm_sec = 18;
|
||||
} else {
|
||||
tm.tm_year = 2013 - 1900;
|
||||
tm.tm_mon = 11 - 1;
|
||||
tm.tm_mday = 15;
|
||||
tm.tm_hour = 18;
|
||||
tm.tm_min = 30;
|
||||
tm.tm_sec = 27;
|
||||
}
|
||||
tm.tm_isdst = -1;
|
||||
mktime(&tm);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivil_Libc);
|
||||
|
||||
void BM_Time_FromCivilUTC_Absl(benchmark::State& state) {
|
||||
const absl::TimeZone tz = absl::UTCTimeZone();
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(
|
||||
absl::FromCivil(absl::CivilSecond(2014, 12, 18, 20, 16, 18), tz));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivilUTC_Absl);
|
||||
|
||||
void BM_Time_FromCivilDay0_Absl(benchmark::State& state) {
|
||||
const absl::TimeZone tz =
|
||||
absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
if ((i & 1) == 0) {
|
||||
benchmark::DoNotOptimize(
|
||||
absl::FromCivil(absl::CivilSecond(2014, 12, 0, 20, 16, 18), tz));
|
||||
} else {
|
||||
benchmark::DoNotOptimize(
|
||||
absl::FromCivil(absl::CivilSecond(2013, 11, 0, 18, 30, 27), tz));
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivilDay0_Absl);
|
||||
|
||||
void BM_Time_FromCivilDay0_Libc(benchmark::State& state) {
|
||||
// No timezone support, so just use localtime.
|
||||
int i = 0;
|
||||
while (state.KeepRunning()) {
|
||||
struct tm tm;
|
||||
if ((i & 1) == 0) {
|
||||
tm.tm_year = 2014 - 1900;
|
||||
tm.tm_mon = 12 - 1;
|
||||
tm.tm_mday = 0;
|
||||
tm.tm_hour = 20;
|
||||
tm.tm_min = 16;
|
||||
tm.tm_sec = 18;
|
||||
} else {
|
||||
tm.tm_year = 2013 - 1900;
|
||||
tm.tm_mon = 11 - 1;
|
||||
tm.tm_mday = 0;
|
||||
tm.tm_hour = 18;
|
||||
tm.tm_min = 30;
|
||||
tm.tm_sec = 27;
|
||||
}
|
||||
tm.tm_isdst = -1;
|
||||
mktime(&tm);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromCivilDay0_Libc);
|
||||
|
||||
//
|
||||
// To/FromTimespec
|
||||
//
|
||||
|
||||
void BM_Time_ToTimespec(benchmark::State& state) {
|
||||
absl::Time now = absl::Now();
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::ToTimespec(now));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_ToTimespec);
|
||||
|
||||
void BM_Time_FromTimespec(benchmark::State& state) {
|
||||
timespec ts = absl::ToTimespec(absl::Now());
|
||||
while (state.KeepRunning()) {
|
||||
if (++ts.tv_nsec == 1000 * 1000 * 1000) {
|
||||
++ts.tv_sec;
|
||||
ts.tv_nsec = 0;
|
||||
}
|
||||
benchmark::DoNotOptimize(absl::TimeFromTimespec(ts));
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_FromTimespec);
|
||||
|
||||
//
|
||||
// Comparison with InfiniteFuture/Past
|
||||
//
|
||||
|
||||
void BM_Time_InfiniteFuture(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::InfiniteFuture());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_InfiniteFuture);
|
||||
|
||||
void BM_Time_InfinitePast(benchmark::State& state) {
|
||||
while (state.KeepRunning()) {
|
||||
benchmark::DoNotOptimize(absl::InfinitePast());
|
||||
}
|
||||
}
|
||||
BENCHMARK(BM_Time_InfinitePast);
|
||||
|
||||
} // namespace
|
||||
1338
TMessagesProj/jni/voip/webrtc/absl/time/time_test.cc
Normal file
1338
TMessagesProj/jni/voip/webrtc/absl/time/time_test.cc
Normal file
File diff suppressed because it is too large
Load diff
97
TMessagesProj/jni/voip/webrtc/absl/time/time_zone_test.cc
Normal file
97
TMessagesProj/jni/voip/webrtc/absl/time/time_zone_test.cc
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// 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/internal/cctz/include/cctz/time_zone.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/internal/test_util.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
namespace cctz = absl::time_internal::cctz;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(TimeZone, ValueSemantics) {
|
||||
absl::TimeZone tz;
|
||||
absl::TimeZone tz2 = tz; // Copy-construct
|
||||
EXPECT_EQ(tz, tz2);
|
||||
tz2 = tz; // Copy-assign
|
||||
EXPECT_EQ(tz, tz2);
|
||||
}
|
||||
|
||||
TEST(TimeZone, Equality) {
|
||||
absl::TimeZone a, b;
|
||||
EXPECT_EQ(a, b);
|
||||
EXPECT_EQ(a.name(), b.name());
|
||||
|
||||
absl::TimeZone implicit_utc;
|
||||
absl::TimeZone explicit_utc = absl::UTCTimeZone();
|
||||
EXPECT_EQ(implicit_utc, explicit_utc);
|
||||
EXPECT_EQ(implicit_utc.name(), explicit_utc.name());
|
||||
|
||||
absl::TimeZone la = absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
absl::TimeZone nyc = absl::time_internal::LoadTimeZone("America/New_York");
|
||||
EXPECT_NE(la, nyc);
|
||||
}
|
||||
|
||||
TEST(TimeZone, CCTZConversion) {
|
||||
const cctz::time_zone cz = cctz::utc_time_zone();
|
||||
const absl::TimeZone tz(cz);
|
||||
EXPECT_EQ(cz, cctz::time_zone(tz));
|
||||
}
|
||||
|
||||
TEST(TimeZone, DefaultTimeZones) {
|
||||
absl::TimeZone tz;
|
||||
EXPECT_EQ("UTC", absl::TimeZone().name());
|
||||
EXPECT_EQ("UTC", absl::UTCTimeZone().name());
|
||||
}
|
||||
|
||||
TEST(TimeZone, FixedTimeZone) {
|
||||
const absl::TimeZone tz = absl::FixedTimeZone(123);
|
||||
const cctz::time_zone cz = cctz::fixed_time_zone(cctz::seconds(123));
|
||||
EXPECT_EQ(tz, absl::TimeZone(cz));
|
||||
}
|
||||
|
||||
TEST(TimeZone, LocalTimeZone) {
|
||||
const absl::TimeZone local_tz = absl::LocalTimeZone();
|
||||
absl::TimeZone tz = absl::time_internal::LoadTimeZone("localtime");
|
||||
EXPECT_EQ(tz, local_tz);
|
||||
}
|
||||
|
||||
TEST(TimeZone, NamedTimeZones) {
|
||||
absl::TimeZone nyc = absl::time_internal::LoadTimeZone("America/New_York");
|
||||
EXPECT_EQ("America/New_York", nyc.name());
|
||||
absl::TimeZone syd = absl::time_internal::LoadTimeZone("Australia/Sydney");
|
||||
EXPECT_EQ("Australia/Sydney", syd.name());
|
||||
absl::TimeZone fixed = absl::FixedTimeZone((((3 * 60) + 25) * 60) + 45);
|
||||
EXPECT_EQ("Fixed/UTC+03:25:45", fixed.name());
|
||||
}
|
||||
|
||||
TEST(TimeZone, Failures) {
|
||||
absl::TimeZone tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
EXPECT_FALSE(LoadTimeZone("Invalid/TimeZone", &tz));
|
||||
EXPECT_EQ(absl::UTCTimeZone(), tz); // guaranteed fallback to UTC
|
||||
|
||||
// Ensures that the load still fails on a subsequent attempt.
|
||||
tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
EXPECT_FALSE(LoadTimeZone("Invalid/TimeZone", &tz));
|
||||
EXPECT_EQ(absl::UTCTimeZone(), tz); // guaranteed fallback to UTC
|
||||
|
||||
// Loading an empty string timezone should fail.
|
||||
tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
|
||||
EXPECT_FALSE(LoadTimeZone("", &tz));
|
||||
EXPECT_EQ(absl::UTCTimeZone(), tz); // guaranteed fallback to UTC
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Loading…
Add table
Add a link
Reference in a new issue