Export of internal Abseil changes.
-- f4e870453d02106c2685e0461816469a4704ad25 by Abseil Team <absl-team@google.com>: Expose TimeZone::NextTransition() and PrevTransition() now that we have absl::CivilSecond support in time.h. Note that these are for informational purposes only. General time code should not care when offset changes occur. PiperOrigin-RevId: 217177292 -- cfadd275c7333f7c27c4d682b9d167010d874e69 by Abseil Team <absl-team@google.com>: Import of CCTZ from GitHub. PiperOrigin-RevId: 217153577 -- 6ff5b8c61a1239b9c0478a7c62bcd2844b310307 by Jon Cohen <cohenjon@google.com>: Fix code examples in hash_testing.h. Includes random clang-format changes. PiperOrigin-RevId: 216898995 -- de124129d27f4627dabe193a10bf106a11783fba by Shaindel Schwartz <shaindel@google.com>: Add contribution guidelines describing how we decide whether to include an API in Abseil. PiperOrigin-RevId: 216886943 GitOrigin-RevId: f4e870453d02106c2685e0461816469a4704ad25 Change-Id: Ib9c6706f5bf931b71c0357bf1342053a3bee8ff7
This commit is contained in:
parent
a00bdd176d
commit
5b70a8910b
6 changed files with 193 additions and 19 deletions
|
@ -18,6 +18,53 @@ You generally only need to submit a CLA once, so if you've already submitted one
|
|||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
## Contribution Guidelines
|
||||
|
||||
Potential contributors sometimes ask us if the Abseil project is the appropriate
|
||||
home for their utility library code or for specific functions implementing
|
||||
missing portions of the standard. Often, the answer to this question is "no".
|
||||
We’d like to articulate our thinking on this issue so that our choices can be
|
||||
understood by everyone and so that contributors can have a better intuition
|
||||
about whether Abseil might be interested in adopting a new library.
|
||||
|
||||
### Priorities
|
||||
|
||||
Although our mission is to augment the C++ standard library, our goal is not to
|
||||
provide a full forward-compatible implementation of the latest standard. For us
|
||||
to consider a library for inclusion in Abseil, it is not enough that a library
|
||||
is useful. We generally choose to release a library when it meets at least one
|
||||
of the following criteria:
|
||||
|
||||
* **Widespread usage** - Using our internal codebase to help gauge usage, most
|
||||
of the libraries we've released have tens of thousands of users.
|
||||
* **Anticipated widespread usage** - Pre-adoption of some standard-compliant
|
||||
APIs may not have broad adoption initially but can be expected to pick up
|
||||
usage when it replaces legacy APIs. `absl::from_chars`, for example,
|
||||
replaces existing code that converts strings to numbers and will therefore
|
||||
likely see usage growth.
|
||||
* **High impact** - APIs that provide a key solution to a specific problem,
|
||||
such as `absl::FixedArray`, have higher impact than usage numbers may signal
|
||||
and are released because of their importance.
|
||||
* **Direct support for a library that falls under one of the above** - When we
|
||||
want access to a smaller library as an implementation detail for a
|
||||
higher-priority library we plan to release, we may release it, as we did
|
||||
with portions of `absl/meta/type_traits.h`. One consequence of this is that
|
||||
the presence of a library in Abseil does not necessarily mean that other
|
||||
similar libraries would be a high priority.
|
||||
|
||||
### API Freeze Consequences
|
||||
|
||||
Via the
|
||||
[Abseil Compatibility Guidelines](https://abseil.io/about/compatibility), we
|
||||
have promised a large degree of API stability. In particular, we will not make
|
||||
backward-incompatible changes to released APIs without also shipping a tool or
|
||||
process that can upgrade our users' code. We are not yet at the point of easily
|
||||
releasing such tools. Therefore, at this time, shipping a library establishes an
|
||||
API contract which is borderline unchangeable. (We can add new functionality,
|
||||
but we cannot easily change existing behavior.) This constraint forces us to
|
||||
very carefully review all APIs that we ship.
|
||||
|
||||
|
||||
## Coding Style
|
||||
|
||||
To keep the source consistent, readable, diffable and easy to merge, we use a
|
||||
|
|
|
@ -90,7 +90,7 @@ namespace absl {
|
|||
// template <typename H>
|
||||
// friend H AbslHashValue(H state, Bad2 x) {
|
||||
// // Uses a and b.
|
||||
// return H::combine(x.a, x.b);
|
||||
// return H::combine(std::move(state), x.a, x.b);
|
||||
// }
|
||||
// friend bool operator==(Bad2 x, Bad2 y) {
|
||||
// // Only uses a.
|
||||
|
@ -107,7 +107,7 @@ namespace absl {
|
|||
// template <typename H>
|
||||
// friend H AbslHashValue(H state, Bad3 x) {
|
||||
// // Only uses a.
|
||||
// return H::combine(x.a);
|
||||
// return H::combine(std::move(state), x.a);
|
||||
// }
|
||||
// friend bool operator==(Bad3 x, Bad3 y) {
|
||||
// // Uses a and b.
|
||||
|
@ -123,19 +123,21 @@ namespace absl {
|
|||
// int *p, size;
|
||||
// template <typename H>
|
||||
// friend H AbslHashValue(H state, Bad4 x) {
|
||||
// return H::combine_range(x.p, x.p + x.size);
|
||||
// return H::combine_contiguous(std::move(state), x.p, x.p + x.size);
|
||||
// }
|
||||
// friend bool operator==(Bad4 x, Bad4 y) {
|
||||
// return std::equal(x.p, x.p + x.size, y.p, y.p + y.size);
|
||||
// // Compare two ranges for equality. C++14 code can instead use std::equal.
|
||||
// return absl::equal(x.p, x.p + x.size, y.p, y.p + y.size);
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// An easy solution to this is to combine the size after combining the range,
|
||||
// like so:
|
||||
// template <typename H>
|
||||
// friend H AbslHashValue(H state, Bad4 x) {
|
||||
// return H::combine(H::combine_range(x.p, x.p + x.size), x.size);
|
||||
// }
|
||||
// template <typename H>
|
||||
// friend H AbslHashValue(H state, Bad4 x) {
|
||||
// return H::combine(
|
||||
// H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size);
|
||||
// }
|
||||
//
|
||||
template <int&... ExplicitBarrier, typename Container>
|
||||
ABSL_MUST_USE_RESULT testing::AssertionResult
|
||||
|
@ -227,7 +229,8 @@ VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
|
|||
// Now we verify that AbslHashValue is also correctly implemented.
|
||||
|
||||
for (const auto& c : classes) {
|
||||
// All elements of the equivalence class must have the same hash expansion.
|
||||
// All elements of the equivalence class must have the same hash
|
||||
// expansion.
|
||||
const SpyHashState expected = c[0].expand();
|
||||
for (const Info& v : c) {
|
||||
if (v.expand() != v.expand()) {
|
||||
|
@ -285,7 +288,7 @@ struct TypeSet {
|
|||
};
|
||||
|
||||
template <typename... T>
|
||||
struct MakeTypeSet : TypeSet<>{};
|
||||
struct MakeTypeSet : TypeSet<> {};
|
||||
template <typename T, typename... Ts>
|
||||
struct MakeTypeSet<T, Ts...> : MakeTypeSet<Ts...>::template Insert<T>::type {};
|
||||
|
||||
|
@ -346,8 +349,7 @@ template <int&..., typename Container, typename Eq>
|
|||
ABSL_MUST_USE_RESULT testing::AssertionResult
|
||||
VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
|
||||
return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
|
||||
hash_internal::ContainerAsVector<Container>::Do(values),
|
||||
equals);
|
||||
hash_internal::ContainerAsVector<Container>::Do(values), equals);
|
||||
}
|
||||
|
||||
template <int&..., typename T>
|
||||
|
|
|
@ -991,15 +991,17 @@ TEST(MakeTime, SysSecondsLimits) {
|
|||
tp = convert(civil_second::min(), west);
|
||||
EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp);
|
||||
|
||||
// Checks that "tm_year + 1900", as used by the "libc" implementation,
|
||||
// can produce year values beyond the range on an int without overflow.
|
||||
if (sizeof(std::time_t) >= 8) {
|
||||
// Checks that "tm_year + 1900", as used by the "libc" implementation,
|
||||
// can produce year values beyond the range on an int without overflow.
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
// localtime_s() and gmtime_s() don't believe in years past 3000.
|
||||
// localtime_s() and gmtime_s() don't believe in years past 3000.
|
||||
#else
|
||||
const time_zone libc_utc = LoadZone("libc:UTC");
|
||||
tp = convert(civil_year(year_t{2147483648}), libc_utc);
|
||||
EXPECT_EQ("2147483648-01-01T00:00:00+00:00", format(RFC3339, tp, libc_utc));
|
||||
const time_zone libc_utc = LoadZone("libc:UTC");
|
||||
tp = convert(civil_year(year_t{2147483648}), libc_utc);
|
||||
EXPECT_EQ("2147483648-01-01T00:00:00+00:00", format(RFC3339, tp, libc_utc));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NextTransition, UTC) {
|
||||
|
|
|
@ -176,6 +176,20 @@ inline int MapWeekday(const cctz::weekday& wd) {
|
|||
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
|
||||
|
||||
//
|
||||
|
@ -366,6 +380,14 @@ absl::TimeZone::TimeInfo TimeZone::At(CivilSecond ct) const {
|
|||
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.
|
||||
//
|
||||
|
|
|
@ -886,7 +886,7 @@ class TimeZone {
|
|||
struct TimeInfo {
|
||||
enum CivilKind {
|
||||
UNIQUE, // the civil time was singular (pre == trans == post)
|
||||
SKIPPED, // the civil time did not exist (pre => trans > post)
|
||||
SKIPPED, // the civil time did not exist (pre >= trans > post)
|
||||
REPEATED, // the civil time was ambiguous (pre < trans <= post)
|
||||
} kind;
|
||||
Time pre; // time calculated using the pre-transition offset
|
||||
|
@ -925,6 +925,44 @@ class TimeZone {
|
|||
// // nov06.post is 2011-11-06 01:15:00 -0800
|
||||
TimeInfo At(CivilSecond ct) const;
|
||||
|
||||
// TimeZone::NextTransition()
|
||||
// TimeZone::PrevTransition()
|
||||
//
|
||||
// Finds the time of the next/previous offset change in this time zone.
|
||||
//
|
||||
// By definition, `NextTransition(t, &trans)` returns false when `t` is
|
||||
// `InfiniteFuture()`, and `PrevTransition(t, &trans)` returns false
|
||||
// when `t` is `InfinitePast()`. If the zone has no transitions, the
|
||||
// result will also be false no matter what the argument.
|
||||
//
|
||||
// Otherwise, when `t` is `InfinitePast()`, `NextTransition(t, &trans)`
|
||||
// returns true and sets `trans` to the first recorded transition. Chains
|
||||
// of calls to `NextTransition()/PrevTransition()` will eventually return
|
||||
// false, but it is unspecified exactly when `NextTransition(t, &trans)`
|
||||
// jumps to false, or what time is set by `PrevTransition(t, &trans)` for
|
||||
// a very distant `t`.
|
||||
//
|
||||
// Note: Enumeration of time-zone transitions is for informational purposes
|
||||
// only. Modern time-related code should not care about when offset changes
|
||||
// occur.
|
||||
//
|
||||
// Example:
|
||||
// absl::TimeZone nyc;
|
||||
// if (!absl::LoadTimeZone("America/New_York", &nyc)) { ... }
|
||||
// const auto now = absl::Now();
|
||||
// auto t = absl::InfinitePast();
|
||||
// absl::TimeZone::CivilTransition trans;
|
||||
// while (t <= now && nyc.NextTransition(t, &trans)) {
|
||||
// // transition: trans.from -> trans.to
|
||||
// t = nyc.At(trans.to).trans;
|
||||
// }
|
||||
struct CivilTransition {
|
||||
CivilSecond from; // the civil time we jump from
|
||||
CivilSecond to; // the civil time we jump to
|
||||
};
|
||||
bool NextTransition(Time t, CivilTransition* trans) const;
|
||||
bool PrevTransition(Time t, CivilTransition* trans) const;
|
||||
|
||||
template <typename H>
|
||||
friend H AbslHashValue(H h, TimeZone tz) {
|
||||
return H::combine(std::move(h), tz.cz_);
|
||||
|
|
|
@ -1135,4 +1135,67 @@ TEST(Time, LegacyDateTime) {
|
|||
EXPECT_EQ("2014-10-29 22:58:59", absl::FormatTime(ymdhms, t, utc));
|
||||
}
|
||||
|
||||
TEST(Time, NextTransitionUTC) {
|
||||
const auto tz = absl::UTCTimeZone();
|
||||
absl::TimeZone::CivilTransition trans;
|
||||
|
||||
auto t = absl::InfinitePast();
|
||||
EXPECT_FALSE(tz.NextTransition(t, &trans));
|
||||
|
||||
t = absl::InfiniteFuture();
|
||||
EXPECT_FALSE(tz.NextTransition(t, &trans));
|
||||
}
|
||||
|
||||
TEST(Time, PrevTransitionUTC) {
|
||||
const auto tz = absl::UTCTimeZone();
|
||||
absl::TimeZone::CivilTransition trans;
|
||||
|
||||
auto t = absl::InfiniteFuture();
|
||||
EXPECT_FALSE(tz.PrevTransition(t, &trans));
|
||||
|
||||
t = absl::InfinitePast();
|
||||
EXPECT_FALSE(tz.PrevTransition(t, &trans));
|
||||
}
|
||||
|
||||
TEST(Time, NextTransitionNYC) {
|
||||
const auto tz = absl::time_internal::LoadTimeZone("America/New_York");
|
||||
absl::TimeZone::CivilTransition trans;
|
||||
|
||||
auto t = absl::FromCivil(absl::CivilSecond(2018, 6, 30, 0, 0, 0), tz);
|
||||
EXPECT_TRUE(tz.NextTransition(t, &trans));
|
||||
EXPECT_EQ(absl::CivilSecond(2018, 11, 4, 2, 0, 0), trans.from);
|
||||
EXPECT_EQ(absl::CivilSecond(2018, 11, 4, 1, 0, 0), trans.to);
|
||||
|
||||
t = absl::InfiniteFuture();
|
||||
EXPECT_FALSE(tz.NextTransition(t, &trans));
|
||||
|
||||
t = absl::InfinitePast();
|
||||
EXPECT_TRUE(tz.NextTransition(t, &trans));
|
||||
if (trans.from == absl::CivilSecond(1918, 03, 31, 2, 0, 0)) {
|
||||
// It looks like the tzdata is only 32 bit (probably macOS),
|
||||
// which bottoms out at 1901-12-13T20:45:52+00:00.
|
||||
EXPECT_EQ(absl::CivilSecond(1918, 3, 31, 3, 0, 0), trans.to);
|
||||
} else {
|
||||
EXPECT_EQ(absl::CivilSecond(1883, 11, 18, 12, 3, 58), trans.from);
|
||||
EXPECT_EQ(absl::CivilSecond(1883, 11, 18, 12, 0, 0), trans.to);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Time, PrevTransitionNYC) {
|
||||
const auto tz = absl::time_internal::LoadTimeZone("America/New_York");
|
||||
absl::TimeZone::CivilTransition trans;
|
||||
|
||||
auto t = absl::FromCivil(absl::CivilSecond(2018, 6, 30, 0, 0, 0), tz);
|
||||
EXPECT_TRUE(tz.PrevTransition(t, &trans));
|
||||
EXPECT_EQ(absl::CivilSecond(2018, 3, 11, 2, 0, 0), trans.from);
|
||||
EXPECT_EQ(absl::CivilSecond(2018, 3, 11, 3, 0, 0), trans.to);
|
||||
|
||||
t = absl::InfinitePast();
|
||||
EXPECT_FALSE(tz.PrevTransition(t, &trans));
|
||||
|
||||
t = absl::InfiniteFuture();
|
||||
EXPECT_TRUE(tz.PrevTransition(t, &trans));
|
||||
// We have a transition but we don't know which one.
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in a new issue