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 | (even if it was for a different project), you probably don't need to do it | ||||||
| again. | 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 | ## Coding Style | ||||||
| 
 | 
 | ||||||
| To keep the source consistent, readable, diffable and easy to merge, we use a | To keep the source consistent, readable, diffable and easy to merge, we use a | ||||||
|  |  | ||||||
|  | @ -90,7 +90,7 @@ namespace absl { | ||||||
| //   template <typename H>
 | //   template <typename H>
 | ||||||
| //   friend H AbslHashValue(H state, Bad2 x) {
 | //   friend H AbslHashValue(H state, Bad2 x) {
 | ||||||
| //     // Uses a and b.
 | //     // 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) {
 | //   friend bool operator==(Bad2 x, Bad2 y) {
 | ||||||
| //     // Only uses a.
 | //     // Only uses a.
 | ||||||
|  | @ -107,7 +107,7 @@ namespace absl { | ||||||
| //   template <typename H>
 | //   template <typename H>
 | ||||||
| //   friend H AbslHashValue(H state, Bad3 x) {
 | //   friend H AbslHashValue(H state, Bad3 x) {
 | ||||||
| //     // Only uses a.
 | //     // Only uses a.
 | ||||||
| //     return H::combine(x.a);
 | //     return H::combine(std::move(state), x.a);
 | ||||||
| //   }
 | //   }
 | ||||||
| //   friend bool operator==(Bad3 x, Bad3 y) {
 | //   friend bool operator==(Bad3 x, Bad3 y) {
 | ||||||
| //     // Uses a and b.
 | //     // Uses a and b.
 | ||||||
|  | @ -123,19 +123,21 @@ namespace absl { | ||||||
| //   int *p, size;
 | //   int *p, size;
 | ||||||
| //   template <typename H>
 | //   template <typename H>
 | ||||||
| //   friend H AbslHashValue(H state, Bad4 x) {
 | //   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) {
 | //   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,
 | // An easy solution to this is to combine the size after combining the range,
 | ||||||
| // like so:
 | // like so:
 | ||||||
| //   template <typename H>
 | // template <typename H>
 | ||||||
| //   friend H AbslHashValue(H state, Bad4 x) {
 | // friend H AbslHashValue(H state, Bad4 x) {
 | ||||||
| //     return H::combine(H::combine_range(x.p, x.p + x.size), x.size);
 | //   return H::combine(
 | ||||||
| //   }
 | //       H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size);
 | ||||||
|  | // }
 | ||||||
| //
 | //
 | ||||||
| template <int&... ExplicitBarrier, typename Container> | template <int&... ExplicitBarrier, typename Container> | ||||||
| ABSL_MUST_USE_RESULT testing::AssertionResult | 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.
 |   // Now we verify that AbslHashValue is also correctly implemented.
 | ||||||
| 
 | 
 | ||||||
|   for (const auto& c : classes) { |   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(); |     const SpyHashState expected = c[0].expand(); | ||||||
|     for (const Info& v : c) { |     for (const Info& v : c) { | ||||||
|       if (v.expand() != v.expand()) { |       if (v.expand() != v.expand()) { | ||||||
|  | @ -285,7 +288,7 @@ struct TypeSet { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename... T> | template <typename... T> | ||||||
| struct MakeTypeSet : TypeSet<>{}; | struct MakeTypeSet : TypeSet<> {}; | ||||||
| template <typename T, typename... Ts> | template <typename T, typename... Ts> | ||||||
| struct MakeTypeSet<T, Ts...> : MakeTypeSet<Ts...>::template Insert<T>::type {}; | 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 | ABSL_MUST_USE_RESULT testing::AssertionResult | ||||||
| VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) { | VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) { | ||||||
|   return hash_internal::VerifyTypeImplementsAbslHashCorrectly( |   return hash_internal::VerifyTypeImplementsAbslHashCorrectly( | ||||||
|       hash_internal::ContainerAsVector<Container>::Do(values), |       hash_internal::ContainerAsVector<Container>::Do(values), equals); | ||||||
|       equals); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <int&..., typename T> | template <int&..., typename T> | ||||||
|  |  | ||||||
|  | @ -991,15 +991,17 @@ TEST(MakeTime, SysSecondsLimits) { | ||||||
|   tp = convert(civil_second::min(), west); |   tp = convert(civil_second::min(), west); | ||||||
|   EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); |   EXPECT_EQ(time_point<absl::time_internal::cctz::seconds>::min(), tp); | ||||||
| 
 | 
 | ||||||
|   // Checks that "tm_year + 1900", as used by the "libc" implementation,
 |   if (sizeof(std::time_t) >= 8) { | ||||||
|   // can produce year values beyond the range on an int without overflow.
 |     // 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) | #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 | #else | ||||||
|   const time_zone libc_utc = LoadZone("libc:UTC"); |     const time_zone libc_utc = LoadZone("libc:UTC"); | ||||||
|   tp = convert(civil_year(year_t{2147483648}), 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)); |     EXPECT_EQ("2147483648-01-01T00:00:00+00:00", format(RFC3339, tp, libc_utc)); | ||||||
| #endif | #endif | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TEST(NextTransition, UTC) { | TEST(NextTransition, UTC) { | ||||||
|  |  | ||||||
|  | @ -176,6 +176,20 @@ inline int MapWeekday(const cctz::weekday& wd) { | ||||||
|   return 1; |   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
 | }  // namespace
 | ||||||
| 
 | 
 | ||||||
| //
 | //
 | ||||||
|  | @ -366,6 +380,14 @@ absl::TimeZone::TimeInfo TimeZone::At(CivilSecond ct) const { | ||||||
|   return ti; |   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.
 | // Conversions involving time zones.
 | ||||||
| //
 | //
 | ||||||
|  |  | ||||||
|  | @ -886,7 +886,7 @@ class TimeZone { | ||||||
|   struct TimeInfo { |   struct TimeInfo { | ||||||
|     enum CivilKind { |     enum CivilKind { | ||||||
|       UNIQUE,    // the civil time was singular (pre == trans == post)
 |       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)
 |       REPEATED,  // the civil time was ambiguous (pre < trans <= post)
 | ||||||
|     } kind; |     } kind; | ||||||
|     Time pre;    // time calculated using the pre-transition offset
 |     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
 |   //   // nov06.post  is 2011-11-06 01:15:00 -0800
 | ||||||
|   TimeInfo At(CivilSecond ct) const; |   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> |   template <typename H> | ||||||
|   friend H AbslHashValue(H h, TimeZone tz) { |   friend H AbslHashValue(H h, TimeZone tz) { | ||||||
|     return H::combine(std::move(h), tz.cz_); |     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)); |   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
 | }  // namespace
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue