about summary refs log tree commit diff
path: root/absl/time
diff options
context:
space:
mode:
Diffstat (limited to 'absl/time')
-rw-r--r--absl/time/BUILD.bazel20
-rw-r--r--absl/time/clock_benchmark.cc72
-rw-r--r--absl/time/duration_benchmark.cc361
-rw-r--r--absl/time/duration_test.cc20
-rw-r--r--absl/time/format_benchmark.cc63
-rw-r--r--absl/time/internal/cctz/src/time_zone_format_test.cc13
-rw-r--r--absl/time/internal/cctz/src/time_zone_lookup_test.cc17
-rw-r--r--absl/time/time.h88
-rw-r--r--absl/time/time_benchmark.cc316
9 files changed, 899 insertions, 71 deletions
diff --git a/absl/time/BUILD.bazel b/absl/time/BUILD.bazel
index 64cb99f73716..fe55fe1f9bf6 100644
--- a/absl/time/BUILD.bazel
+++ b/absl/time/BUILD.bazel
@@ -93,3 +93,23 @@ cc_test(
         "@com_google_googletest//:gtest_main",
     ],
 )
+
+cc_test(
+    name = "time_benchmark",
+    srcs = [
+        "clock_benchmark.cc",
+        "duration_benchmark.cc",
+        "format_benchmark.cc",
+        "time_benchmark.cc",
+    ],
+    copts = ABSL_TEST_COPTS,
+    tags = [
+        "benchmark",
+    ],
+    deps = [
+        ":test_util",
+        ":time",
+        "//absl/base",
+        "@com_github_google_benchmark//:benchmark_main",
+    ],
+)
diff --git a/absl/time/clock_benchmark.cc b/absl/time/clock_benchmark.cc
new file mode 100644
index 000000000000..3d3cd9d5c42c
--- /dev/null
+++ b/absl/time/clock_benchmark.cc
@@ -0,0 +1,72 @@
+// Copyright 2018 The Abseil Authors.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/time/clock.h"
+
+#if !defined(_WIN32)
+#include <sys/time.h>
+#endif  // _WIN32
+#include <cstdio>
+
+#include "absl/base/internal/cycleclock.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+void BM_Clock_Now_AbslTime(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::Now());
+  }
+}
+BENCHMARK(BM_Clock_Now_AbslTime);
+
+void BM_Clock_Now_GetCurrentTimeNanos(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::GetCurrentTimeNanos());
+  }
+}
+BENCHMARK(BM_Clock_Now_GetCurrentTimeNanos);
+
+void BM_Clock_Now_AbslTime_ToUnixNanos(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToUnixNanos(absl::Now()));
+  }
+}
+BENCHMARK(BM_Clock_Now_AbslTime_ToUnixNanos);
+
+void BM_Clock_Now_CycleClock(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::base_internal::CycleClock::Now());
+  }
+}
+BENCHMARK(BM_Clock_Now_CycleClock);
+
+#if !defined(_WIN32)
+static void BM_Clock_Now_gettimeofday(benchmark::State& state) {
+  struct timeval tv;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(gettimeofday(&tv, nullptr));
+  }
+}
+BENCHMARK(BM_Clock_Now_gettimeofday);
+
+static void BM_Clock_Now_clock_gettime(benchmark::State& state) {
+  struct timespec ts;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(clock_gettime(CLOCK_REALTIME, &ts));
+  }
+}
+BENCHMARK(BM_Clock_Now_clock_gettime);
+#endif  // _WIN32
+
+}  // namespace
diff --git a/absl/time/duration_benchmark.cc b/absl/time/duration_benchmark.cc
new file mode 100644
index 000000000000..54f89a1f000d
--- /dev/null
+++ b/absl/time/duration_benchmark.cc
@@ -0,0 +1,361 @@
+// Copyright 2018 The Abseil Authors.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+#include <ctime>
+#include <string>
+
+#include "absl/time/time.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+//
+// Factory functions
+//
+
+void BM_Duration_Factory_Nanoseconds(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::Nanoseconds(1));
+  }
+}
+BENCHMARK(BM_Duration_Factory_Nanoseconds);
+
+void BM_Duration_Factory_Microseconds(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::Microseconds(1));
+  }
+}
+BENCHMARK(BM_Duration_Factory_Microseconds);
+
+void BM_Duration_Factory_Milliseconds(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::Milliseconds(1));
+  }
+}
+BENCHMARK(BM_Duration_Factory_Milliseconds);
+
+void BM_Duration_Factory_Seconds(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::Seconds(1));
+  }
+}
+BENCHMARK(BM_Duration_Factory_Seconds);
+
+void BM_Duration_Factory_Minutes(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::Minutes(1));
+  }
+}
+BENCHMARK(BM_Duration_Factory_Minutes);
+
+void BM_Duration_Factory_Hours(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::Hours(1));
+  }
+}
+BENCHMARK(BM_Duration_Factory_Hours);
+
+//
+// Arithmetic
+//
+
+void BM_Duration_Addition(benchmark::State& state) {
+  absl::Duration d = absl::Nanoseconds(1);
+  absl::Duration step = absl::Milliseconds(1);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(d += step);
+  }
+}
+BENCHMARK(BM_Duration_Addition);
+
+void BM_Duration_Subtraction(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(std::numeric_limits<int64_t>::max());
+  absl::Duration step = absl::Milliseconds(1);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(d -= step);
+  }
+}
+BENCHMARK(BM_Duration_Subtraction);
+
+void BM_Duration_Multiplication_Fixed(benchmark::State& state) {
+  absl::Duration d = absl::Milliseconds(1);
+  absl::Duration s;
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(s += d * (i + 1));
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_Multiplication_Fixed);
+
+void BM_Duration_Multiplication_Double(benchmark::State& state) {
+  absl::Duration d = absl::Milliseconds(1);
+  absl::Duration s;
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(s += d * (i + 1.0));
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_Multiplication_Double);
+
+void BM_Duration_Division_Fixed(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(1);
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(d /= i + 1);
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_Division_Fixed);
+
+void BM_Duration_Division_Double(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(1);
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(d /= i + 1.0);
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_Division_Double);
+
+void BM_Duration_FDivDuration_Nanoseconds(benchmark::State& state) {
+  double d = 1;
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(
+        d += absl::FDivDuration(absl::Milliseconds(i), absl::Nanoseconds(1)));
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_FDivDuration_Nanoseconds);
+
+void BM_Duration_IDivDuration_Nanoseconds(benchmark::State& state) {
+  int64_t a = 1;
+  absl::Duration ignore;
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(a +=
+                             absl::IDivDuration(absl::Nanoseconds(i),
+                                                absl::Nanoseconds(1), &ignore));
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_IDivDuration_Nanoseconds);
+
+void BM_Duration_IDivDuration_Microseconds(benchmark::State& state) {
+  int64_t a = 1;
+  absl::Duration ignore;
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(a += absl::IDivDuration(absl::Microseconds(i),
+                                                     absl::Microseconds(1),
+                                                     &ignore));
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_IDivDuration_Microseconds);
+
+void BM_Duration_IDivDuration_Milliseconds(benchmark::State& state) {
+  int64_t a = 1;
+  absl::Duration ignore;
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(a += absl::IDivDuration(absl::Milliseconds(i),
+                                                     absl::Milliseconds(1),
+                                                     &ignore));
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_IDivDuration_Milliseconds);
+
+void BM_Duration_IDivDuration_Seconds(benchmark::State& state) {
+  int64_t a = 1;
+  absl::Duration ignore;
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(
+        a += absl::IDivDuration(absl::Seconds(i), absl::Seconds(1), &ignore));
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_IDivDuration_Seconds);
+
+void BM_Duration_IDivDuration_Minutes(benchmark::State& state) {
+  int64_t a = 1;
+  absl::Duration ignore;
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(
+        a += absl::IDivDuration(absl::Minutes(i), absl::Minutes(1), &ignore));
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_IDivDuration_Minutes);
+
+void BM_Duration_IDivDuration_Hours(benchmark::State& state) {
+  int64_t a = 1;
+  absl::Duration ignore;
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(
+        a += absl::IDivDuration(absl::Hours(i), absl::Hours(1), &ignore));
+    ++i;
+  }
+}
+BENCHMARK(BM_Duration_IDivDuration_Hours);
+
+void BM_Duration_ToInt64Nanoseconds(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(100000);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToInt64Nanoseconds(d));
+  }
+}
+BENCHMARK(BM_Duration_ToInt64Nanoseconds);
+
+void BM_Duration_ToInt64Microseconds(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(100000);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToInt64Microseconds(d));
+  }
+}
+BENCHMARK(BM_Duration_ToInt64Microseconds);
+
+void BM_Duration_ToInt64Milliseconds(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(100000);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToInt64Milliseconds(d));
+  }
+}
+BENCHMARK(BM_Duration_ToInt64Milliseconds);
+
+void BM_Duration_ToInt64Seconds(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(100000);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToInt64Seconds(d));
+  }
+}
+BENCHMARK(BM_Duration_ToInt64Seconds);
+
+void BM_Duration_ToInt64Minutes(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(100000);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToInt64Minutes(d));
+  }
+}
+BENCHMARK(BM_Duration_ToInt64Minutes);
+
+void BM_Duration_ToInt64Hours(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(100000);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToInt64Hours(d));
+  }
+}
+BENCHMARK(BM_Duration_ToInt64Hours);
+
+//
+// To/FromTimespec
+//
+
+void BM_Duration_ToTimespec_AbslTime(benchmark::State& state) {
+  absl::Duration d = absl::Seconds(1);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToTimespec(d));
+  }
+}
+BENCHMARK(BM_Duration_ToTimespec_AbslTime);
+
+ABSL_ATTRIBUTE_NOINLINE timespec DoubleToTimespec(double seconds) {
+  timespec ts;
+  ts.tv_sec = seconds;
+  ts.tv_nsec = (seconds - ts.tv_sec) * (1000 * 1000 * 1000);
+  return ts;
+}
+
+void BM_Duration_ToTimespec_Double(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(DoubleToTimespec(1.0));
+  }
+}
+BENCHMARK(BM_Duration_ToTimespec_Double);
+
+void BM_Duration_FromTimespec_AbslTime(benchmark::State& state) {
+  timespec ts;
+  ts.tv_sec = 0;
+  ts.tv_nsec = 0;
+  while (state.KeepRunning()) {
+    if (++ts.tv_nsec == 1000 * 1000 * 1000) {
+      ++ts.tv_sec;
+      ts.tv_nsec = 0;
+    }
+    benchmark::DoNotOptimize(absl::DurationFromTimespec(ts));
+  }
+}
+BENCHMARK(BM_Duration_FromTimespec_AbslTime);
+
+ABSL_ATTRIBUTE_NOINLINE double TimespecToDouble(timespec ts) {
+  return ts.tv_sec + (ts.tv_nsec / (1000 * 1000 * 1000));
+}
+
+void BM_Duration_FromTimespec_Double(benchmark::State& state) {
+  timespec ts;
+  ts.tv_sec = 0;
+  ts.tv_nsec = 0;
+  while (state.KeepRunning()) {
+    if (++ts.tv_nsec == 1000 * 1000 * 1000) {
+      ++ts.tv_sec;
+      ts.tv_nsec = 0;
+    }
+    benchmark::DoNotOptimize(TimespecToDouble(ts));
+  }
+}
+BENCHMARK(BM_Duration_FromTimespec_Double);
+
+//
+// String conversions
+//
+
+const char* const kDurations[] = {
+    "0",                                   // 0
+    "123ns",                               // 1
+    "1h2m3s",                              // 2
+    "-2h3m4.005006007s",                   // 3
+    "2562047788015215h30m7.99999999975s",  // 4
+};
+const int kNumDurations = sizeof(kDurations) / sizeof(kDurations[0]);
+
+void BM_Duration_FormatDuration(benchmark::State& state) {
+  const std::string s = kDurations[state.range(0)];
+  state.SetLabel(s);
+  absl::Duration d;
+  absl::ParseDuration(kDurations[state.range(0)], &d);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::FormatDuration(d));
+  }
+}
+BENCHMARK(BM_Duration_FormatDuration)->DenseRange(0, kNumDurations - 1);
+
+void BM_Duration_ParseDuration(benchmark::State& state) {
+  const std::string s = kDurations[state.range(0)];
+  state.SetLabel(s);
+  absl::Duration d;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ParseDuration(s, &d));
+  }
+}
+BENCHMARK(BM_Duration_ParseDuration)->DenseRange(0, kNumDurations - 1);
+
+}  // namespace
diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc
index 7918e1677052..704684edb34e 100644
--- a/absl/time/duration_test.cc
+++ b/absl/time/duration_test.cc
@@ -330,18 +330,10 @@ TEST(Duration, ToChrono) {
   EXPECT_EQ(hours::max(), absl::ToChronoHours(inf));
 }
 
