blob: ca4a6a0b11492bc8b5c21673e2c832ad1ab44006 [file] [edit]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Unit tests for the time_utils helpers get_weekday, get_time_of_day and
/// update_from_seconds.
///
//===----------------------------------------------------------------------===//
#include "hdr/limits_macros.h"
#include "hdr/stdint_proxy.h"
#include "hdr/types/struct_tm.h"
#include "hdr/types/time_t.h"
#include "src/time/time_constants.h"
#include "src/time/time_utils.h"
#include "test/UnitTest/Test.h"
namespace time_constants = LIBC_NAMESPACE::time_constants;
namespace time_utils = LIBC_NAMESPACE::time_utils;
// Straightforward reference: (unix_days + 4) mod 7 with a non-negative result.
static int reference_weekday(int64_t unix_days) {
int64_t wday = (unix_days % 7 + 7 + 4) % 7;
return static_cast<int>(wday);
}
// Smallest and largest day counts accepted by update_from_seconds.
constexpr int64_t MIN_SUPPORTED_DAYS =
INT_MIN *
static_cast<int64_t>(time_constants::NUMBER_OF_SECONDS_IN_LEAP_YEAR) /
time_constants::SECONDS_PER_DAY;
constexpr int64_t MAX_SUPPORTED_DAYS =
INT_MAX *
static_cast<int64_t>(time_constants::NUMBER_OF_SECONDS_IN_LEAP_YEAR) /
time_constants::SECONDS_PER_DAY;
// Documented valid input range of get_weekday.
constexpr int64_t MIN_WEEKDAY_DAYS = -230471710930685136LL;
constexpr int64_t MAX_WEEKDAY_DAYS = 230696890912053658LL;
// Documented valid input range of get_time_of_day.
constexpr uint32_t MAX_TIME_OF_DAY_SECONDS = 2255818;
TEST(LlvmLibcTimeUtilsGetWeekday, KnownDates) {
// 1970-01-01 Thursday
EXPECT_EQ(time_utils::get_weekday(0), 4);
// 1969-12-31 Wednesday
EXPECT_EQ(time_utils::get_weekday(-1), 3);
// 1970-01-02 Friday
EXPECT_EQ(time_utils::get_weekday(1), 5);
// 1970-01-04 Sunday
EXPECT_EQ(time_utils::get_weekday(3), 0);
// 1900-01-01 Monday
EXPECT_EQ(time_utils::get_weekday(-25567), 1);
// 2000-01-01 Saturday
EXPECT_EQ(time_utils::get_weekday(10957), 6);
// 2038-01-19 Tuesday
EXPECT_EQ(time_utils::get_weekday(24855), 2);
}
TEST(LlvmLibcTimeUtilsGetWeekday, IsConstexpr) {
static_assert(time_utils::get_weekday(0) == 4);
static_assert(time_utils::get_weekday(-1) == 3);
static_assert(time_utils::get_weekday(10957) == 6);
}
TEST(LlvmLibcTimeUtilsGetWeekday, MatchesReferenceAroundEpoch) {
// About +/-27,000 years around the Unix epoch.
for (int64_t days = -10'000'000; days <= 10'000'000; ++days)
ASSERT_EQ(time_utils::get_weekday(days), reference_weekday(days));
}
TEST(LlvmLibcTimeUtilsGetWeekday, MatchesReferenceAtSupportedLimits) {
// Every day near both ends of the range accepted by update_from_seconds.
for (int64_t i = 0; i < 1'000'000; ++i) {
ASSERT_EQ(time_utils::get_weekday(MIN_SUPPORTED_DAYS + i),
reference_weekday(MIN_SUPPORTED_DAYS + i));
ASSERT_EQ(time_utils::get_weekday(MAX_SUPPORTED_DAYS - i),
reference_weekday(MAX_SUPPORTED_DAYS - i));
}
}
TEST(LlvmLibcTimeUtilsGetWeekday, MatchesReferenceAtDocumentedLimits) {
for (int64_t i = 0; i < 1'000'000; ++i) {
ASSERT_EQ(time_utils::get_weekday(MIN_WEEKDAY_DAYS + i),
reference_weekday(MIN_WEEKDAY_DAYS + i));
ASSERT_EQ(time_utils::get_weekday(MAX_WEEKDAY_DAYS - i),
reference_weekday(MAX_WEEKDAY_DAYS - i));
}
}
TEST(LlvmLibcTimeUtilsGetWeekday, MatchesReferenceAcrossRange) {
// Sparse sweep across the whole documented range using a step that is
// coprime with 7 so all residues are exercised.
constexpr int64_t STEP = 1'000'000'007LL * 1009;
for (int64_t days = MIN_WEEKDAY_DAYS; days <= MAX_WEEKDAY_DAYS - STEP;
days += STEP)
ASSERT_EQ(time_utils::get_weekday(days), reference_weekday(days));
}
TEST(LlvmLibcTimeUtilsGetTimeOfDay, KnownTimes) {
time_utils::TimeOfDay t = time_utils::get_time_of_day(0);
EXPECT_EQ(t.hour, 0);
EXPECT_EQ(t.minute, 0);
EXPECT_EQ(t.second, 0);
t = time_utils::get_time_of_day(59);
EXPECT_EQ(t.hour, 0);
EXPECT_EQ(t.minute, 0);
EXPECT_EQ(t.second, 59);
t = time_utils::get_time_of_day(60);
EXPECT_EQ(t.hour, 0);
EXPECT_EQ(t.minute, 1);
EXPECT_EQ(t.second, 0);
t = time_utils::get_time_of_day(3599);
EXPECT_EQ(t.hour, 0);
EXPECT_EQ(t.minute, 59);
EXPECT_EQ(t.second, 59);
t = time_utils::get_time_of_day(3600);
EXPECT_EQ(t.hour, 1);
EXPECT_EQ(t.minute, 0);
EXPECT_EQ(t.second, 0);
// 12:34:56
t = time_utils::get_time_of_day(12 * 3600 + 34 * 60 + 56);
EXPECT_EQ(t.hour, 12);
EXPECT_EQ(t.minute, 34);
EXPECT_EQ(t.second, 56);
// Last second of the day: 23:59:59
t = time_utils::get_time_of_day(86399);
EXPECT_EQ(t.hour, 23);
EXPECT_EQ(t.minute, 59);
EXPECT_EQ(t.second, 59);
}
TEST(LlvmLibcTimeUtilsGetTimeOfDay, IsConstexpr) {
constexpr time_utils::TimeOfDay TIME_OF_DAY =
time_utils::get_time_of_day(86399);
static_assert(TIME_OF_DAY.hour == 23 && TIME_OF_DAY.minute == 59 &&
TIME_OF_DAY.second == 59);
}
TEST(LlvmLibcTimeUtilsGetTimeOfDay, ExhaustiveDocumentedRange) {
for (uint32_t s = 0; s <= MAX_TIME_OF_DAY_SECONDS; ++s) {
time_utils::TimeOfDay t = time_utils::get_time_of_day(s);
ASSERT_EQ(t.hour, static_cast<int>(s / 3600));
ASSERT_EQ(t.minute, static_cast<int>(s / 60 % 60));
ASSERT_EQ(t.second, static_cast<int>(s % 60));
}
}
// Checks the weekday and time of day that update_from_seconds produces for
// seconds against straightforward arithmetic.
static void expect_weekday_and_time_of_day(time_t seconds) {
tm result{};
ASSERT_TRUE(time_utils::update_from_seconds(seconds, &result).has_value());
time_t days = seconds / time_constants::SECONDS_PER_DAY;
time_t rem = seconds % time_constants::SECONDS_PER_DAY;
if (rem < 0) {
rem += time_constants::SECONDS_PER_DAY;
--days;
}
EXPECT_EQ(result.tm_wday, reference_weekday(days));
EXPECT_EQ(result.tm_hour, static_cast<int>(rem / 3600));
EXPECT_EQ(result.tm_min, static_cast<int>(rem / 60 % 60));
EXPECT_EQ(result.tm_sec, static_cast<int>(rem % 60));
}
TEST(LlvmLibcTimeUtilsUpdateFromSeconds, WeekdayAndTimeOfDay) {
// Step by 3599 seconds (one second short of an hour) over several years on
// both sides of the epoch, so each successive hour is sampled at a different
// second offset, comparing with straightforward arithmetic.
constexpr time_t SPAN = 10LL * 366 * time_constants::SECONDS_PER_DAY;
for (time_t seconds = -SPAN; seconds <= SPAN; seconds += 3599)
expect_weekday_and_time_of_day(seconds);
}
TEST(LlvmLibcTimeUtilsUpdateFromSeconds, FarFutureAndPast) {
// About +/-2.1 billion years, close to the largest years that fit in an int.
constexpr time_t FAR_SECONDS =
INT_MAX * static_cast<int64_t>(time_constants::DAYS_PER_NON_LEAP_YEAR) *
time_constants::SECONDS_PER_DAY;
for (time_t offset = 0; offset < 2 * time_constants::SECONDS_PER_DAY;
offset += 1234) {
expect_weekday_and_time_of_day(FAR_SECONDS - offset);
expect_weekday_and_time_of_day(-FAR_SECONDS + offset);
}
}