mirror of
https://github.com/SerenityOS/serenity
synced 2026-04-25 17:15:42 +02:00
AK/DOSPackedTime+Tests: Add helpers for parsing Unix timestamps
This commit is contained in:
committed by
Sönke Holz
parent
32edf98bc4
commit
97e18c96e5
@@ -5,9 +5,12 @@
|
||||
*/
|
||||
|
||||
#include <AK/DOSPackedTime.h>
|
||||
#include <AK/Error.h>
|
||||
|
||||
namespace AK {
|
||||
|
||||
constexpr auto seconds_per_day = 86'400;
|
||||
|
||||
UnixDateTime time_from_packed_dos(DOSPackedDate date, DOSPackedTime time)
|
||||
{
|
||||
if (date.value == 0)
|
||||
@@ -36,4 +39,52 @@ DOSPackedTime to_packed_dos_time(unsigned hour, unsigned minute, unsigned second
|
||||
return time;
|
||||
}
|
||||
|
||||
// FIXME: Improve these naive algorithms.
|
||||
ErrorOr<DOSPackedDate> to_packed_dos_date(UnixDateTime const& unix_date_time)
|
||||
{
|
||||
auto truncated_seconds_since_epoch = unix_date_time.truncated_seconds_since_epoch();
|
||||
if (truncated_seconds_since_epoch < first_dos_representable_unix_timestamp || truncated_seconds_since_epoch > static_cast<i64>(last_dos_representable_unix_timestamp))
|
||||
return EINVAL;
|
||||
|
||||
auto years_since_epoch = 0;
|
||||
auto leftover_seconds = truncated_seconds_since_epoch;
|
||||
while (leftover_seconds >= days_in_year(years_since_epoch + 1970) * seconds_per_day) {
|
||||
leftover_seconds -= days_in_year(years_since_epoch + 1970) * seconds_per_day;
|
||||
++years_since_epoch;
|
||||
}
|
||||
|
||||
auto month_of_year = 1;
|
||||
for (; month_of_year <= 12; ++month_of_year) {
|
||||
auto seconds_in_current_month = days_in_month(years_since_epoch + 1970, month_of_year) * seconds_per_day;
|
||||
if (leftover_seconds < seconds_in_current_month)
|
||||
break;
|
||||
leftover_seconds -= seconds_in_current_month;
|
||||
}
|
||||
|
||||
VERIFY(month_of_year <= 12);
|
||||
|
||||
auto day = leftover_seconds / seconds_per_day + 1;
|
||||
|
||||
return to_packed_dos_date(years_since_epoch + 1970, month_of_year, day);
|
||||
}
|
||||
|
||||
ErrorOr<DOSPackedTime> to_packed_dos_time(UnixDateTime const& unix_date_time)
|
||||
{
|
||||
constexpr auto seconds_per_hour = 3'600;
|
||||
constexpr auto seconds_per_minute = 60;
|
||||
|
||||
auto date = TRY(to_packed_dos_date(unix_date_time));
|
||||
|
||||
auto truncated_seconds_since_epoch = unix_date_time.truncated_seconds_since_epoch();
|
||||
auto leftover_seconds = truncated_seconds_since_epoch - days_since_epoch(date.year + first_dos_year, date.month, date.day) * seconds_per_day;
|
||||
|
||||
auto hours = leftover_seconds / seconds_per_hour;
|
||||
leftover_seconds -= hours * seconds_per_hour;
|
||||
|
||||
auto minutes = leftover_seconds / seconds_per_minute;
|
||||
leftover_seconds -= minutes * seconds_per_minute;
|
||||
|
||||
return to_packed_dos_time(hours, minutes, leftover_seconds);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,10 +32,14 @@ union DOSPackedDate {
|
||||
static_assert(sizeof(DOSPackedDate) == 2);
|
||||
|
||||
inline constexpr u16 first_dos_year = 1980;
|
||||
inline constexpr u32 first_dos_representable_unix_timestamp = UnixDateTime::from_unix_time_parts(1980, 1, 1, 0, 0, 0, 0).seconds_since_epoch();
|
||||
inline constexpr u64 last_dos_representable_unix_timestamp = UnixDateTime::from_unix_time_parts(2107, 12, 31, 23, 59, 59, 0).seconds_since_epoch();
|
||||
|
||||
UnixDateTime time_from_packed_dos(DOSPackedDate, DOSPackedTime);
|
||||
DOSPackedDate to_packed_dos_date(unsigned year, unsigned month, unsigned day);
|
||||
DOSPackedTime to_packed_dos_time(unsigned hour, unsigned minute, unsigned second);
|
||||
ErrorOr<DOSPackedDate> to_packed_dos_date(UnixDateTime const&);
|
||||
ErrorOr<DOSPackedTime> to_packed_dos_time(UnixDateTime const&);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ set(AK_TEST_SOURCES
|
||||
TestComplex.cpp
|
||||
TestConstrainedStream.cpp
|
||||
TestCoroutine.cpp
|
||||
TestDOSPackedTime.cpp
|
||||
TestDisjointChunks.cpp
|
||||
TestDistinctNumeric.cpp
|
||||
TestDoublyLinkedList.cpp
|
||||
|
||||
57
Tests/AK/TestDOSPackedTime.cpp
Normal file
57
Tests/AK/TestDOSPackedTime.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2025, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/DOSPackedTime.h>
|
||||
#include <AK/Time.h>
|
||||
|
||||
TEST_CASE(test_date_serialization)
|
||||
{
|
||||
auto matches = [](i32 year, u8 month, u8 day) -> bool {
|
||||
DOSPackedDate date = {};
|
||||
date.year = year - 1980;
|
||||
date.month = month;
|
||||
date.day = day;
|
||||
|
||||
return MUST(to_packed_dos_date(UnixDateTime::from_unix_time_parts(year, month, day, 0, 0, 0, 0))).value == date.value;
|
||||
};
|
||||
|
||||
EXPECT(matches(1980, 1, 1));
|
||||
EXPECT(matches(2000, 1, 1));
|
||||
EXPECT(matches(2016, 2, 29));
|
||||
EXPECT(matches(2016, 3, 1));
|
||||
EXPECT(matches(2017, 2, 28));
|
||||
EXPECT(matches(2017, 3, 1));
|
||||
EXPECT(matches(2018, 10, 10));
|
||||
EXPECT(matches(2025, 4, 26));
|
||||
}
|
||||
|
||||
TEST_CASE(test_time_serialization)
|
||||
{
|
||||
auto matches = [](u8 hour, u8 minute, u8 second) -> bool {
|
||||
DOSPackedTime time = {};
|
||||
time.hour = hour;
|
||||
time.minute = minute;
|
||||
// This can only handle 2-second intervals, since it's stored in only 5 bits.
|
||||
time.second = second / 2;
|
||||
|
||||
return MUST(to_packed_dos_time(UnixDateTime::from_unix_time_parts(2025, 1, 1, hour, minute, second, 0))).value == time.value;
|
||||
};
|
||||
|
||||
auto test_hour = [&matches](u8 hour) -> bool {
|
||||
for (size_t minute = 0; minute < 60; ++minute) {
|
||||
for (size_t second = 0; second < 60; ++second) {
|
||||
if (!matches(hour, minute, second))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
EXPECT(test_hour(0));
|
||||
EXPECT(test_hour(23));
|
||||
}
|
||||
Reference in New Issue
Block a user