From c85e679e2d802159f7eb1a70a1667dd20dea0379 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Tue, 25 Aug 2020 19:19:16 -0400 Subject: AK+LibCore+Kernel: Have fewer implementations of day_of_year The JS tests pointed out that the implementation in DateTime had an off-by-one in the month when doing the leap year check, so this change fixes that bug. --- AK/Time.cpp | 45 ++++++++++++++++++++++++++++++++++++++++ AK/Time.h | 8 +++++++ Kernel/CMakeLists.txt | 1 + Kernel/RTC.cpp | 47 +----------------------------------------- Libraries/LibC/time.cpp | 8 +------ Libraries/LibCore/DateTime.cpp | 8 +------ 6 files changed, 57 insertions(+), 60 deletions(-) create mode 100644 AK/Time.cpp diff --git a/AK/Time.cpp b/AK/Time.cpp new file mode 100644 index 0000000000..63253f90b7 --- /dev/null +++ b/AK/Time.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2020, The SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace AK { + +int day_of_year(int year, unsigned month, int day) +{ + ASSERT(month >= 1 && month <= 12); + + static const int seek_table[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int day_of_year = seek_table[month - 1] + day - 1; + + if (is_leap_year(year) && month >= 3) + day_of_year++; + + return day_of_year; +} + +} diff --git a/AK/Time.h b/AK/Time.h index 309218747b..612b92f691 100644 --- a/AK/Time.h +++ b/AK/Time.h @@ -28,6 +28,13 @@ namespace AK { +// Month and day start at 1. Month must be >= 1 and <= 12. +// The return value is 0-indexed, that is Jan 1 is day 0. +// Day may be negative or larger than the number of days +// in the given month. If day is negative enough, the result +// can be negative. +int day_of_year(int year, unsigned month, int day); + inline bool is_leap_year(int year) { return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); @@ -161,6 +168,7 @@ inline bool operator!=(const TimespecType& a, const TimespecType& b) } +using AK::day_of_year; using AK::is_leap_year; using AK::timespec_add; using AK::timespec_add_timeval; diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index ff9379d823..87befd7fc2 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -207,6 +207,7 @@ set(AK_SOURCES ../AK/StringImpl.cpp ../AK/StringUtils.cpp ../AK/StringView.cpp + ../AK/Time.cpp ) set(ELF_SOURCES diff --git a/Kernel/RTC.cpp b/Kernel/RTC.cpp index 25f494bd98..bced1bf661 100644 --- a/Kernel/RTC.cpp +++ b/Kernel/RTC.cpp @@ -49,50 +49,6 @@ static bool update_in_progress() return CMOS::read(0x0a) & 0x80; } -static unsigned days_in_months_since_start_of_year(unsigned month, unsigned year) -{ - ASSERT(month <= 11); - unsigned days = 0; - switch (month) { - case 11: - days += 30; - [[fallthrough]]; - case 10: - days += 31; - [[fallthrough]]; - case 9: - days += 30; - [[fallthrough]]; - case 8: - days += 31; - [[fallthrough]]; - case 7: - days += 31; - [[fallthrough]]; - case 6: - days += 30; - [[fallthrough]]; - case 5: - days += 31; - [[fallthrough]]; - case 4: - days += 30; - [[fallthrough]]; - case 3: - days += 31; - [[fallthrough]]; - case 2: - if (is_leap_year(year)) - days += 29; - else - days += 28; - [[fallthrough]]; - case 1: - days += 31; - } - return days; -} - static u8 bcd_to_binary(u8 bcd) { return (bcd & 0x0F) + ((bcd >> 4) * 10); @@ -149,8 +105,7 @@ time_t now() ASSERT(year >= 2018); return years_to_days_since_epoch(year) * 86400 - + days_in_months_since_start_of_year(month - 1, year) * 86400 - + (day - 1) * 86400 + + day_of_year(year, month, day) * 86400 + hour * 3600 + minute * 60 + second; diff --git a/Libraries/LibC/time.cpp b/Libraries/LibC/time.cpp index 7a49ec8a95..032da5b6f0 100644 --- a/Libraries/LibC/time.cpp +++ b/Libraries/LibC/time.cpp @@ -117,13 +117,7 @@ static time_t tm_to_time(struct tm* tm, long timezone_adjust_seconds) } int days = years_to_days_since_epoch(1900 + tm->tm_year); - - tm->tm_yday = tm->tm_mday - 1; - for (int month = 0; month < tm->tm_mon; ++month) - tm->tm_yday += __days_per_month[month]; - if (tm->tm_mon > 1 && is_leap_year(1900 + tm->tm_year)) - ++tm->tm_yday; - + tm->tm_yday = day_of_year(1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday); days += tm->tm_yday; int seconds = tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec; diff --git a/Libraries/LibCore/DateTime.cpp b/Libraries/LibCore/DateTime.cpp index a1cd35cc7b..c4062699cc 100644 --- a/Libraries/LibCore/DateTime.cpp +++ b/Libraries/LibCore/DateTime.cpp @@ -81,13 +81,7 @@ unsigned DateTime::days_in_month() const unsigned DateTime::day_of_year() const { - static const int seek_table[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - int day_of_year = seek_table[m_month - 1] + m_day; - - if (is_leap_year() && m_month > 3) - day_of_year++; - - return day_of_year - 1; + return ::day_of_year(m_year, m_month, m_day); } bool DateTime::is_leap_year() const -- cgit v1.2.3