#include "base/time/time.h"
#include <stdint.h>
#include <time.h>
#include <limits>
#include <optional>
#include <string>
#include "base/build_time.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/environment.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/to_string.h"
#include "base/test/gtest_util.h"
#include "base/threading/platform_thread.h"
#include "base/time/time_override.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/icu/source/common/unicode/utypes.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/jni_android.h"
#elif BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_CHROMEOS)
#include "base/test/icu_test_util.h"
#elif BUILDFLAG(IS_WIN)
#include <windows.h>
#endif
namespace base {
namespace {
#if BUILDFLAG(IS_FUCHSIA)
const char kHonoluluTimeZoneId[] = "Pacific/Honolulu";
const int kHonoluluOffsetHours = -10;
const int kHonoluluOffsetSeconds = kHonoluluOffsetHours * 60 * 60;
#endif
#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_CHROMEOS)
const char kThaiLocale[] = "th-TH";
const char kBangkokTimeZoneId[] = "Asia/Bangkok";
std::optional<base::TimeDelta> GetTimeZoneOffsetAtTime(const char* timezone_id,
Time time) {
std::unique_ptr<icu::TimeZone> tz(icu::TimeZone::createTimeZone(timezone_id));
if (*tz == icu::TimeZone::getUnknown()) {
return {};
}
int32_t raw_offset = 0;
int32_t dst_offset = 0;
UErrorCode ec = U_ZERO_ERROR;
tz->getOffset(time.InSecondsFSinceUnixEpoch(), false, raw_offset, dst_offset,
ec);
if (!U_SUCCESS(ec)) {
return {};
}
return base::Milliseconds(raw_offset + dst_offset);
}
TimeDelta TimePassedAfterMidnight(const Time::Exploded& time) {
return base::Hours(time.hour) + base::Minutes(time.minute) +
base::Seconds(time.second) + base::Milliseconds(time.millisecond);
}
class ScopedLibcTZ {
public:
explicit ScopedLibcTZ(const std::string& timezone) {
auto env = base::Environment::Create();
std::string old_timezone_value;
if (env->GetVar(kTZ, &old_timezone_value)) {
old_timezone_ = old_timezone_value;
}
if (!env->SetVar(kTZ, timezone)) {
success_ = false;
}
tzset();
}
~ScopedLibcTZ() {
auto env = base::Environment::Create();
if (old_timezone_.has_value()) {
CHECK(env->SetVar(kTZ, old_timezone_.value()));
} else {
CHECK(env->UnSetVar(kTZ));
}
}
ScopedLibcTZ(const ScopedLibcTZ& other) = delete;
ScopedLibcTZ& operator=(const ScopedLibcTZ& other) = delete;
bool is_success() const { return success_; }
private:
static constexpr char kTZ[] = "TZ";
bool success_ = true;
std::optional<std::string> old_timezone_;
};
constexpr char ScopedLibcTZ::kTZ[];
#endif
TEST(TimeTestOutOfBounds, FromExplodedOutOfBoundsTime) { … }
class TimeTest : public testing::Test { … };
TEST_F(TimeTest, DeltaSinceWindowsEpoch) { … }
TEST_F(TimeTest, TimeT) { … }
TEST_F(TimeTest, UTCTimeT) { … }
TEST_F(TimeTest, LocalTimeT) { … }
TEST_F(TimeTest, JsTime) { … }
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
TEST_F(TimeTest, FromTimeVal) { … }
#endif
TEST_F(TimeTest, FromExplodedWithMilliseconds) { … }
TEST_F(TimeTest, ZeroIsSymmetric) { … }
TEST_F(TimeTest, LocalExplode) { … }
TEST_F(TimeTest, UTCExplode) { … }
TEST_F(TimeTest, UTCMidnight) { … }
TEST_F(TimeTest, LocalMidnight) { … }
#if BUILDFLAG(IS_FUCHSIA)
TEST_F(TimeTest, LocalExplodeIsLocal) {
test::ScopedRestoreDefaultTimezone honolulu_time(kHonoluluTimeZoneId);
Time comparison_time_utc(comparison_time_local_);
Time::Exploded utc_exploded;
comparison_time_utc.UTCExplode(&utc_exploded);
Time::Exploded local_exploded;
comparison_time_utc.LocalExplode(&local_exploded);
EXPECT_EQ(utc_exploded.year, local_exploded.year);
EXPECT_EQ(utc_exploded.month, local_exploded.month);
EXPECT_EQ(utc_exploded.day_of_week, local_exploded.day_of_week);
EXPECT_EQ(utc_exploded.day_of_month, local_exploded.day_of_month);
EXPECT_EQ(utc_exploded.hour + kHonoluluOffsetHours, local_exploded.hour);
EXPECT_EQ(utc_exploded.minute, local_exploded.minute);
EXPECT_EQ(utc_exploded.second, local_exploded.second);
EXPECT_EQ(utc_exploded.millisecond, local_exploded.millisecond);
Time time_from_local_exploded;
EXPECT_TRUE(
Time::FromLocalExploded(local_exploded, &time_from_local_exploded));
EXPECT_EQ(comparison_time_utc, time_from_local_exploded);
Time time_from_utc_exploded;
EXPECT_TRUE(Time::FromUTCExploded(local_exploded, &time_from_utc_exploded));
EXPECT_EQ(comparison_time_utc + Hours(kHonoluluOffsetHours),
time_from_utc_exploded);
}
TEST_F(TimeTest, LocalMidnightIsLocal) {
test::ScopedRestoreDefaultTimezone honolulu_time(kHonoluluTimeZoneId);
Time comparison_time_utc(comparison_time_local_);
Time::Exploded utc_midnight_exploded;
comparison_time_utc.UTCMidnight().UTCExplode(&utc_midnight_exploded);
Time::Exploded local_midnight_utc_exploded;
comparison_time_utc.LocalMidnight().UTCExplode(&local_midnight_utc_exploded);
EXPECT_EQ(utc_midnight_exploded.year, local_midnight_utc_exploded.year);
EXPECT_EQ(utc_midnight_exploded.month, local_midnight_utc_exploded.month);
EXPECT_EQ(utc_midnight_exploded.day_of_week,
local_midnight_utc_exploded.day_of_week);
EXPECT_EQ(utc_midnight_exploded.day_of_month,
local_midnight_utc_exploded.day_of_month);
EXPECT_EQ(0, utc_midnight_exploded.hour);
EXPECT_EQ(0 - kHonoluluOffsetHours, local_midnight_utc_exploded.hour);
EXPECT_EQ(0, local_midnight_utc_exploded.minute);
EXPECT_EQ(0, local_midnight_utc_exploded.second);
EXPECT_EQ(0, local_midnight_utc_exploded.millisecond);
Time::Exploded local_midnight_exploded;
comparison_time_utc.LocalMidnight().LocalExplode(&local_midnight_exploded);
EXPECT_EQ(utc_midnight_exploded.year, local_midnight_exploded.year);
EXPECT_EQ(utc_midnight_exploded.month, local_midnight_exploded.month);
EXPECT_EQ(utc_midnight_exploded.day_of_week,
local_midnight_exploded.day_of_week);
EXPECT_EQ(utc_midnight_exploded.day_of_month,
local_midnight_exploded.day_of_month);
EXPECT_EQ(0, local_midnight_exploded.hour);
EXPECT_EQ(0, local_midnight_exploded.minute);
EXPECT_EQ(0, local_midnight_exploded.second);
EXPECT_EQ(0, local_midnight_exploded.millisecond);
}
#endif
TEST_F(TimeTest, ParseTimeTest1) { … }
TEST_F(TimeTest, DayOfWeekSunday) { … }
TEST_F(TimeTest, DayOfWeekWednesday) { … }
TEST_F(TimeTest, DayOfWeekSaturday) { … }
TEST_F(TimeTest, ParseTimeTest2) { … }
TEST_F(TimeTest, ParseTimeTest3) { … }
TEST_F(TimeTest, ParseTimeTest4) { … }
TEST_F(TimeTest, ParseTimeTest5) { … }
TEST_F(TimeTest, ParseTimeTest6) { … }
TEST_F(TimeTest, ParseTimeTest7) { … }
TEST_F(TimeTest, ParseTimeTest8) { … }
TEST_F(TimeTest, ParseTimeTest9) { … }
TEST_F(TimeTest, ParseTimeTest10) { … }
TEST_F(TimeTest, ParseTimeTest11) { … }
TEST_F(TimeTest, ParseTimeTestEpoch0) { … }
TEST_F(TimeTest, ParseTimeTestEpoch1) { … }
TEST_F(TimeTest, ParseTimeTestEpoch2) { … }
TEST_F(TimeTest, ParseTimeTestEpochNeg1) { … }
TEST_F(TimeTest, ParseTimeTestEpochNotNeg1) { … }
TEST_F(TimeTest, ParseTimeTestEpochNeg2) { … }
TEST_F(TimeTest, ParseTimeTestEpoch1960) { … }
TEST_F(TimeTest, ParseTimeTestEmpty) { … }
TEST_F(TimeTest, ParseTimeTestInvalidString) { … }
TEST_F(TimeTest, ExplodeBeforeUnixEpoch) { … }
TEST_F(TimeTest, Max) { … }
TEST_F(TimeTest, MaxConversions) { … }
TEST_F(TimeTest, Min) { … }
TEST_F(TimeTest, TimeTOverflow) { … }
#if BUILDFLAG(IS_ANDROID)
TEST_F(TimeTest, FromLocalExplodedCrashOnAndroid) {
Time::Exploded midnight = {2013,
10,
0,
13,
0,
0,
0,
};
static char buffer[] = "TZ=America/Santiago";
putenv(buffer);
tzset();
Time t;
EXPECT_TRUE(Time::FromLocalExploded(midnight, &t));
EXPECT_EQ(1381633200, t.ToTimeT());
}
#endif
TEST_F(TimeTest, Explode_Y10KCompliance) { … }
#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_CHROMEOS)
TEST_F(TimeTest, UTCExplodedIsLocaleIndependent) {
test::ScopedRestoreICUDefaultLocale scoped_icu_locale(kThaiLocale);
test::ScopedRestoreDefaultTimezone scoped_timezone(kBangkokTimeZoneId);
ScopedLibcTZ scoped_libc_tz(kBangkokTimeZoneId);
ASSERT_TRUE(scoped_libc_tz.is_success());
Time::Exploded utc_exploded_orig;
utc_exploded_orig.year = 2020;
utc_exploded_orig.month = 7;
utc_exploded_orig.day_of_week = 5;
utc_exploded_orig.day_of_month = 3;
utc_exploded_orig.hour = 12;
utc_exploded_orig.minute = 0;
utc_exploded_orig.second = 0;
utc_exploded_orig.millisecond = 0;
Time time;
ASSERT_TRUE(base::Time::FromUTCExploded(utc_exploded_orig, &time));
Time::Exploded utc_exploded;
time.UTCExplode(&utc_exploded);
EXPECT_EQ(utc_exploded_orig.year, utc_exploded.year);
EXPECT_EQ(utc_exploded_orig.month, utc_exploded.month);
EXPECT_EQ(utc_exploded_orig.day_of_week, utc_exploded.day_of_week);
EXPECT_EQ(utc_exploded_orig.day_of_month, utc_exploded.day_of_month);
EXPECT_EQ(utc_exploded_orig.hour, utc_exploded.hour);
EXPECT_EQ(utc_exploded_orig.minute, utc_exploded.minute);
EXPECT_EQ(utc_exploded_orig.second, utc_exploded.second);
EXPECT_EQ(utc_exploded_orig.millisecond, utc_exploded.millisecond);
}
TEST_F(TimeTest, LocalExplodedIsLocaleIndependent) {
test::ScopedRestoreICUDefaultLocale scoped_icu_locale(kThaiLocale);
test::ScopedRestoreDefaultTimezone scoped_timezone(kBangkokTimeZoneId);
ScopedLibcTZ scoped_libc_tz(kBangkokTimeZoneId);
ASSERT_TRUE(scoped_libc_tz.is_success());
Time::Exploded utc_exploded_orig;
utc_exploded_orig.year = 2020;
utc_exploded_orig.month = 7;
utc_exploded_orig.day_of_week = 5;
utc_exploded_orig.day_of_month = 3;
utc_exploded_orig.hour = 12;
utc_exploded_orig.minute = 0;
utc_exploded_orig.second = 0;
utc_exploded_orig.millisecond = 0;
Time time;
ASSERT_TRUE(base::Time::FromUTCExploded(utc_exploded_orig, &time));
std::optional<TimeDelta> expected_delta =
GetTimeZoneOffsetAtTime(kBangkokTimeZoneId, time);
ASSERT_TRUE(expected_delta.has_value());
ASSERT_LT(*expected_delta, base::Hours(12));
Time::Exploded local_exploded;
time.LocalExplode(&local_exploded);
TimeDelta actual_delta = TimePassedAfterMidnight(local_exploded) -
TimePassedAfterMidnight(utc_exploded_orig);
EXPECT_EQ(utc_exploded_orig.year, local_exploded.year);
EXPECT_EQ(utc_exploded_orig.month, local_exploded.month);
EXPECT_EQ(utc_exploded_orig.day_of_week, local_exploded.day_of_week);
EXPECT_EQ(utc_exploded_orig.day_of_month, local_exploded.day_of_month);
EXPECT_EQ(actual_delta, *expected_delta);
}
#endif
TEST_F(TimeTest, FromExploded_MinMax) { … }
class TimeOverride { … };
Time TimeOverride::now_time_;
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_NowOverride …
#else
#define MAYBE_NowOverride …
#endif
TEST_F(TimeTest, MAYBE_NowOverride) { … }
#undef MAYBE_NowOverride
#if BUILDFLAG(IS_FUCHSIA)
TEST(ZxTimeTest, ToFromConversions) {
Time unix_epoch = Time::UnixEpoch();
EXPECT_EQ(unix_epoch.ToZxTime(), 0);
EXPECT_EQ(Time::FromZxTime(6000000000), unix_epoch + Seconds(6));
TimeTicks ticks_now = TimeTicks::Now();
EXPECT_GE(ticks_now.ToZxTime(), 0);
TimeTicks ticks_later = ticks_now + Seconds(2);
EXPECT_EQ((ticks_later.ToZxTime() - ticks_now.ToZxTime()), 2000000000);
EXPECT_EQ(TimeTicks::FromZxTime(3000000000), TimeTicks() + Seconds(3));
EXPECT_EQ(TimeDelta().ToZxDuration(), 0);
EXPECT_EQ(TimeDelta::FromZxDuration(0), TimeDelta());
EXPECT_EQ(Seconds(2).ToZxDuration(), 2000000000);
EXPECT_EQ(TimeDelta::FromZxDuration(4000000000), Seconds(4));
}
#endif
TEST(TimeTicks, Deltas) { … }
static void HighResClockTest(TimeTicks (*GetTicks)()) { … }
TEST(TimeTicks, HighRes) { … }
class TimeTicksOverride { … };
TimeTicks TimeTicksOverride::now_ticks_;
TEST(TimeTicks, NowOverride) { … }
class ThreadTicksOverride { … };
ThreadTicks ThreadTicksOverride::now_ticks_;
#if BUILDFLAG(IS_IOS)
#define MAYBE_NowOverride …
#else
#define MAYBE_NowOverride …
#endif
TEST(ThreadTicks, MAYBE_NowOverride) { … }
TEST(ThreadTicks, ThreadNow) { … }
TEST(TimeTicks, SnappedToNextTickBasic) { … }
TEST(TimeTicks, SnappedToNextTickOverflow) { … }
#if BUILDFLAG(IS_ANDROID)
TEST(TimeTicks, Android_FromUptimeMillis_ClocksMatch) {
JNIEnv* const env = android::AttachCurrentThread();
android::ScopedJavaLocalRef<jclass> clazz(
android::GetClass(env, "android/os/SystemClock"));
ASSERT_TRUE(clazz.obj());
const jmethodID method_id =
android::MethodID::Get<android::MethodID::TYPE_STATIC>(
env, clazz.obj(), "uptimeMillis", "()J");
ASSERT_FALSE(!method_id);
const TimeTicks lower_bound_ticks = TimeTicks::Now() - Milliseconds(1);
const TimeTicks converted_ticks = TimeTicks::FromUptimeMillis(
env->CallStaticLongMethod(clazz.obj(), method_id));
const TimeTicks upper_bound_ticks = TimeTicks::Now();
EXPECT_LE(lower_bound_ticks, converted_ticks);
EXPECT_GE(upper_bound_ticks, converted_ticks);
}
TEST(TimeTicks, Android_FromJavaNanoTime_ClocksMatch) {
JNIEnv* const env = android::AttachCurrentThread();
android::ScopedJavaLocalRef<jclass> clazz(
android::GetClass(env, "java/lang/System"));
ASSERT_TRUE(clazz.obj());
const jmethodID method_id =
android::MethodID::Get<android::MethodID::TYPE_STATIC>(env, clazz.obj(),
"nanoTime", "()J");
ASSERT_FALSE(!method_id);
const TimeTicks lower_bound_ticks = TimeTicks::Now();
const TimeTicks converted_ticks = TimeTicks::FromJavaNanoTime(
env->CallStaticLongMethod(clazz.obj(), method_id));
const TimeTicks upper_bound_ticks = TimeTicks::Now() + Microseconds(1);
EXPECT_LE(lower_bound_ticks, converted_ticks);
EXPECT_GE(upper_bound_ticks, converted_ticks);
}
#endif
class LiveTicksOverride { … };
LiveTicks LiveTicksOverride::now_ticks_;
TEST(LiveTicks, NowOverride) { … }
TEST(TimeDelta, FromAndIn) { … }
TEST(TimeDelta, InRoundsTowardsZero) { … }
TEST(TimeDelta, InDaysFloored) { … }
TEST(TimeDelta, InSecondsFloored) { … }
TEST(TimeDelta, InMillisecondsRoundedUp) { … }
TEST(TimeDelta, InXXXOverflow) { … }
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
TEST(TimeDelta, TimeSpecConversion) { … }
#endif
TEST(TimeDelta, WindowsEpoch) { … }
TEST(TimeDelta, Hz) { … }
TEST(TimeDelta, Magnitude) { … }
TEST(TimeDelta, ZeroMinMax) { … }
TEST(TimeDelta, MaxConversions) { … }
TEST(TimeDelta, MinConversions) { … }
TEST(TimeDelta, FiniteMaxMin) { … }
TEST(TimeDelta, NumericOperators) { … }
TEST(TimeDelta, TimeDeltaOperators) { … }
TEST(TimeDelta, Overflows) { … }
TEST(TimeDelta, CeilToMultiple) { … }
TEST(TimeDelta, FloorToMultiple) { … }
TEST(TimeDelta, RoundToMultiple) { … }
TEST(TimeBase, AddSubDeltaSaturates) { … }
TEST(TimeBase, AddSubInfinities) { … }
constexpr TimeTicks TestTimeTicksConstexprCopyAssignment() { … }
TEST(TimeTicks, ConstexprAndTriviallyCopiable) { … }
constexpr ThreadTicks TestThreadTicksConstexprCopyAssignment() { … }
TEST(ThreadTicks, ConstexprAndTriviallyCopiable) { … }
constexpr TimeDelta TestTimeDeltaConstexprCopyAssignment() { … }
TEST(TimeDelta, ConstexprAndTriviallyCopiable) { … }
TEST(TimeDeltaLogging, DCheckEqCompiles) { … }
TEST(TimeDeltaLogging, EmptyIsZero) { … }
TEST(TimeDeltaLogging, FiveHundredMs) { … }
TEST(TimeDeltaLogging, MinusTenSeconds) { … }
TEST(TimeDeltaLogging, DoesNotMessUpFormattingFlags) { … }
TEST(TimeDeltaLogging, DoesNotMakeStreamBad) { … }
TEST(TimeLogging, DCheckEqCompiles) { … }
TEST(TimeLogging, ChromeBirthdate) { … }
TEST(TimeLogging, Microseconds) { … }
TEST(TimeLogging, DoesNotMessUpFormattingFlags) { … }
TEST(TimeLogging, DoesNotMakeStreamBad) { … }
TEST(TimeTicksLogging, DCheckEqCompiles) { … }
TEST(TimeTicksLogging, ZeroTime) { … }
TEST(TimeTicksLogging, FortyYearsLater) { … }
TEST(TimeTicksLogging, DoesNotMessUpFormattingFlags) { … }
TEST(TimeTicksLogging, DoesNotMakeStreamBad) { … }
}
}