diff options
Diffstat (limited to 'absl/time')
-rw-r--r-- | absl/time/BUILD.bazel | 13 | ||||
-rw-r--r-- | absl/time/clock.cc | 2 | ||||
-rw-r--r-- | absl/time/duration.cc | 21 | ||||
-rw-r--r-- | absl/time/duration_test.cc | 202 | ||||
-rw-r--r-- | absl/time/format.cc | 4 | ||||
-rw-r--r-- | absl/time/time.cc | 13 | ||||
-rw-r--r-- | absl/time/time.h | 164 | ||||
-rw-r--r-- | absl/time/time_test.cc | 61 |
8 files changed, 431 insertions, 49 deletions
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel index da8167f93e4c..0d340f25ca3c 100644 --- a/absl/time/BUILD.bazel +++ b/absl/time/BUILD.bazel @@ -97,16 +97,3 @@ cc_test( "@com_googlesource_code_cctz//:time_zone", ], ) - -# Used by get_current_time_test, which, due to a dependency on commandlineflags -# and some required cleanup, is staying back in //base for now. -cc_library( - name = "get_current_time_for_test", - testonly = 1, - copts = ABSL_DEFAULT_COPTS, - textual_hdrs = [ - "clock.cc", - "clock.h", - ], - deps = ["//absl/base"], -) diff --git a/absl/time/clock.cc b/absl/time/clock.cc index e2bc01bdb6a3..9ffc1c4f91d8 100644 --- a/absl/time/clock.cc +++ b/absl/time/clock.cc @@ -368,7 +368,7 @@ static uint64_t UpdateLastSample( // into the fast past. That causes lots of register spills and reloads that // are unnecessary unless the slow path is taken. // -// TODO(b/36012148) Remove this attribute when our compiler is smart enough +// TODO(absl-team) Remove this attribute when our compiler is smart enough // to do the right thing. ABSL_ATTRIBUTE_NOINLINE static int64_t GetCurrentTimeNanosSlowPath() LOCKS_EXCLUDED(lock) { diff --git a/absl/time/duration.cc b/absl/time/duration.cc index 07d1082d8977..f5081955d3bd 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc @@ -641,6 +641,25 @@ timeval ToTimeval(Duration d) { return tv; } +std::chrono::nanoseconds ToChronoNanoseconds(Duration d) { + return time_internal::ToChronoDuration<std::chrono::nanoseconds>(d); +} +std::chrono::microseconds ToChronoMicroseconds(Duration d) { + return time_internal::ToChronoDuration<std::chrono::microseconds>(d); +} +std::chrono::milliseconds ToChronoMilliseconds(Duration d) { + return time_internal::ToChronoDuration<std::chrono::milliseconds>(d); +} +std::chrono::seconds ToChronoSeconds(Duration d) { + return time_internal::ToChronoDuration<std::chrono::seconds>(d); +} +std::chrono::minutes ToChronoMinutes(Duration d) { + return time_internal::ToChronoDuration<std::chrono::minutes>(d); +} +std::chrono::hours ToChronoHours(Duration d) { + return time_internal::ToChronoDuration<std::chrono::hours>(d); +} + // // To/From std::string formatting. // @@ -852,7 +871,7 @@ bool ParseDuration(const std::string& dur_string, Duration* d) { return true; } -// TODO(b/63899288) copybara strip once dependencies are removed. +// TODO(absl-team): Remove once dependencies are removed. bool ParseFlag(const std::string& text, Duration* dst, std::string* /* err */) { return ParseDuration(text, dst); } diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index ec8e17f548e6..ab98b02ac38f 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <chrono> // NOLINT(build/c++11) #include <cmath> #include <cstdint> #include <ctime> @@ -132,50 +133,50 @@ TEST(Duration, ToConversion) { #undef TEST_DURATION_CONVERSION } -template <int64_t n> +template <int64_t N> void TestToConversion() { - constexpr absl::Duration nano = absl::Nanoseconds(n); - EXPECT_EQ(n, absl::ToInt64Nanoseconds(nano)); + constexpr absl::Duration nano = absl::Nanoseconds(N); + EXPECT_EQ(N, absl::ToInt64Nanoseconds(nano)); EXPECT_EQ(0, absl::ToInt64Microseconds(nano)); EXPECT_EQ(0, absl::ToInt64Milliseconds(nano)); EXPECT_EQ(0, absl::ToInt64Seconds(nano)); EXPECT_EQ(0, absl::ToInt64Minutes(nano)); EXPECT_EQ(0, absl::ToInt64Hours(nano)); - const absl::Duration micro = absl::Microseconds(n); - EXPECT_EQ(n * 1000, absl::ToInt64Nanoseconds(micro)); - EXPECT_EQ(n, absl::ToInt64Microseconds(micro)); + const absl::Duration micro = absl::Microseconds(N); + EXPECT_EQ(N * 1000, absl::ToInt64Nanoseconds(micro)); + EXPECT_EQ(N, absl::ToInt64Microseconds(micro)); EXPECT_EQ(0, absl::ToInt64Milliseconds(micro)); EXPECT_EQ(0, absl::ToInt64Seconds(micro)); EXPECT_EQ(0, absl::ToInt64Minutes(micro)); EXPECT_EQ(0, absl::ToInt64Hours(micro)); - const absl::Duration milli = absl::Milliseconds(n); - EXPECT_EQ(n * 1000 * 1000, absl::ToInt64Nanoseconds(milli)); - EXPECT_EQ(n * 1000, absl::ToInt64Microseconds(milli)); - EXPECT_EQ(n, absl::ToInt64Milliseconds(milli)); + const absl::Duration milli = absl::Milliseconds(N); + EXPECT_EQ(N * 1000 * 1000, absl::ToInt64Nanoseconds(milli)); + EXPECT_EQ(N * 1000, absl::ToInt64Microseconds(milli)); + EXPECT_EQ(N, absl::ToInt64Milliseconds(milli)); EXPECT_EQ(0, absl::ToInt64Seconds(milli)); EXPECT_EQ(0, absl::ToInt64Minutes(milli)); EXPECT_EQ(0, absl::ToInt64Hours(milli)); - const absl::Duration sec = absl::Seconds(n); - EXPECT_EQ(n * 1000 * 1000 * 1000, absl::ToInt64Nanoseconds(sec)); - EXPECT_EQ(n * 1000 * 1000, absl::ToInt64Microseconds(sec)); - EXPECT_EQ(n * 1000, absl::ToInt64Milliseconds(sec)); - EXPECT_EQ(n, absl::ToInt64Seconds(sec)); + const absl::Duration sec = absl::Seconds(N); + EXPECT_EQ(N * 1000 * 1000 * 1000, absl::ToInt64Nanoseconds(sec)); + EXPECT_EQ(N * 1000 * 1000, absl::ToInt64Microseconds(sec)); + EXPECT_EQ(N * 1000, absl::ToInt64Milliseconds(sec)); + EXPECT_EQ(N, absl::ToInt64Seconds(sec)); EXPECT_EQ(0, absl::ToInt64Minutes(sec)); EXPECT_EQ(0, absl::ToInt64Hours(sec)); - const absl::Duration min = absl::Minutes(n); - EXPECT_EQ(n * 60 * 1000 * 1000 * 1000, absl::ToInt64Nanoseconds(min)); - EXPECT_EQ(n * 60 * 1000 * 1000, absl::ToInt64Microseconds(min)); - EXPECT_EQ(n * 60 * 1000, absl::ToInt64Milliseconds(min)); - EXPECT_EQ(n * 60, absl::ToInt64Seconds(min)); - EXPECT_EQ(n, absl::ToInt64Minutes(min)); + const absl::Duration min = absl::Minutes(N); + EXPECT_EQ(N * 60 * 1000 * 1000 * 1000, absl::ToInt64Nanoseconds(min)); + EXPECT_EQ(N * 60 * 1000 * 1000, absl::ToInt64Microseconds(min)); + EXPECT_EQ(N * 60 * 1000, absl::ToInt64Milliseconds(min)); + EXPECT_EQ(N * 60, absl::ToInt64Seconds(min)); + EXPECT_EQ(N, absl::ToInt64Minutes(min)); EXPECT_EQ(0, absl::ToInt64Hours(min)); - const absl::Duration hour = absl::Hours(n); - EXPECT_EQ(n * 60 * 60 * 1000 * 1000 * 1000, absl::ToInt64Nanoseconds(hour)); - EXPECT_EQ(n * 60 * 60 * 1000 * 1000, absl::ToInt64Microseconds(hour)); - EXPECT_EQ(n * 60 * 60 * 1000, absl::ToInt64Milliseconds(hour)); - EXPECT_EQ(n * 60 * 60, absl::ToInt64Seconds(hour)); - EXPECT_EQ(n * 60, absl::ToInt64Minutes(hour)); - EXPECT_EQ(n, absl::ToInt64Hours(hour)); + const absl::Duration hour = absl::Hours(N); + EXPECT_EQ(N * 60 * 60 * 1000 * 1000 * 1000, absl::ToInt64Nanoseconds(hour)); + EXPECT_EQ(N * 60 * 60 * 1000 * 1000, absl::ToInt64Microseconds(hour)); + EXPECT_EQ(N * 60 * 60 * 1000, absl::ToInt64Milliseconds(hour)); + EXPECT_EQ(N * 60 * 60, absl::ToInt64Seconds(hour)); + EXPECT_EQ(N * 60, absl::ToInt64Minutes(hour)); + EXPECT_EQ(N, absl::ToInt64Hours(hour)); } TEST(Duration, ToConversionDeprecated) { @@ -186,6 +187,149 @@ TEST(Duration, ToConversionDeprecated) { TestToConversion<-43>(); } +template <int64_t N> +void TestFromChronoBasicEquality() { + using std::chrono::nanoseconds; + using std::chrono::microseconds; + using std::chrono::milliseconds; + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + + static_assert(absl::Nanoseconds(N) == absl::FromChrono(nanoseconds(N)), ""); + static_assert(absl::Microseconds(N) == absl::FromChrono(microseconds(N)), ""); + static_assert(absl::Milliseconds(N) == absl::FromChrono(milliseconds(N)), ""); + static_assert(absl::Seconds(N) == absl::FromChrono(seconds(N)), ""); + static_assert(absl::Minutes(N) == absl::FromChrono(minutes(N)), ""); + static_assert(absl::Hours(N) == absl::FromChrono(hours(N)), ""); +} + +TEST(Duration, FromChrono) { + TestFromChronoBasicEquality<-123>(); + TestFromChronoBasicEquality<-1>(); + TestFromChronoBasicEquality<0>(); + TestFromChronoBasicEquality<1>(); + TestFromChronoBasicEquality<123>(); + + // Minutes (might, depending on the platform) saturate at +inf. + const auto chrono_minutes_max = std::chrono::minutes::max(); + const auto minutes_max = absl::FromChrono(chrono_minutes_max); + const int64_t minutes_max_count = chrono_minutes_max.count(); + if (minutes_max_count > kint64max / 60) { + EXPECT_EQ(absl::InfiniteDuration(), minutes_max); + } else { + EXPECT_EQ(absl::Minutes(minutes_max_count), minutes_max); + } + + // Minutes (might, depending on the platform) saturate at -inf. + const auto chrono_minutes_min = std::chrono::minutes::min(); + const auto minutes_min = absl::FromChrono(chrono_minutes_min); + const int64_t minutes_min_count = chrono_minutes_min.count(); + if (minutes_min_count < kint64min / 60) { + EXPECT_EQ(-absl::InfiniteDuration(), minutes_min); + } else { + EXPECT_EQ(absl::Minutes(minutes_min_count), minutes_min); + } + + // Hours (might, depending on the platform) saturate at +inf. + const auto chrono_hours_max = std::chrono::hours::max(); + const auto hours_max = absl::FromChrono(chrono_hours_max); + const int64_t hours_max_count = chrono_hours_max.count(); + if (hours_max_count > kint64max / 3600) { + EXPECT_EQ(absl::InfiniteDuration(), hours_max); + } else { + EXPECT_EQ(absl::Hours(hours_max_count), hours_max); + } + + // Hours (might, depending on the platform) saturate at -inf. + const auto chrono_hours_min = std::chrono::hours::min(); + const auto hours_min = absl::FromChrono(chrono_hours_min); + const int64_t hours_min_count = chrono_hours_min.count(); + if (hours_min_count < kint64min / 3600) { + EXPECT_EQ(-absl::InfiniteDuration(), hours_min); + } else { + EXPECT_EQ(absl::Hours(hours_min_count), hours_min); + } +} + +template <int64_t N> +void TestToChrono() { + using std::chrono::nanoseconds; + using std::chrono::microseconds; + using std::chrono::milliseconds; + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + + EXPECT_EQ(nanoseconds(N), absl::ToChronoNanoseconds(absl::Nanoseconds(N))); + EXPECT_EQ(microseconds(N), absl::ToChronoMicroseconds(absl::Microseconds(N))); + EXPECT_EQ(milliseconds(N), absl::ToChronoMilliseconds(absl::Milliseconds(N))); + EXPECT_EQ(seconds(N), absl::ToChronoSeconds(absl::Seconds(N))); + + constexpr auto absl_minutes = absl::Minutes(N); + auto chrono_minutes = minutes(N); + if (absl_minutes == -absl::InfiniteDuration()) { + chrono_minutes = minutes::min(); + } else if (absl_minutes == absl::InfiniteDuration()) { + chrono_minutes = minutes::max(); + } + EXPECT_EQ(chrono_minutes, absl::ToChronoMinutes(absl_minutes)); + + constexpr auto absl_hours = absl::Hours(N); + auto chrono_hours = hours(N); + if (absl_hours == -absl::InfiniteDuration()) { + chrono_hours = hours::min(); + } else if (absl_hours == absl::InfiniteDuration()) { + chrono_hours = hours::max(); + } + EXPECT_EQ(chrono_hours, absl::ToChronoHours(absl_hours)); +} + +TEST(Duration, ToChrono) { + using std::chrono::nanoseconds; + using std::chrono::microseconds; + using std::chrono::milliseconds; + using std::chrono::seconds; + using std::chrono::minutes; + using std::chrono::hours; + + TestToChrono<kint64min>(); + TestToChrono<-1>(); + TestToChrono<0>(); + TestToChrono<1>(); + TestToChrono<kint64max>(); + + // Verify truncation toward zero. + const auto tick = absl::Nanoseconds(1) / 4; + EXPECT_EQ(nanoseconds(0), absl::ToChronoNanoseconds(tick)); + EXPECT_EQ(nanoseconds(0), absl::ToChronoNanoseconds(-tick)); + EXPECT_EQ(microseconds(0), absl::ToChronoMicroseconds(tick)); + EXPECT_EQ(microseconds(0), absl::ToChronoMicroseconds(-tick)); + EXPECT_EQ(milliseconds(0), absl::ToChronoMilliseconds(tick)); + EXPECT_EQ(milliseconds(0), absl::ToChronoMilliseconds(-tick)); + EXPECT_EQ(seconds(0), absl::ToChronoSeconds(tick)); + EXPECT_EQ(seconds(0), absl::ToChronoSeconds(-tick)); + EXPECT_EQ(minutes(0), absl::ToChronoMinutes(tick)); + EXPECT_EQ(minutes(0), absl::ToChronoMinutes(-tick)); + EXPECT_EQ(hours(0), absl::ToChronoHours(tick)); + EXPECT_EQ(hours(0), absl::ToChronoHours(-tick)); + + // Verifies +/- infinity saturation at max/min. + constexpr auto inf = absl::InfiniteDuration(); + EXPECT_EQ(nanoseconds::min(), absl::ToChronoNanoseconds(-inf)); + EXPECT_EQ(nanoseconds::max(), absl::ToChronoNanoseconds(inf)); + EXPECT_EQ(microseconds::min(), absl::ToChronoMicroseconds(-inf)); + EXPECT_EQ(microseconds::max(), absl::ToChronoMicroseconds(inf)); + EXPECT_EQ(milliseconds::min(), absl::ToChronoMilliseconds(-inf)); + EXPECT_EQ(milliseconds::max(), absl::ToChronoMilliseconds(inf)); + EXPECT_EQ(seconds::min(), absl::ToChronoSeconds(-inf)); + EXPECT_EQ(seconds::max(), absl::ToChronoSeconds(inf)); + EXPECT_EQ(minutes::min(), absl::ToChronoMinutes(-inf)); + EXPECT_EQ(minutes::max(), absl::ToChronoMinutes(inf)); + EXPECT_EQ(hours::min(), absl::ToChronoHours(-inf)); + EXPECT_EQ(hours::max(), absl::ToChronoHours(inf)); +} + // Used for testing the factory overloads. template <typename T> struct ImplicitlyConvertible { @@ -248,7 +392,7 @@ TEST(Duration, FactoryOverloads) { } TEST(Duration, InfinityExamples) { - // These examples are used in the documentation in //base/time.h. They are + // These examples are used in the documentation in time.h. They are // written so that they can be copy-n-pasted easily. constexpr absl::Duration inf = absl::InfiniteDuration(); diff --git a/absl/time/format.cc b/absl/time/format.cc index c58a886bf93a..7c88db5b00d0 100644 --- a/absl/time/format.cc +++ b/absl/time/format.cc @@ -127,8 +127,8 @@ bool ParseTime(const std::string& format, const std::string& input, absl::TimeZo return b; } -// TODO(b/63899288) copybara strip once dependencies are removed. -// Functions required to support absl::Time flags. See go/flags. +// TODO(absl-team): Remove once dependencies are removed. +// Functions required to support absl::Time flags. bool ParseFlag(const std::string& text, absl::Time* t, std::string* error) { return absl::ParseTime(RFC3339_full, text, absl::UTCTimeZone(), t, error); } diff --git a/absl/time/time.cc b/absl/time/time.cc index fd5a41b15080..ee14ba306e80 100644 --- a/absl/time/time.cc +++ b/absl/time/time.cc @@ -367,4 +367,17 @@ int64_t ToUniversal(absl::Time t) { return absl::FloorToUnit(t - absl::UniversalEpoch(), absl::Nanoseconds(100)); } +Time FromChrono(const std::chrono::system_clock::time_point& tp) { + return time_internal::FromUnixDuration(time_internal::FromChrono( + tp - std::chrono::system_clock::from_time_t(0))); +} + +std::chrono::system_clock::time_point ToChronoTime(absl::Time t) { + using D = std::chrono::system_clock::duration; + auto d = time_internal::ToUnixDuration(t); + if (d < ZeroDuration()) d = Floor(d, FromChrono(D{1})); + return std::chrono::system_clock::from_time_t(0) + + time_internal::ToChronoDuration<D>(d); +} + } // namespace absl diff --git a/absl/time/time.h b/absl/time/time.h index 302c76037e6f..6976343708be 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -361,7 +361,7 @@ Duration Hours(T n) { // Example: // // absl::Duration d = absl::Milliseconds(1500); -// int64_t isec = ToInt64Seconds(d); // isec == 1 +// int64_t isec = ToInt64Seconds(d); // isec == 1 int64_t ToInt64Nanoseconds(Duration d); int64_t ToInt64Microseconds(Duration d); int64_t ToInt64Milliseconds(Duration d); @@ -391,6 +391,46 @@ double ToDoubleSeconds(Duration d); double ToDoubleMinutes(Duration d); double ToDoubleHours(Duration d); +// FromChrono() +// +// Converts any of the pre-defined std::chrono durations to an absl::Duration. +// +// Example: +// +// std::chrono::milliseconds ms(123); +// absl::Duration d = absl::FromChrono(ms); +constexpr Duration FromChrono(const std::chrono::nanoseconds& d); +constexpr Duration FromChrono(const std::chrono::microseconds& d); +constexpr Duration FromChrono(const std::chrono::milliseconds& d); +constexpr Duration FromChrono(const std::chrono::seconds& d); +constexpr Duration FromChrono(const std::chrono::minutes& d); +constexpr Duration FromChrono(const std::chrono::hours& d); + +// ToChronoNanoseconds() +// ToChronoMicroseconds() +// ToChronoMilliseconds() +// ToChronoSeconds() +// ToChronoMinutes() +// ToChronoHours() +// +// Converts an absl::Duration to any of the pre-defined std::chrono durations. +// If overflow would occur, the returned value will saturate at the min/max +// chrono duration value instead. +// +// Example: +// +// absl::Duration d = absl::Microseconds(123); +// auto x = absl::ToChronoMicroseconds(d); +// auto y = absl::ToChronoNanoseconds(d); // x == y +// auto z = absl::ToChronoSeconds(absl::InfiniteDuration()); +// // z == std::chrono::seconds::max() +std::chrono::nanoseconds ToChronoNanoseconds(Duration d); +std::chrono::microseconds ToChronoMicroseconds(Duration d); +std::chrono::milliseconds ToChronoMilliseconds(Duration d); +std::chrono::seconds ToChronoSeconds(Duration d); +std::chrono::minutes ToChronoMinutes(Duration d); +std::chrono::hours ToChronoHours(Duration d); + // InfiniteDuration() // // Returns an infinite `Duration`. To get a `Duration` representing negative @@ -445,7 +485,7 @@ inline std::ostream& operator<<(std::ostream& os, Duration d) { bool ParseDuration(const std::string& dur_string, Duration* d); // Flag Support -// TODO(b/63899288) copybara strip once dependencies are removed. +// TODO(absl-team): Remove once dependencies are removed. // ParseFlag() // @@ -790,6 +830,30 @@ Time TimeFromTimeval(timeval tv); timespec ToTimespec(Time t); timeval ToTimeval(Time t); +// FromChrono() +// +// Converts a std::chrono::system_clock::time_point to an absl::Time. +// +// Example: +// +// auto tp = std::chrono::system_clock::from_time_t(123); +// absl::Time t = absl::FromChrono(tp); +// // t == absl::FromTimeT(123) +Time FromChrono(const std::chrono::system_clock::time_point& tp); + +// ToChronoTime() +// +// Converts an absl::Time to a std::chrono::system_clock::time_point. If +// overflow would occur, the returned value will saturate at the min/max time +// point value instead. +// +// Example: +// +// absl::Time t = absl::FromTimeT(123); +// auto tp = absl::ToChronoTime(t); +// // tp == std::chrono::system_clock::from_time_t(123); +std::chrono::system_clock::time_point ToChronoTime(absl::Time); + // RFC3339_full // RFC3339_sec // @@ -917,7 +981,7 @@ bool ParseTime(const std::string& format, const std::string& input, bool ParseTime(const std::string& format, const std::string& input, TimeZone tz, Time* time, std::string* err); -// TODO(b/63899288) copybara strip once dependencies are removed. +// TODO(absl-team): Remove once dependencies are removed. // ParseFlag() // UnparseFlag() @@ -1072,6 +1136,81 @@ constexpr int64_t NegateAndSubtractOne(int64_t n) { // knowledge, we would need to add-in/subtract-out UnixEpoch() respectively. constexpr Time FromUnixDuration(Duration d) { return Time(d); } constexpr Duration ToUnixDuration(Time t) { return t.rep_; } + +template <std::intmax_t N> +constexpr absl::Duration FromInt64(int64_t v, std::ratio<1, N>) { + static_assert(0 < N && N <= 1000 * 1000 * 1000, "Unsupported ratio"); + // Subsecond ratios cannot overflow. + return MakeNormalizedDuration( + v / N, v % N * kTicksPerNanosecond * 1000 * 1000 * 1000 / N); +} +constexpr absl::Duration FromInt64(int64_t v, std::ratio<60>) { + return Minutes(v); +} +constexpr absl::Duration FromInt64(int64_t v, std::ratio<3600>) { + return Hours(v); +} + +// IsValidRep64<T>(0) is true if the expression `int64_t{std::declval<T>()}` is +// valid. That is, if a T can be assigned to an int64_t without narrowing. +template <typename T> +constexpr auto IsValidRep64(int) + -> decltype(int64_t{std::declval<T>()}, bool()) { + return true; +} +template <typename T> +constexpr auto IsValidRep64(char) -> bool { + return false; +} + +// Converts a std::chrono::duration to an absl::Duration. +template <typename Rep, typename Period> +constexpr absl::Duration FromChrono( + const std::chrono::duration<Rep, Period>& d) { + static_assert(IsValidRep64<Rep>(0), "duration::rep is invalid"); + return FromInt64(int64_t{d.count()}, Period{}); +} + +template <typename Ratio> +int64_t ToInt64(absl::Duration d, Ratio) { + // Note: This may be used on MSVC, which may have a system_clock period of + // std::ratio<1, 10 * 1000 * 1000> + return ToInt64Seconds(d * Ratio::den / Ratio::num); +} +// Fastpath implementations for the 6 common duration units. +inline int64_t ToInt64(absl::Duration d, std::nano) { + return ToInt64Nanoseconds(d); +} +inline int64_t ToInt64(absl::Duration d, std::micro) { + return ToInt64Microseconds(d); +} +inline int64_t ToInt64(absl::Duration d, std::milli) { + return ToInt64Milliseconds(d); +} +inline int64_t ToInt64(absl::Duration d, std::ratio<1>) { + return ToInt64Seconds(d); +} +inline int64_t ToInt64(absl::Duration d, std::ratio<60>) { + return ToInt64Minutes(d); +} +inline int64_t ToInt64(absl::Duration d, std::ratio<3600>) { + return ToInt64Hours(d); +} + +// Converts an absl::Duration to a chrono duration of type T. +template <typename T> +T ToChronoDuration(absl::Duration d) { + using Rep = typename T::rep; + using Period = typename T::period; + static_assert(IsValidRep64<Rep>(0), "duration::rep is invalid"); + if (time_internal::IsInfiniteDuration(d)) + return d < ZeroDuration() ? T::min() : T::max(); + const auto v = ToInt64(d, Period{}); + if (v > std::numeric_limits<Rep>::max()) return T::max(); + if (v < std::numeric_limits<Rep>::min()) return T::min(); + return T{v}; +} + } // namespace time_internal constexpr bool operator<(Duration lhs, Duration rhs) { @@ -1156,6 +1295,25 @@ constexpr Duration InfiniteDuration() { return time_internal::MakeDuration(std::numeric_limits<int64_t>::max(), ~0U); } +constexpr Duration FromChrono(const std::chrono::nanoseconds& d) { + return time_internal::FromChrono(d); +} +constexpr Duration FromChrono(const std::chrono::microseconds& d) { + return time_internal::FromChrono(d); +} +constexpr Duration FromChrono(const std::chrono::milliseconds& d) { + return time_internal::FromChrono(d); +} +constexpr Duration FromChrono(const std::chrono::seconds& d) { + return time_internal::FromChrono(d); +} +constexpr Duration FromChrono(const std::chrono::minutes& d) { + return time_internal::FromChrono(d); +} +constexpr Duration FromChrono(const std::chrono::hours& d) { + return time_internal::FromChrono(d); +} + constexpr Time FromUnixNanos(int64_t ns) { return time_internal::FromUnixDuration(Nanoseconds(ns)); } diff --git a/absl/time/time_test.cc b/absl/time/time_test.cc index 51b9e53d6905..64083880c1e0 100644 --- a/absl/time/time_test.cc +++ b/absl/time/time_test.cc @@ -14,6 +14,7 @@ #include "absl/time/time.h" +#include <chrono> // NOLINT(build/c++11) #include <cstring> #include <ctime> #include <iomanip> @@ -489,6 +490,66 @@ TEST(Time, RoundtripConversion) { #undef TEST_CONVERSION_ROUND_TRIP } +template <typename Duration> +std::chrono::system_clock::time_point MakeChronoUnixTime(const Duration& d) { + return std::chrono::system_clock::from_time_t(0) + d; +} + +TEST(Time, FromChrono) { + EXPECT_EQ(absl::FromTimeT(-1), + absl::FromChrono(std::chrono::system_clock::from_time_t(-1))); + EXPECT_EQ(absl::FromTimeT(0), + absl::FromChrono(std::chrono::system_clock::from_time_t(0))); + EXPECT_EQ(absl::FromTimeT(1), + absl::FromChrono(std::chrono::system_clock::from_time_t(1))); + + EXPECT_EQ( + absl::FromUnixMillis(-1), + absl::FromChrono(MakeChronoUnixTime(std::chrono::milliseconds(-1)))); + EXPECT_EQ(absl::FromUnixMillis(0), + absl::FromChrono(MakeChronoUnixTime(std::chrono::milliseconds(0)))); + EXPECT_EQ(absl::FromUnixMillis(1), + absl::FromChrono(MakeChronoUnixTime(std::chrono::milliseconds(1)))); + + // Chrono doesn't define exactly its range and precision (neither does + // absl::Time), so let's simply test +/- ~100 years to make sure things work. + const auto century_sec = 60 * 60 * 24 * 365 * int64_t{100}; + const auto century = std::chrono::seconds(century_sec); + const auto chrono_future = MakeChronoUnixTime(century); + const auto chrono_past = MakeChronoUnixTime(-century); + EXPECT_EQ(absl::FromUnixSeconds(century_sec), + absl::FromChrono(chrono_future)); + EXPECT_EQ(absl::FromUnixSeconds(-century_sec), absl::FromChrono(chrono_past)); + + // Roundtrip them both back to chrono. + EXPECT_EQ(chrono_future, + absl::ToChronoTime(absl::FromUnixSeconds(century_sec))); + EXPECT_EQ(chrono_past, + absl::ToChronoTime(absl::FromUnixSeconds(-century_sec))); +} + +TEST(Time, ToChronoTime) { + EXPECT_EQ(std::chrono::system_clock::from_time_t(-1), + absl::ToChronoTime(absl::FromTimeT(-1))); + EXPECT_EQ(std::chrono::system_clock::from_time_t(0), + absl::ToChronoTime(absl::FromTimeT(0))); + EXPECT_EQ(std::chrono::system_clock::from_time_t(1), + absl::ToChronoTime(absl::FromTimeT(1))); + + EXPECT_EQ(MakeChronoUnixTime(std::chrono::milliseconds(-1)), + absl::ToChronoTime(absl::FromUnixMillis(-1))); + EXPECT_EQ(MakeChronoUnixTime(std::chrono::milliseconds(0)), + absl::ToChronoTime(absl::FromUnixMillis(0))); + EXPECT_EQ(MakeChronoUnixTime(std::chrono::milliseconds(1)), + absl::ToChronoTime(absl::FromUnixMillis(1))); + + // Time before the Unix epoch should floor, not trunc. + const auto tick = absl::Nanoseconds(1) / 4; + EXPECT_EQ(std::chrono::system_clock::from_time_t(0) - + std::chrono::system_clock::duration(1), + absl::ToChronoTime(absl::UnixEpoch() - tick)); +} + TEST(Time, ConvertDateTime) { const absl::TimeZone utc = absl::UTCTimeZone(); const absl::TimeZone goog = |