// Copyright 2025 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. #include "wv_date_time.h" #include // #include #include #include "wv_duration.h" #include "wv_timestamp.h" namespace wvutil { namespace test { namespace { // This is a Unix time chosen for testing. // Friday, April 19th, 2024 13:36:18.507 // April 19th is the 110th day of the year when a leap year. constexpr uint64_t kTestUnixTimeS = 1713533778; constexpr uint64_t kTestUnixTimeMs = 1713533778507; constexpr Seconds kTestTimeS = Seconds(kTestUnixTimeS); constexpr Milliseconds kTestTimeMs = Milliseconds(kTestUnixTimeMs); constexpr uint32_t kTestYear = 2024; constexpr uint32_t kTestMonth = 4; // April constexpr uint32_t kTestDay = 19; constexpr uint32_t kTestHour = 13; constexpr uint32_t kTestMinute = 36; constexpr uint32_t kTestSecond = 18; constexpr uint32_t kTestMs = 507; constexpr uint32_t kTestDayOfWeek = 6; // Friday constexpr uint32_t kTestDayOfYear = 110; constexpr int64_t kOneMinuteS = 60; constexpr int64_t kOneHourS = kOneMinuteS * 60; constexpr int64_t kOneDayS = kOneHourS * 24; constexpr int64_t kOneCommonYearS = kOneDayS * 365; constexpr int64_t kOneLeapYearS = kOneDayS * 366; constexpr int64_t kOneSecondMs = 1000; constexpr int64_t kOneMinuteMs = kOneSecondMs * 60; constexpr int64_t kOneHourMs = kOneMinuteMs * 60; constexpr int64_t kOneDayMs = kOneHourMs * 24; uint32_t DayOfWeekAddition(uint32_t day_of_week, uint32_t inc) { return ((day_of_week - 1 + inc) % 7) + 1; } uint32_t DayOfWeekSubtraction(uint32_t day_of_week, uint32_t dec) { const uint32_t reverse_day_of_week = (8 - day_of_week); const uint32_t reverse_new_day_of_week = DayOfWeekAddition(reverse_day_of_week, dec); return (8 - reverse_new_day_of_week); } } // namespace TEST(WvDateTimeUtilTest, FromUnixSeconds) { const DateTime datetime = DateTime::FromUnixSeconds(kTestUnixTimeS, kTestMs); ASSERT_TRUE(datetime.IsSet()); EXPECT_EQ(datetime.epoch_seconds(), kTestTimeS); EXPECT_EQ(datetime.epoch_milliseconds(), kTestTimeMs); EXPECT_EQ(datetime.year(), kTestYear); EXPECT_EQ(datetime.month(), kTestMonth); EXPECT_EQ(datetime.day(), kTestDay); EXPECT_EQ(datetime.day_of_year(), kTestDayOfYear); EXPECT_EQ(datetime.day_of_week(), kTestDayOfWeek); EXPECT_EQ(datetime.hour(), kTestHour); EXPECT_EQ(datetime.minute(), kTestMinute); EXPECT_EQ(datetime.second(), kTestSecond); EXPECT_EQ(datetime.millisecond(), kTestMs); } TEST(WvDateTimeUtilTest, FromUnixMilliseconds) { const DateTime datetime = DateTime::FromUnixMilliseconds(kTestUnixTimeMs); ASSERT_TRUE(datetime.IsSet()); EXPECT_EQ(datetime.epoch_seconds(), kTestTimeS); EXPECT_EQ(datetime.epoch_milliseconds(), kTestTimeMs); EXPECT_EQ(datetime.year(), kTestYear); EXPECT_EQ(datetime.month(), kTestMonth); EXPECT_EQ(datetime.day(), kTestDay); EXPECT_EQ(datetime.day_of_year(), kTestDayOfYear); EXPECT_EQ(datetime.day_of_week(), kTestDayOfWeek); EXPECT_EQ(datetime.hour(), kTestHour); EXPECT_EQ(datetime.minute(), kTestMinute); EXPECT_EQ(datetime.second(), kTestSecond); EXPECT_EQ(datetime.millisecond(), kTestMs); } TEST(WvDateTimeUtilTest, Min) { // Thursday, January 1st, 1970 00:00:00.001 const DateTime datetime = DateTime::Min(); ASSERT_TRUE(datetime.IsSet()); EXPECT_EQ(datetime.epoch_seconds(), Seconds(0)); EXPECT_EQ(datetime.epoch_milliseconds(), Milliseconds(1)); EXPECT_EQ(datetime.year(), 1970u); EXPECT_EQ(datetime.month(), 1u); EXPECT_EQ(datetime.day(), 1u); EXPECT_EQ(datetime.day_of_year(), 1u); EXPECT_EQ(datetime.day_of_week(), 5u); EXPECT_EQ(datetime.hour(), 0u); EXPECT_EQ(datetime.minute(), 0u); EXPECT_EQ(datetime.second(), 0u); EXPECT_EQ(datetime.millisecond(), 1u); } TEST(WvDateTimeUtilTest, Max) { constexpr int64_t kMaxEpochTimeS = 253402300799; constexpr int64_t kMaxEpochTimeMs = (kMaxEpochTimeS * 1000) + 999; // Friday, December 31st, 9999 23:59:59.999 const DateTime datetime = DateTime::Max(); ASSERT_TRUE(datetime.IsSet()); EXPECT_EQ(datetime.epoch_seconds(), Seconds(kMaxEpochTimeS)); EXPECT_EQ(datetime.epoch_milliseconds(), Milliseconds(kMaxEpochTimeMs)); EXPECT_EQ(datetime.year(), 9999u); EXPECT_EQ(datetime.month(), 12u); EXPECT_EQ(datetime.day(), 31u); EXPECT_EQ(datetime.day_of_year(), 365u); EXPECT_EQ(datetime.day_of_week(), 6u); EXPECT_EQ(datetime.hour(), 23u); EXPECT_EQ(datetime.minute(), 59u); EXPECT_EQ(datetime.second(), 59u); EXPECT_EQ(datetime.millisecond(), 999u); } TEST(WvDateTimeUtilTest, Clear) { DateTime datetime = DateTime::FromUnixMilliseconds(kTestUnixTimeMs); ASSERT_TRUE(datetime.IsSet()); datetime.Clear(); EXPECT_FALSE(datetime.IsSet()); EXPECT_EQ(datetime.year(), 0u); EXPECT_EQ(datetime.month(), 0u); EXPECT_EQ(datetime.day(), 0u); EXPECT_EQ(datetime.day_of_year(), 0u); EXPECT_EQ(datetime.day_of_week(), 0u); EXPECT_EQ(datetime.hour(), 0u); EXPECT_EQ(datetime.minute(), 0u); EXPECT_EQ(datetime.second(), 0u); EXPECT_EQ(datetime.millisecond(), 0u); } TEST(WvDateTimeUtilTest, Comparison) { const DateTime datetime_a = DateTime::Min(); const DateTime datetime_b = DateTime::FromUnixMilliseconds(kTestUnixTimeMs - kOneDayMs); const DateTime datetime_c = DateTime::FromUnixMilliseconds(kTestUnixTimeMs); const DateTime datetime_d = DateTime::FromUnixMilliseconds(kTestUnixTimeMs + 1); const DateTime datetime_e = DateTime::Max(); // Equality EXPECT_EQ(datetime_a, datetime_a); EXPECT_EQ(datetime_b, datetime_b); EXPECT_EQ(datetime_c, datetime_c); EXPECT_EQ(datetime_d, datetime_d); EXPECT_EQ(datetime_e, datetime_e); // Inequality. EXPECT_NE(datetime_a, datetime_b); EXPECT_NE(datetime_a, datetime_c); EXPECT_NE(datetime_a, datetime_d); EXPECT_NE(datetime_a, datetime_e); EXPECT_NE(datetime_b, datetime_c); EXPECT_NE(datetime_b, datetime_d); EXPECT_NE(datetime_b, datetime_e); EXPECT_NE(datetime_c, datetime_d); EXPECT_NE(datetime_c, datetime_e); EXPECT_NE(datetime_d, datetime_e); // Less than EXPECT_LT(datetime_a, datetime_b); EXPECT_LT(datetime_a, datetime_c); EXPECT_LT(datetime_a, datetime_d); EXPECT_LT(datetime_a, datetime_e); EXPECT_LT(datetime_b, datetime_c); EXPECT_LT(datetime_b, datetime_d); EXPECT_LT(datetime_b, datetime_e); EXPECT_LT(datetime_c, datetime_d); EXPECT_LT(datetime_c, datetime_e); EXPECT_LT(datetime_d, datetime_e); // Less than or equal. EXPECT_LE(datetime_a, datetime_a); EXPECT_LE(datetime_a, datetime_b); EXPECT_LE(datetime_a, datetime_c); EXPECT_LE(datetime_a, datetime_d); EXPECT_LE(datetime_a, datetime_e); EXPECT_LE(datetime_b, datetime_b); EXPECT_LE(datetime_b, datetime_c); EXPECT_LE(datetime_b, datetime_d); EXPECT_LE(datetime_b, datetime_e); EXPECT_LE(datetime_c, datetime_c); EXPECT_LE(datetime_c, datetime_d); EXPECT_LE(datetime_c, datetime_e); EXPECT_LE(datetime_d, datetime_d); EXPECT_LE(datetime_d, datetime_e); EXPECT_LE(datetime_e, datetime_e); // Greater than EXPECT_GT(datetime_b, datetime_a); EXPECT_GT(datetime_c, datetime_a); EXPECT_GT(datetime_d, datetime_a); EXPECT_GT(datetime_e, datetime_a); EXPECT_GT(datetime_c, datetime_b); EXPECT_GT(datetime_d, datetime_b); EXPECT_GT(datetime_e, datetime_b); EXPECT_GT(datetime_d, datetime_c); EXPECT_GT(datetime_e, datetime_c); EXPECT_GT(datetime_e, datetime_d); // Greater than or equal. EXPECT_GE(datetime_a, datetime_a); EXPECT_GE(datetime_b, datetime_a); EXPECT_GE(datetime_c, datetime_a); EXPECT_GE(datetime_d, datetime_a); EXPECT_GE(datetime_e, datetime_a); EXPECT_GE(datetime_b, datetime_b); EXPECT_GE(datetime_c, datetime_b); EXPECT_GE(datetime_d, datetime_b); EXPECT_GE(datetime_e, datetime_b); EXPECT_GE(datetime_c, datetime_c); EXPECT_GE(datetime_d, datetime_c); EXPECT_GE(datetime_e, datetime_c); EXPECT_GE(datetime_d, datetime_d); EXPECT_GE(datetime_e, datetime_d); EXPECT_GE(datetime_e, datetime_e); } TEST(WvDateTimeUtilTest, Addition_WithDuration) { const DateTime start = DateTime::FromUnixMilliseconds(kTestUnixTimeMs); ASSERT_TRUE(start.IsSet()); const Duration one_day = Duration::FromMilliseconds(kOneDayMs); const DateTime tomorrow = start + one_day; ASSERT_TRUE(tomorrow.IsSet()); EXPECT_EQ(tomorrow.epoch_seconds(), kTestTimeS + Seconds(kOneDayS)); EXPECT_EQ(tomorrow.epoch_milliseconds(), kTestTimeMs + Milliseconds(kOneDayMs)); EXPECT_EQ(tomorrow.year(), kTestYear); EXPECT_EQ(tomorrow.month(), kTestMonth); EXPECT_EQ(tomorrow.day(), kTestDay + 1); EXPECT_EQ(tomorrow.day_of_year(), kTestDayOfYear + 1); EXPECT_EQ(tomorrow.day_of_week(), DayOfWeekAddition(kTestDayOfWeek, 1)); EXPECT_EQ(tomorrow.hour(), kTestHour); EXPECT_EQ(tomorrow.minute(), kTestMinute); EXPECT_EQ(tomorrow.second(), kTestSecond); EXPECT_EQ(tomorrow.millisecond(), kTestMs); // Note: This is 30 days for April to May. const Duration one_month = Duration::FromSeconds(kOneDayS * 30); const DateTime next_month = start + one_month; EXPECT_EQ(next_month.year(), kTestYear); EXPECT_EQ(next_month.month(), kTestMonth + 1); EXPECT_EQ(next_month.day(), kTestDay); EXPECT_EQ(next_month.day_of_year(), kTestDayOfYear + 30); EXPECT_EQ(next_month.day_of_week(), DayOfWeekAddition(kTestDayOfWeek, 30)); EXPECT_EQ(next_month.hour(), kTestHour); EXPECT_EQ(next_month.minute(), kTestMinute); EXPECT_EQ(next_month.second(), kTestSecond); EXPECT_EQ(next_month.millisecond(), kTestMs); // Note: This should roll over a day. const uint32_t day_inc_11h = (kTestHour + 11) / 24; const Duration eleven_hours = Duration::FromSeconds(kOneHourS * 11); const DateTime hours_later = start + eleven_hours; EXPECT_EQ(hours_later.year(), kTestYear); EXPECT_EQ(hours_later.month(), kTestMonth); EXPECT_EQ(hours_later.day(), kTestDay + day_inc_11h); EXPECT_EQ(hours_later.day_of_year(), kTestDayOfYear + day_inc_11h); EXPECT_EQ(hours_later.day_of_week(), DayOfWeekAddition(kTestDayOfWeek, day_inc_11h)); const uint32_t new_hour_11h = (kTestHour + 11) % 24; EXPECT_EQ(hours_later.hour(), new_hour_11h); EXPECT_EQ(hours_later.minute(), kTestMinute); EXPECT_EQ(hours_later.second(), kTestSecond); EXPECT_EQ(hours_later.millisecond(), kTestMs); const Duration five_years = Duration::FromSeconds(kOneCommonYearS * 4 + kOneLeapYearS); const DateTime fewer_years_later = start + five_years; EXPECT_EQ(fewer_years_later.year(), kTestYear + 5); EXPECT_EQ(fewer_years_later.month(), kTestMonth); EXPECT_EQ(fewer_years_later.day(), kTestDay); EXPECT_EQ(fewer_years_later.hour(), kTestHour); EXPECT_EQ(fewer_years_later.minute(), kTestMinute); EXPECT_EQ(fewer_years_later.second(), kTestSecond); EXPECT_EQ(fewer_years_later.millisecond(), kTestMs); const DateTime same_time = start + Duration::Zero(); EXPECT_EQ(start, same_time); } TEST(WvDateTimeUtilTest, Addition_WithDuration_Invalid) { // Addition with an unset date time is not allowed. const DateTime unset; ASSERT_FALSE(unset.IsSet()); const Duration one_month = Duration::FromSeconds(kOneDayS * 30); DateTime result = unset + one_month; EXPECT_FALSE(result.IsSet()); // Addition that causes overflow is not allowed. result = DateTime::Max() + Duration::FromMilliseconds(1); EXPECT_FALSE(result.IsSet()); result = DateTime::Min() + Duration::FromSeconds(kOneCommonYearS * 10000); EXPECT_FALSE(result.IsSet()); } TEST(WvDateTimeUtilTest, Subtraction_WithDuration) { const DateTime start = DateTime::FromUnixMilliseconds(kTestUnixTimeMs); ASSERT_TRUE(start.IsSet()); const Duration one_day = Duration::FromMilliseconds(kOneDayMs); const DateTime yesterday = start - one_day; ASSERT_TRUE(yesterday.IsSet()); EXPECT_EQ(yesterday.epoch_seconds(), Seconds(kTestUnixTimeS - kOneDayS)); EXPECT_EQ(yesterday.epoch_milliseconds(), Milliseconds(kTestUnixTimeMs - kOneDayMs)); EXPECT_EQ(yesterday.year(), kTestYear); EXPECT_EQ(yesterday.month(), kTestMonth); EXPECT_EQ(yesterday.day(), kTestDay - 1); EXPECT_EQ(yesterday.day_of_year(), kTestDayOfYear - 1); EXPECT_EQ(yesterday.day_of_week(), DayOfWeekSubtraction(kTestDayOfWeek, 1)); EXPECT_EQ(yesterday.hour(), kTestHour); EXPECT_EQ(yesterday.minute(), kTestMinute); EXPECT_EQ(yesterday.second(), kTestSecond); EXPECT_EQ(yesterday.millisecond(), kTestMs); // Note: This is a 31 days for March. const Duration one_month = Duration::FromSeconds(kOneDayS * 31); const DateTime previous_month = start - one_month; EXPECT_EQ(previous_month.year(), kTestYear); EXPECT_EQ(previous_month.month(), kTestMonth - 1); EXPECT_EQ(previous_month.day(), kTestDay); EXPECT_EQ(previous_month.day_of_year(), kTestDayOfYear - 31); EXPECT_EQ(previous_month.day_of_week(), DayOfWeekSubtraction(kTestDayOfWeek, 31)); EXPECT_EQ(previous_month.hour(), kTestHour); EXPECT_EQ(previous_month.minute(), kTestMinute); EXPECT_EQ(previous_month.second(), kTestSecond); EXPECT_EQ(previous_month.millisecond(), kTestMs); const uint32_t day_dec_14h = (kTestHour < 14) ? 1 : 0; const Duration fourteen_hours = Duration::FromSeconds(kOneHourS * 14); const DateTime hours_earlier = start - fourteen_hours; EXPECT_EQ(yesterday.year(), kTestYear); EXPECT_EQ(yesterday.month(), kTestMonth); EXPECT_EQ(yesterday.day(), kTestDay - day_dec_14h); EXPECT_EQ(yesterday.day_of_year(), kTestDayOfYear - day_dec_14h); EXPECT_EQ(yesterday.day_of_week(), DayOfWeekSubtraction(kTestDayOfWeek, day_dec_14h)); const uint32_t new_hour_14h = (kTestHour < 14) ? (kTestHour + 10) : kTestHour - 14; EXPECT_EQ(hours_earlier.hour(), new_hour_14h); EXPECT_EQ(hours_earlier.minute(), kTestMinute); EXPECT_EQ(hours_earlier.second(), kTestSecond); EXPECT_EQ(hours_earlier.millisecond(), kTestMs); const Duration five_years = Duration::FromSeconds(kOneCommonYearS * 3 + kOneLeapYearS * 2); const DateTime few_years_earlier = start - five_years; EXPECT_EQ(few_years_earlier.year(), kTestYear - 5); EXPECT_EQ(few_years_earlier.month(), kTestMonth); EXPECT_EQ(few_years_earlier.day(), kTestDay); EXPECT_EQ(few_years_earlier.hour(), kTestHour); EXPECT_EQ(few_years_earlier.minute(), kTestMinute); EXPECT_EQ(few_years_earlier.second(), kTestSecond); EXPECT_EQ(few_years_earlier.millisecond(), kTestMs); const DateTime same_time = start - Duration::Zero(); EXPECT_EQ(start, same_time); } TEST(WvDateTimeUtilTest, Subtraction_WithDuration_Invalid) { // Subtract with an unset date time is not allowed. const DateTime unset; ASSERT_FALSE(unset.IsSet()); const Duration one_month = Duration::FromSeconds(kOneDayS * 30); DateTime result = unset - one_month; EXPECT_FALSE(result.IsSet()); // Subtract that causes overflow is not allowed. result = DateTime::Min() - Duration::FromMilliseconds(1); EXPECT_FALSE(result.IsSet()); result = DateTime::Max() - Duration::FromSeconds(kOneCommonYearS * 10000); EXPECT_FALSE(result.IsSet()); } TEST(WvDateTimeUtilTest, Difference) { const Duration expected_diff = Duration(Hours(15) + Minutes(6) + Seconds(30) + Milliseconds(123)); const DateTime datetime_a = DateTime::FromUnixSeconds(kTestUnixTimeS, kTestMs); const DateTime datetime_b = DateTime::FromUnixMilliseconds( datetime_a.epoch_milliseconds() + expected_diff.total_milliseconds()); const Duration diff_ab = datetime_b - datetime_a; EXPECT_EQ(diff_ab, expected_diff); const Duration diff_ba = datetime_a - datetime_b; EXPECT_EQ(diff_ba, -expected_diff); // Very big diff const Duration diff_min_max = DateTime::Max() - DateTime::Min(); EXPECT_TRUE(diff_min_max.IsPositive()); const Duration diff_max_min = DateTime::Min() - DateTime::Max(); EXPECT_TRUE(diff_max_min.IsNegative()); EXPECT_EQ(diff_min_max, -diff_max_min); // No difference. const Duration diff_aa = datetime_a - datetime_a; EXPECT_TRUE(diff_aa.IsZero()); } TEST(WvDateTimeUtilTest, Difference_Invalid) { const DateTime datetime = DateTime::FromUnixSeconds(kTestUnixTimeS, kTestMs); const DateTime unset; Duration diff = datetime - unset; EXPECT_TRUE(diff.IsZero()); diff = unset - datetime; EXPECT_TRUE(diff.IsZero()); } TEST(WvDateTimeUtilTest, ToString) { // Default should ISO date times with short timezones, and milliseconds // only printed if non-zero. DateTime datetime = DateTime::FromUnixSeconds(kTestUnixTimeS, kTestMs); EXPECT_EQ(datetime.ToString(), "2024-04-19T13:36:18.507Z"); datetime = DateTime::FromUnixSeconds(kTestUnixTimeS); EXPECT_EQ(datetime.ToString(), "2024-04-19T13:36:18Z"); EXPECT_EQ(DateTime::Min().ToString(), "1970-01-01T00:00:00.001Z"); EXPECT_EQ(DateTime::Max().ToString(), "9999-12-31T23:59:59.999Z"); EXPECT_EQ(DateTime().ToString(), ""); } } // namespace test } // namespace wvutil