about summary refs log tree commit diff
path: root/absl
diff options
context:
space:
mode:
Diffstat (limited to 'absl')
-rw-r--r--absl/hash/hash_testing.h26
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc14
-rw-r--r--absl/time/time.cc22
-rw-r--r--absl/time/time.h40
-rw-r--r--absl/time/time_test.cc63
5 files changed, 146 insertions, 19 deletions
diff --git a/absl/hash/hash_testing.h b/absl/hash/hash_testing.h
index 1e3cda64467d..52bcb55a20f5 100644
--- a/absl/hash/hash_testing.h
+++ b/absl/hash/hash_testing.h
@@ -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>
diff --git a/absl/time/internal/cctz/src/time_zone_lookup_test.cc b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
index 280c96b4f207..f28e7f853b69 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -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) {
diff --git a/absl/time/time.cc b/absl/time/time.cc
index 0703856fa327..ac2c8a8356f2 100644
--- a/absl/time/time.cc
+++ b/absl/time/time.cc
@@ -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.
 //
diff --git a/absl/time/time.h b/absl/time/time.h
index 2858da2955f6..7d5c3fde7a61 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -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_);
diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc
index feca45873814..4d71070925f5 100644
--- a/absl/time/time_test.cc
+++ b/absl/time/time_test.cc
@@ -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