* [PATCH 1/2] datetime: Support dates outside of 1901..2038 range
@ 2024-08-17 16:55 Vladimir Serbinenko
2024-08-17 16:55 ` [PATCH 2/2] date_unit_test: test dates outside of 32-bit unix range Vladimir Serbinenko
0 siblings, 1 reply; 2+ messages in thread
From: Vladimir Serbinenko @ 2024-08-17 16:55 UTC (permalink / raw)
To: grub-devel; +Cc: Vladimir Serbinenko
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
---
grub-core/lib/datetime.c | 31 ++++++++++++++++++++++++-------
include/grub/datetime.h | 15 +++++++--------
2 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/grub-core/lib/datetime.c b/grub-core/lib/datetime.c
index 9120128ca..9badd53dd 100644
--- a/grub-core/lib/datetime.c
+++ b/grub-core/lib/datetime.c
@@ -59,6 +59,8 @@ grub_get_weekday_name (struct grub_datetime *datetime)
#define SECPERDAY (24*SECPERHOUR)
#define DAYSPERYEAR 365
#define DAYSPER4YEARS (4*DAYSPERYEAR+1)
+#define DAYSPER100YEARS (100*DAYSPERYEAR+24)
+#define DAYSPER400YEARS (400*DAYSPERYEAR+97)
void
@@ -71,7 +73,7 @@ grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime)
/* Convenience: let's have 3 consecutive non-bissextile years
at the beginning of the counting date. So count from 1901. */
int days_epoch;
- /* Number of days since 1st Januar, 1901. */
+ /* Number of days since 1st January, 1601 (proleptic). */
unsigned days;
/* Seconds into current day. */
unsigned secs_in_day;
@@ -87,9 +89,25 @@ grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime)
days_epoch = grub_divmod64 (nix, SECPERDAY, NULL);
secs_in_day = nix - days_epoch * SECPERDAY;
- days = days_epoch + 69 * DAYSPERYEAR + 17;
+ days = days_epoch + 369 * DAYSPERYEAR + 17 + 24 * 3;
- datetime->year = 1901 + 4 * (days / DAYSPER4YEARS);
+ datetime->year = 1601 + 400 * (days / DAYSPER400YEARS);
+ days %= DAYSPER400YEARS;
+
+ /* On 31st December of bissextile years 365 days from the beginning
+ of the year elapsed but year isn't finished yet */
+ if (days / DAYSPER100YEARS == 4)
+ {
+ datetime->year += 396;
+ days -= 396*DAYSPERYEAR + 96;
+ }
+ else
+ {
+ datetime->year += 100 * (days / DAYSPER100YEARS);
+ days %= DAYSPER100YEARS;
+ }
+
+ datetime->year += 4 * (days / DAYSPER4YEARS);
days %= DAYSPER4YEARS;
/* On 31st December of bissextile years 365 days from the beginning
of the year elapsed but year isn't finished yet */
@@ -103,11 +121,10 @@ grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime)
datetime->year += days / DAYSPERYEAR;
days %= DAYSPERYEAR;
}
+ int isbisextile = datetime->year % 4 == 0 && (datetime->year % 100 != 0 || datetime->year % 400 == 0);
for (i = 0; i < 12
- && days >= (i==1 && datetime->year % 4 == 0
- ? 29 : months[i]); i++)
- days -= (i==1 && datetime->year % 4 == 0
- ? 29 : months[i]);
+ && days >= (i==1 && isbisextile ? 29 : months[i]); i++)
+ days -= (i==1 && isbisextile ? 29 : months[i]);
datetime->month = i + 1;
datetime->day = 1 + days;
datetime->hour = (secs_in_day / SECPERHOUR);
diff --git a/include/grub/datetime.h b/include/grub/datetime.h
index bcec636f0..9289b0d00 100644
--- a/include/grub/datetime.h
+++ b/include/grub/datetime.h
@@ -54,7 +54,7 @@ void grub_unixtime2datetime (grub_int64_t nix,
static inline int
grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix)
{
- grub_int32_t ret;
+ grub_int64_t ret;
int y4, ay;
const grub_uint16_t monthssum[12]
= { 0,
@@ -75,15 +75,11 @@ grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix)
const int SECPERHOUR = 60 * SECPERMIN;
const int SECPERDAY = 24 * SECPERHOUR;
const int SECPERYEAR = 365 * SECPERDAY;
- const int SECPER4YEARS = 4 * SECPERYEAR + SECPERDAY;
+ const grub_int64_t SECPER4YEARS = 4 * SECPERYEAR + SECPERDAY;
- if (datetime->year > 2038 || datetime->year < 1901)
- return 0;
if (datetime->month > 12 || datetime->month < 1)
return 0;
- /* In the period of validity of unixtime all years divisible by 4
- are bissextile*/
/* Convenience: let's have 3 consecutive non-bissextile years
at the beginning of the epoch. So count from 1973 instead of 1970 */
ret = 3 * SECPERYEAR + SECPERDAY;
@@ -94,13 +90,16 @@ grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix)
ret += y4 * SECPER4YEARS;
ret += ay * SECPERYEAR;
+ ret -= ((datetime->year - 1) / 100 - (datetime->year - 1) / 400 - 15) * SECPERDAY;
+
ret += monthssum[datetime->month - 1] * SECPERDAY;
- if (ay == 3 && datetime->month >= 3)
+ int isbisextile = ay == 3 && (datetime->year % 100 != 0 || datetime->year % 400 == 0);
+ if (isbisextile && datetime->month >= 3)
ret += SECPERDAY;
ret += (datetime->day - 1) * SECPERDAY;
if ((datetime->day > months[datetime->month - 1]
- && (!ay || datetime->month != 2 || datetime->day != 29))
+ && !(isbisextile && datetime->month == 2 && datetime->day == 29))
|| datetime->day < 1)
return 0;
--
2.39.2
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH 2/2] date_unit_test: test dates outside of 32-bit unix range
2024-08-17 16:55 [PATCH 1/2] datetime: Support dates outside of 1901..2038 range Vladimir Serbinenko
@ 2024-08-17 16:55 ` Vladimir Serbinenko
0 siblings, 0 replies; 2+ messages in thread
From: Vladimir Serbinenko @ 2024-08-17 16:55 UTC (permalink / raw)
To: grub-devel; +Cc: Vladimir Serbinenko
Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com>
---
tests/date_unit_test.c | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/tests/date_unit_test.c b/tests/date_unit_test.c
index 99774f199..a42801936 100644
--- a/tests/date_unit_test.c
+++ b/tests/date_unit_test.c
@@ -25,12 +25,13 @@
#include <grub/test.h>
static void
-date_test (grub_int32_t v)
+date_test (grub_int64_t v)
{
struct grub_datetime dt;
time_t t = v;
struct tm *g;
int w;
+ grub_int64_t back = 0;
g = gmtime (&t);
@@ -38,28 +39,33 @@ date_test (grub_int32_t v)
w = grub_get_weekday (&dt);
- grub_test_assert (g->tm_sec == dt.second, "time %d bad second: %d vs %d", v,
+ grub_datetime2unixtime (&dt, &back);
+
+ grub_test_assert (g->tm_sec == dt.second, "time %lld bad second: %d vs %d", (long long)v,
g->tm_sec, dt.second);
- grub_test_assert (g->tm_min == dt.minute, "time %d bad minute: %d vs %d", v,
+ grub_test_assert (g->tm_min == dt.minute, "time %lld bad minute: %d vs %d", (long long)v,
g->tm_min, dt.minute);
- grub_test_assert (g->tm_hour == dt.hour, "time %d bad hour: %d vs %d", v,
+ grub_test_assert (g->tm_hour == dt.hour, "time %lld bad hour: %d vs %d", (long long)v,
g->tm_hour, dt.hour);
- grub_test_assert (g->tm_mday == dt.day, "time %d bad day: %d vs %d", v,
+ grub_test_assert (g->tm_mday == dt.day, "time %lld bad day: %d vs %d", (long long)v,
g->tm_mday, dt.day);
- grub_test_assert (g->tm_mon + 1 == dt.month, "time %d bad month: %d vs %d", v,
+ grub_test_assert (g->tm_mon + 1 == dt.month, "time %lld bad month: %d vs %d",(long long)v,
g->tm_mon + 1, dt.month);
grub_test_assert (g->tm_year + 1900 == dt.year,
- "time %d bad year: %d vs %d", v,
+ "time %lld bad year: %d vs %d", (long long)v,
g->tm_year + 1900, dt.year);
- grub_test_assert (g->tm_wday == w, "time %d bad week day: %d vs %d", v,
+ grub_test_assert (g->tm_wday == w, "time %lld bad week day: %d vs %d", (long long)v,
g->tm_wday, w);
+ grub_test_assert (back == v, "time %lld bad back transform: %lld", (long long)v,
+ (long long)back);
}
static void
date_test_iter (void)
{
- grub_int32_t tests[] = { -1, 0, +1, -2133156255, GRUB_INT32_MIN,
+ grub_int32_t tests[] = { -1, 0, +1, 978224552, 2133156255, -2110094321, GRUB_INT32_MIN,
GRUB_INT32_MAX };
+ grub_int64_t tests64[] = { 5774965200LL, 4108700725LL };
unsigned i;
for (i = 0; i < ARRAY_SIZE (tests); i++)
@@ -71,6 +77,17 @@ date_test_iter (void)
date_test (x);
date_test (-x);
}
+ if (sizeof (time_t) > 4)
+ {
+ for (i = 0; i < ARRAY_SIZE (tests64); i++)
+ date_test (tests64[i]);
+ for (i = 0; i < 1000000; i++)
+ {
+ grub_int64_t x = rand () + (grub_uint64_t)GRUB_INT32_MAX;
+ date_test (x);
+ date_test (-x);
+ }
+ }
}
GRUB_UNIT_TEST ("date_unit_test", date_test_iter);
--
2.39.2
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2024-08-17 16:56 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-17 16:55 [PATCH 1/2] datetime: Support dates outside of 1901..2038 range Vladimir Serbinenko
2024-08-17 16:55 ` [PATCH 2/2] date_unit_test: test dates outside of 32-bit unix range Vladimir Serbinenko
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.