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
pull/194/head
Abseil Team 6 years ago committed by Xiaoyi Zhang
parent a00bdd176d
commit 5b70a8910b
  1. 47
      CONTRIBUTING.md
  2. 26
      absl/hash/hash_testing.h
  3. 14
      absl/time/internal/cctz/src/time_zone_lookup_test.cc
  4. 22
      absl/time/time.cc
  5. 40
      absl/time/time.h
  6. 63
      absl/time/time_test.cc

@ -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…
Cancel
Save