-// Used for testing the factory overloads.
-template <typename T>
-struct ImplicitlyConvertible {
-  T n_;
-  explicit ImplicitlyConvertible(T n) : n_(n) {}
-  // Marking this conversion operator with 'explicit' will cause the test to
-  // fail (as desired).
-  operator T() { return n_; }
-};
-
 TEST(Duration, FactoryOverloads) {
+  enum E { kOne = 1 };
 #define TEST_FACTORY_OVERLOADS(NAME)                                          \
+  EXPECT_EQ(1, NAME(kOne) / NAME(kOne));                                      \
   EXPECT_EQ(1, NAME(static_cast<int8_t>(1)) / NAME(1));                       \
   EXPECT_EQ(1, NAME(static_cast<int16_t>(1)) / NAME(1));                      \
   EXPECT_EQ(1, NAME(static_cast<int32_t>(1)) / NAME(1));                      \
@@ -350,14 +342,6 @@ TEST(Duration, FactoryOverloads) {
   EXPECT_EQ(1, NAME(static_cast<uint16_t>(1)) / NAME(1));                     \
   EXPECT_EQ(1, NAME(static_cast<uint32_t>(1)) / NAME(1));                     \
   EXPECT_EQ(1, NAME(static_cast<uint64_t>(1)) / NAME(1));                     \
-  EXPECT_EQ(1, NAME(ImplicitlyConvertible<int8_t>(1)) / NAME(1));             \
-  EXPECT_EQ(1, NAME(ImplicitlyConvertible<int16_t>(1)) / NAME(1));            \
-  EXPECT_EQ(1, NAME(ImplicitlyConvertible<int32_t>(1)) / NAME(1));            \
-  EXPECT_EQ(1, NAME(ImplicitlyConvertible<int64_t>(1)) / NAME(1));            \
-  EXPECT_EQ(1, NAME(ImplicitlyConvertible<uint8_t>(1)) / NAME(1));            \
-  EXPECT_EQ(1, NAME(ImplicitlyConvertible<uint16_t>(1)) / NAME(1));           \
-  EXPECT_EQ(1, NAME(ImplicitlyConvertible<uint32_t>(1)) / NAME(1));           \
-  EXPECT_EQ(1, NAME(ImplicitlyConvertible<uint64_t>(1)) / NAME(1));           \
   EXPECT_EQ(NAME(1) / 2, NAME(static_cast<float>(0.5)));                      \
   EXPECT_EQ(NAME(1) / 2, NAME(static_cast<double>(0.5)));                     \
   EXPECT_EQ(1.5, absl::FDivDuration(NAME(static_cast<float>(1.5)), NAME(1))); \
diff --git a/absl/time/format_benchmark.cc b/absl/time/format_benchmark.cc
new file mode 100644
index 000000000000..ee53d71c6854
--- /dev/null
+++ b/absl/time/format_benchmark.cc
@@ -0,0 +1,63 @@
+// Copyright 2018 The Abseil Authors.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <cstddef>
+#include <string>
+
+#include "absl/time/internal/test_util.h"
+#include "absl/time/time.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+namespace {
+const char* const kFormats[] = {
+    absl::RFC1123_full,     // 0
+    absl::RFC1123_no_wday,  // 1
+    absl::RFC3339_full,     // 2
+    absl::RFC3339_sec,      // 3
+    "%Y-%m-%dT%H:%M:%S",    // 4
+    "%Y-%m-%d",             // 5
+};
+const int kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
+}  // namespace
+
+void BM_Format_FormatTime(benchmark::State& state) {
+  const std::string fmt = kFormats[state.range(0)];
+  state.SetLabel(fmt);
+  const absl::TimeZone lax =
+      absl::time_internal::LoadTimeZone("America/Los_Angeles");
+  const absl::Time t =
+      absl::FromDateTime(1977, 6, 28, 9, 8, 7, lax) + absl::Nanoseconds(1);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::FormatTime(fmt, t, lax).length());
+  }
+}
+BENCHMARK(BM_Format_FormatTime)->DenseRange(0, kNumFormats - 1);
+
+void BM_Format_ParseTime(benchmark::State& state) {
+  const std::string fmt = kFormats[state.range(0)];
+  state.SetLabel(fmt);
+  const absl::TimeZone lax =
+      absl::time_internal::LoadTimeZone("America/Los_Angeles");
+  absl::Time t =
+      absl::FromDateTime(1977, 6, 28, 9, 8, 7, lax) + absl::Nanoseconds(1);
+  const std::string when = absl::FormatTime(fmt, t, lax);
+  std::string err;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ParseTime(fmt, when, lax, &t, &err));
+  }
+}
+BENCHMARK(BM_Format_ParseTime)->DenseRange(0, kNumFormats - 1);
+
+}  // namespace
diff --git a/absl/time/internal/cctz/src/time_zone_format_test.cc b/absl/time/internal/cctz/src/time_zone_format_test.cc
index 3a5f19ac8b1f..7d5b02ad3a68 100644
--- a/absl/time/internal/cctz/src/time_zone_format_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_format_test.cc
@@ -463,13 +463,12 @@ TEST(Format, ExtendedSecondOffset) {
 
   EXPECT_TRUE(load_time_zone("Europe/Moscow", &tz));
   tp = convert(civil_second(1919, 6, 30, 23, 59, 59), utc);
-  if (tz.lookup(tp).offset == 4 * 60 * 60) {
-    // We're likely dealing with zoneinfo that doesn't support really old
-    // timestamps, so Europe/Moscow never looks to be on local mean time.
-  } else {
-    TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
-    TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
-  }
+#if defined(__ANDROID__) && __ANDROID_API__ < 25
+  // Only Android 'N'.1 and beyond have this tz2016g transition.
+#else
+  TestFormatSpecifier(tp, tz, "%E*z", "+04:31:19");
+  TestFormatSpecifier(tp, tz, "%Ez", "+04:31");
+#endif
   tp += seconds(1);
   TestFormatSpecifier(tp, tz, "%E*z", "+04:00:00");
 }
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 f97eab0227eb..06b172a80323 100644
--- a/absl/time/internal/cctz/src/time_zone_lookup_test.cc
+++ b/absl/time/internal/cctz/src/time_zone_lookup_test.cc
@@ -693,7 +693,14 @@ TEST(TimeZones, LoadZonesConcurrently) {
 
   // Allow a small number of failures to account for skew between
   // the contents of kTimeZoneNames and the zoneinfo data source.
+#if defined(__ANDROID__)
+  // Cater to the possibility of using an even older zoneinfo data
+  // source when running on Android, where it is difficult to override
+  // the bionic tzdata provided by the test environment.
+  const std::size_t max_failures = 20;
+#else
   const std::size_t max_failures = 3;
+#endif
   std::set<std::string> failures;
   for (const auto& thread_failure : thread_failures) {
     failures.insert(thread_failure.begin(), thread_failure.end());
@@ -839,7 +846,7 @@ TEST(TimeZoneImpl, LocalTimeInFixed) {
   const time_zone tz = fixed_time_zone(offset);
   const auto tp = system_clock::from_time_t(0);
   ExpectTime(tp, tz, 1969, 12, 31, 15, 26, 13, offset.count(), false,
-             "UTC-083347");
+             "-083347");
   EXPECT_EQ(weekday::wednesday, get_weekday(civil_day(convert(tp, tz))));
 }
 
@@ -1098,6 +1105,9 @@ TEST(TimeZoneEdgeCase, PacificApia) {
 TEST(TimeZoneEdgeCase, AfricaCairo) {
   const time_zone tz = LoadZone("Africa/Cairo");
 
+#if defined(__ANDROID__) && __ANDROID_API__ < 21
+  // Only Android 'L' and beyond have this tz2014c transition.
+#else
   // An interesting case of midnight not existing.
   //
   //   1400191199 == Thu, 15 May 2014 23:59:59 +0200 (EET)
@@ -1106,11 +1116,15 @@ TEST(TimeZoneEdgeCase, AfricaCairo) {
   ExpectTime(tp, tz, 2014, 5, 15, 23, 59, 59, 2 * 3600, false, "EET");
   tp += seconds(1);
   ExpectTime(tp, tz, 2014, 5, 16, 1, 0, 0, 3 * 3600, true, "EEST");
+#endif
 }
 
 TEST(TimeZoneEdgeCase, AfricaMonrovia) {
   const time_zone tz = LoadZone("Africa/Monrovia");
 
+#if defined(__ANDROID__) && __ANDROID_API__ < 26
+  // Only Android 'O' and beyond have this tz2017b transition.
+#else
   // Strange offset change -00:44:30 -> +00:00:00 (non-DST)
   //
   //   63593069 == Thu,  6 Jan 1972 23:59:59 -0044 (MMT)
@@ -1119,6 +1133,7 @@ TEST(TimeZoneEdgeCase, AfricaMonrovia) {
   ExpectTime(tp, tz, 1972, 1, 6, 23, 59, 59, -44.5 * 60, false, "MMT");
   tp += seconds(1);
   ExpectTime(tp, tz, 1972, 1, 7, 0, 44, 30, 0 * 60, false, "GMT");
+#endif
 }
 
 TEST(TimeZoneEdgeCase, AmericaJamaica) {
diff --git a/absl/time/time.h b/absl/time/time.h
index c50d69a5689f..99c12bbd87ed 100644
--- a/absl/time/time.h
+++ b/absl/time/time.h
@@ -82,8 +82,15 @@ constexpr Duration MakeDuration(int64_t hi, uint32_t lo);
 constexpr Duration MakeDuration(int64_t hi, int64_t lo);
 constexpr int64_t kTicksPerNanosecond = 4;
 constexpr int64_t kTicksPerSecond = 1000 * 1000 * 1000 * kTicksPerNanosecond;
+template <std::intmax_t N>
+constexpr Duration FromInt64(int64_t v, std::ratio<1, N>);
+constexpr Duration FromInt64(int64_t v, std::ratio<60>);
+constexpr Duration FromInt64(int64_t v, std::ratio<3600>);
+template <typename T>
+using EnableIfIntegral = typename std::enable_if<
+    std::is_integral<T>::value || std::is_enum<T>::value, int>::type;
 template <typename T>
-using IsFloatingPoint =
+using EnableIfFloat =
     typename std::enable_if<std::is_floating_point<T>::value, int>::type;
 }  // namespace time_internal
 
@@ -178,15 +185,15 @@ inline Duration operator-(Duration lhs, Duration rhs) { return lhs -= rhs; }
 
 // Multiplicative Operators
 template <typename T>
-inline Duration operator*(Duration lhs, T rhs) {
+Duration operator*(Duration lhs, T rhs) {
   return lhs *= rhs;
 }
 template <typename T>
-inline Duration operator*(T lhs, Duration rhs) {
+Duration operator*(T lhs, Duration rhs) {
   return rhs *= lhs;
 }
 template <typename T>
-inline Duration operator/(Duration lhs, T rhs) {
+Duration operator/(Duration lhs, T rhs) {
   return lhs /= rhs;
 }
 inline int64_t operator/(Duration lhs, Duration rhs) {
@@ -322,27 +329,27 @@ constexpr Duration Hours(int64_t n);
 // Example:
 //   auto a = absl::Seconds(1.5);        // OK
 //   auto b = absl::Milliseconds(1500);  // BETTER
-template <typename T, time_internal::IsFloatingPoint<T> = 0>
+template <typename T, time_internal::EnableIfFloat<T> = 0>
 Duration Nanoseconds(T n) {
   return n * Nanoseconds(1);
 }
-template <typename T, time_internal::IsFloatingPoint<T> = 0>
+template <typename T, time_internal::EnableIfFloat<T> = 0>
 Duration Microseconds(T n) {
   return n * Microseconds(1);
 }
-template <typename T, time_internal::IsFloatingPoint<T> = 0>
+template <typename T, time_internal::EnableIfFloat<T> = 0>
 Duration Milliseconds(T n) {
   return n * Milliseconds(1);
 }
-template <typename T, time_internal::IsFloatingPoint<T> = 0>
+template <typename T, time_internal::EnableIfFloat<T> = 0>
 Duration Seconds(T n) {
   return n * Seconds(1);
 }
-template <typename T, time_internal::IsFloatingPoint<T> = 0>
+template <typename T, time_internal::EnableIfFloat<T> = 0>
 Duration Minutes(T n) {
   return n * Minutes(1);
 }
-template <typename T, time_internal::IsFloatingPoint<T> = 0>
+template <typename T, time_internal::EnableIfFloat<T> = 0>
 Duration Hours(T n) {
   return n * Hours(1);
 }
@@ -1154,10 +1161,16 @@ constexpr Duration FromInt64(int64_t v, std::ratio<1, N>) {
       v / N, v % N * kTicksPerNanosecond * 1000 * 1000 * 1000 / N);
 }
 constexpr Duration FromInt64(int64_t v, std::ratio<60>) {
-  return Minutes(v);
+  return (v <= std::numeric_limits<int64_t>::max() / 60 &&
+          v >= std::numeric_limits<int64_t>::min() / 60)
+             ? MakeDuration(v * 60)
+             : v > 0 ? InfiniteDuration() : -InfiniteDuration();
 }
 constexpr Duration FromInt64(int64_t v, std::ratio<3600>) {
-  return Hours(v);
+  return (v <= std::numeric_limits<int64_t>::max() / 3600 &&
+          v >= std::numeric_limits<int64_t>::min() / 3600)
+             ? MakeDuration(v * 3600)
+             : v > 0 ? InfiniteDuration() : -InfiniteDuration();
 }
 
 // IsValidRep64<T>(0) is true if the expression `int64_t{std::declval<T>()}` is
@@ -1220,6 +1233,24 @@ T ToChronoDuration(Duration d) {
 }
 
 }  // namespace time_internal
+constexpr Duration Nanoseconds(int64_t n) {
+  return time_internal::FromInt64(n, std::nano{});
+}
+constexpr Duration Microseconds(int64_t n) {
+  return time_internal::FromInt64(n, std::micro{});
+}
+constexpr Duration Milliseconds(int64_t n) {
+  return time_internal::FromInt64(n, std::milli{});
+}
+constexpr Duration Seconds(int64_t n) {
+  return time_internal::FromInt64(n, std::ratio<1>{});
+}
+constexpr Duration Minutes(int64_t n) {
+  return time_internal::FromInt64(n, std::ratio<60>{});
+}
+constexpr Duration Hours(int64_t n) {
+  return time_internal::FromInt64(n, std::ratio<3600>{});
+}
 
 constexpr bool operator<(Duration lhs, Duration rhs) {
   return time_internal::GetRepHi(lhs) != time_internal::GetRepHi(rhs)
@@ -1261,39 +1292,6 @@ constexpr Duration operator-(Duration d) {
                              time_internal::GetRepLo(d));
 }
 
-constexpr Duration Nanoseconds(int64_t n) {
-  return time_internal::MakeNormalizedDuration(
-      n / (1000 * 1000 * 1000),
-      n % (1000 * 1000 * 1000) * time_internal::kTicksPerNanosecond);
-}
-
-constexpr Duration Microseconds(int64_t n) {
-  return time_internal::MakeNormalizedDuration(
-      n / (1000 * 1000),
-      n % (1000 * 1000) * (1000 * time_internal::kTicksPerNanosecond));
-}
-
-constexpr Duration Milliseconds(int64_t n) {
-  return time_internal::MakeNormalizedDuration(
-      n / 1000, n % 1000 * (1000 * 1000 * time_internal::kTicksPerNanosecond));
-}
-
-constexpr Duration Seconds(int64_t n) { return time_internal::MakeDuration(n); }
-
-constexpr Duration Minutes(int64_t n) {
-  return (n <= std::numeric_limits<int64_t>::max() / 60 &&
-          n >= std::numeric_limits<int64_t>::min() / 60)
-             ? time_internal::MakeDuration(n * 60)
-             : n > 0 ? InfiniteDuration() : -InfiniteDuration();
-}
-
-constexpr Duration Hours(int64_t n) {
-  return (n <= std::numeric_limits<int64_t>::max() / 3600 &&
-          n >= std::numeric_limits<int64_t>::min() / 3600)
-             ? time_internal::MakeDuration(n * 3600)
-             : n > 0 ? InfiniteDuration() : -InfiniteDuration();
-}
-
 constexpr Duration InfiniteDuration() {
   return time_internal::MakeDuration(std::numeric_limits<int64_t>::max(), ~0U);
 }
diff --git a/absl/time/time_benchmark.cc b/absl/time/time_benchmark.cc
new file mode 100644
index 000000000000..e1009946cd58
--- /dev/null
+++ b/absl/time/time_benchmark.cc
@@ -0,0 +1,316 @@
+// Copyright 2018 The Abseil Authors.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "absl/time/time.h"
+
+#if !defined(_WIN32)
+#include <sys/time.h>
+#endif  // _WIN32
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <cstring>
+#include <ctime>
+#include <memory>
+#include <string>
+
+#include "absl/time/clock.h"
+#include "absl/time/internal/test_util.h"
+#include "benchmark/benchmark.h"
+
+namespace {
+
+//
+// Addition/Subtraction of a duration
+//
+
+void BM_Time_Arithmetic(benchmark::State& state) {
+  const absl::Duration nano = absl::Nanoseconds(1);
+  const absl::Duration sec = absl::Seconds(1);
+  absl::Time t = absl::UnixEpoch();
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(t += nano);
+    benchmark::DoNotOptimize(t -= sec);
+  }
+}
+BENCHMARK(BM_Time_Arithmetic);
+
+//
+// Time difference
+//
+
+void BM_Time_Difference(benchmark::State& state) {
+  absl::Time start = absl::Now();
+  absl::Time end = start + absl::Nanoseconds(1);
+  absl::Duration diff;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(diff += end - start);
+  }
+}
+BENCHMARK(BM_Time_Difference);
+
+//
+// ToDateTime
+//
+// In each "ToDateTime" benchmark we switch between two instants
+// separated by at least one transition in order to defeat any
+// internal caching of previous results (e.g., see local_time_hint_).
+//
+// The "UTC" variants use UTC instead of the Google/local time zone.
+//
+
+void BM_Time_ToDateTime_Absl(benchmark::State& state) {
+  const absl::TimeZone tz =
+      absl::time_internal::LoadTimeZone("America/Los_Angeles");
+  absl::Time t = absl::FromUnixSeconds(1384569027);
+  absl::Time t2 = absl::FromUnixSeconds(1418962578);
+  while (state.KeepRunning()) {
+    std::swap(t, t2);
+    t += absl::Seconds(1);
+    benchmark::DoNotOptimize(t.In(tz));
+  }
+}
+BENCHMARK(BM_Time_ToDateTime_Absl);
+
+void BM_Time_ToDateTime_Libc(benchmark::State& state) {
+  // No timezone support, so just use localtime.
+  time_t t = 1384569027;
+  time_t t2 = 1418962578;
+  while (state.KeepRunning()) {
+    std::swap(t, t2);
+    t += 1;
+    struct tm tm;
+#if !defined(_WIN32)
+    benchmark::DoNotOptimize(localtime_r(&t, &tm));
+#else   // _WIN32
+    benchmark::DoNotOptimize(localtime_s(&tm, &t));
+#endif  // _WIN32
+  }
+}
+BENCHMARK(BM_Time_ToDateTime_Libc);
+
+void BM_Time_ToDateTimeUTC_Absl(benchmark::State& state) {
+  const absl::TimeZone tz = absl::UTCTimeZone();
+  absl::Time t = absl::FromUnixSeconds(1384569027);
+  while (state.KeepRunning()) {
+    t += absl::Seconds(1);
+    benchmark::DoNotOptimize(t.In(tz));
+  }
+}
+BENCHMARK(BM_Time_ToDateTimeUTC_Absl);
+
+void BM_Time_ToDateTimeUTC_Libc(benchmark::State& state) {
+  time_t t = 1384569027;
+  while (state.KeepRunning()) {
+    t += 1;
+    struct tm tm;
+#if !defined(_WIN32)
+    benchmark::DoNotOptimize(gmtime_r(&t, &tm));
+#else   // _WIN32
+    benchmark::DoNotOptimize(gmtime_s(&tm, &t));
+#endif  // _WIN32
+  }
+}
+BENCHMARK(BM_Time_ToDateTimeUTC_Libc);
+
+//
+// FromUnixMicros
+//
+
+void BM_Time_FromUnixMicros(benchmark::State& state) {
+  int i = 0;
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::FromUnixMicros(i));
+    ++i;
+  }
+}
+BENCHMARK(BM_Time_FromUnixMicros);
+
+void BM_Time_ToUnixNanos(benchmark::State& state) {
+  const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(ToUnixNanos(t));
+  }
+}
+BENCHMARK(BM_Time_ToUnixNanos);
+
+void BM_Time_ToUnixMicros(benchmark::State& state) {
+  const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(ToUnixMicros(t));
+  }
+}
+BENCHMARK(BM_Time_ToUnixMicros);
+
+void BM_Time_ToUnixMillis(benchmark::State& state) {
+  const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(ToUnixMillis(t));
+  }
+}
+BENCHMARK(BM_Time_ToUnixMillis);
+
+void BM_Time_ToUnixSeconds(benchmark::State& state) {
+  const absl::Time t = absl::UnixEpoch() + absl::Seconds(123);
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToUnixSeconds(t));
+  }
+}
+BENCHMARK(BM_Time_ToUnixSeconds);
+
+//
+// FromDateTime
+//
+// In each "FromDateTime" benchmark we switch between two YMDhms
+// values separated by at least one transition in order to defeat any
+// internal caching of previous results (e.g., see time_local_hint_).
+//
+// The "UTC" variants use UTC instead of the Google/local time zone.
+// The "Day0" variants require normalization of the day of month.
+//
+
+void BM_Time_FromDateTime_Absl(benchmark::State& state) {
+  const absl::TimeZone tz =
+      absl::time_internal::LoadTimeZone("America/Los_Angeles");
+  int i = 0;
+  while (state.KeepRunning()) {
+    if ((i & 1) == 0) {
+      absl::FromDateTime(2014, 12, 18, 20, 16, 18, tz);
+    } else {
+      absl::FromDateTime(2013, 11, 15, 18, 30, 27, tz);
+    }
+    ++i;
+  }
+}
+BENCHMARK(BM_Time_FromDateTime_Absl);
+
+void BM_Time_FromDateTime_Libc(benchmark::State& state) {
+  // No timezone support, so just use localtime.
+  int i = 0;
+  while (state.KeepRunning()) {
+    struct tm tm;
+    if ((i & 1) == 0) {
+      tm.tm_year = 2014 - 1900;
+      tm.tm_mon = 12 - 1;
+      tm.tm_mday = 18;
+      tm.tm_hour = 20;
+      tm.tm_min = 16;
+      tm.tm_sec = 18;
+    } else {
+      tm.tm_year = 2013 - 1900;
+      tm.tm_mon = 11 - 1;
+      tm.tm_mday = 15;
+      tm.tm_hour = 18;
+      tm.tm_min = 30;
+      tm.tm_sec = 27;
+    }
+    tm.tm_isdst = -1;
+    mktime(&tm);
+    ++i;
+  }
+}
+BENCHMARK(BM_Time_FromDateTime_Libc);
+
+void BM_Time_FromDateTimeUTC_Absl(benchmark::State& state) {
+  const absl::TimeZone tz = absl::UTCTimeZone();
+  while (state.KeepRunning()) {
+    FromDateTime(2014, 12, 18, 20, 16, 18, tz);
+  }
+}
+BENCHMARK(BM_Time_FromDateTimeUTC_Absl);
+
+void BM_Time_FromDateTimeDay0_Absl(benchmark::State& state) {
+  const absl::TimeZone tz =
+      absl::time_internal::LoadTimeZone("America/Los_Angeles");
+  int i = 0;
+  while (state.KeepRunning()) {
+    if ((i & 1) == 0) {
+      absl::FromDateTime(2014, 12, 0, 20, 16, 18, tz);
+    } else {
+      absl::FromDateTime(2013, 11, 0, 18, 30, 27, tz);
+    }
+    ++i;
+  }
+}
+BENCHMARK(BM_Time_FromDateTimeDay0_Absl);
+
+void BM_Time_FromDateTimeDay0_Libc(benchmark::State& state) {
+  // No timezone support, so just use localtime.
+  int i = 0;
+  while (state.KeepRunning()) {
+    struct tm tm;
+    if ((i & 1) == 0) {
+      tm.tm_year = 2014 - 1900;
+      tm.tm_mon = 12 - 1;
+      tm.tm_mday = 0;
+      tm.tm_hour = 20;
+      tm.tm_min = 16;
+      tm.tm_sec = 18;
+    } else {
+      tm.tm_year = 2013 - 1900;
+      tm.tm_mon = 11 - 1;
+      tm.tm_mday = 0;
+      tm.tm_hour = 18;
+      tm.tm_min = 30;
+      tm.tm_sec = 27;
+    }
+    tm.tm_isdst = -1;
+    mktime(&tm);
+    ++i;
+  }
+}
+BENCHMARK(BM_Time_FromDateTimeDay0_Libc);
+
+//
+// To/FromTimespec
+//
+
+void BM_Time_ToTimespec(benchmark::State& state) {
+  absl::Time now = absl::Now();
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::ToTimespec(now));
+  }
+}
+BENCHMARK(BM_Time_ToTimespec);
+
+void BM_Time_FromTimespec(benchmark::State& state) {
+  timespec ts = absl::ToTimespec(absl::Now());
+  while (state.KeepRunning()) {
+    if (++ts.tv_nsec == 1000 * 1000 * 1000) {
+      ++ts.tv_sec;
+      ts.tv_nsec = 0;
+    }
+    benchmark::DoNotOptimize(absl::TimeFromTimespec(ts));
+  }
+}
+BENCHMARK(BM_Time_FromTimespec);
+
+//
+// Comparison with InfiniteFuture/Past
+//
+
+void BM_Time_InfiniteFuture(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::InfiniteFuture());
+  }
+}
+BENCHMARK(BM_Time_InfiniteFuture);
+
+void BM_Time_InfinitePast(benchmark::State& state) {
+  while (state.KeepRunning()) {
+    benchmark::DoNotOptimize(absl::InfinitePast());
+  }
+}
+BENCHMARK(BM_Time_InfinitePast);
+
+}  // namespace