summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md47
-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
6 files changed, 193 insertions, 19 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 40351ddc..f4cb4a29 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -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
diff --git a/absl/hash/hash_testing.h b/absl/hash/hash_testing.h
index 1e3cda64..52bcb55a 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 280c96b4..f28e7f85 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 0703856f..ac2c8a83 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 2858da29..7d5c3fde 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 feca4587..4d710709 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