lib/datetime: Support dates outside of 1901..2038 range
Fixes: https://savannah.gnu.org/bugs/?63894 Fixes: https://savannah.gnu.org/bugs/?66301 Signed-off-by: Vladimir Serbinenko <phcoder@gmail.com> Signed-off-by: Andrew Hamilton <adhamilt@gmail.com> Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
parent
02788bfdf9
commit
6837293b87
@ -64,7 +64,10 @@ grub_get_weekday_name (struct grub_datetime *datetime)
|
||||
#define SECPERDAY (24*SECPERHOUR)
|
||||
#define DAYSPERYEAR 365
|
||||
#define DAYSPER4YEARS (4*DAYSPERYEAR+1)
|
||||
|
||||
/* 24 leap years in 100 years */
|
||||
#define DAYSPER100YEARS (100 * DAYSPERYEAR + 24)
|
||||
/* 97 leap years in 400 years */
|
||||
#define DAYSPER400YEARS (400 * DAYSPERYEAR + 97)
|
||||
|
||||
void
|
||||
grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime)
|
||||
@ -76,10 +79,12 @@ 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, 1 (proleptic). */
|
||||
unsigned days;
|
||||
/* Seconds into current day. */
|
||||
unsigned secs_in_day;
|
||||
/* Tracks whether this is a leap year. */
|
||||
bool bisextile;
|
||||
|
||||
/* Transform C divisions and modulos to mathematical ones */
|
||||
if (nix < 0)
|
||||
@ -92,27 +97,63 @@ 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;
|
||||
/*
|
||||
* 1970 is Unix Epoch. Adjust to a year 1 epoch:
|
||||
* Leap year logic:
|
||||
* - Years evenly divisible by 400 are leap years
|
||||
* - Otherwise, if divisible by 100 are not leap years
|
||||
* - Otherwise, if divisible by 4 are leap years
|
||||
* There are four 400-year periods (1600 years worth of days with leap days)
|
||||
* There are 369 years in addition to the four 400 year periods
|
||||
* There are three 100-year periods worth of leap days (3*24)
|
||||
* There are 17 leap days in 69 years (beyond the three 100 year periods)
|
||||
*/
|
||||
days = 4 * DAYSPER400YEARS + 369 * DAYSPERYEAR + 3 * 24 + 17 + days_epoch;
|
||||
|
||||
datetime->year = 1901 + 4 * (days / DAYSPER4YEARS);
|
||||
datetime->year = 1 + 400 * (days / DAYSPER400YEARS);
|
||||
days %= DAYSPER400YEARS;
|
||||
|
||||
/*
|
||||
* On 31st December of bissextile (leap) years 365 days from the beginning
|
||||
* of the year elapsed but year isn't finished yet - every 400 years
|
||||
* 396 is 4 years less than 400 year leap cycle
|
||||
* 96 is 1 day less than number of leap days in 400 years
|
||||
*/
|
||||
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 */
|
||||
/*
|
||||
* On 31st December of bissextile (leap) years 365 days from the beginning
|
||||
* of the year elapsed but year isn't finished yet - every 4 years
|
||||
*/
|
||||
if (days / DAYSPERYEAR == 4)
|
||||
{
|
||||
datetime->year += 3;
|
||||
days -= 3*DAYSPERYEAR;
|
||||
days -= 3 * DAYSPERYEAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
datetime->year += days / DAYSPERYEAR;
|
||||
days %= DAYSPERYEAR;
|
||||
}
|
||||
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]);
|
||||
|
||||
bisextile = (datetime->year % 4 == 0
|
||||
&& (datetime->year % 100 != 0
|
||||
|| datetime->year % 400 == 0)) ? true : false;
|
||||
for (i = 0;
|
||||
i < 12 && days >= ((i == 1 && bisextile == true) ? 29 : months[i]);
|
||||
i++)
|
||||
days -= ((i == 1 && bisextile == true) ? 29 : months[i]);
|
||||
datetime->month = i + 1;
|
||||
datetime->day = 1 + days;
|
||||
datetime->hour = (secs_in_day / SECPERHOUR);
|
||||
|
||||
@ -54,8 +54,9 @@ 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;
|
||||
bool bisextile;
|
||||
const grub_uint16_t monthssum[12]
|
||||
= { 0,
|
||||
31,
|
||||
@ -75,15 +76,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;
|
||||
@ -91,16 +88,31 @@ grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix)
|
||||
/* Transform C divisions and modulos to mathematical ones */
|
||||
y4 = ((datetime->year - 1) >> 2) - (1973 / 4);
|
||||
ay = datetime->year - 1973 - 4 * y4;
|
||||
ret += y4 * SECPER4YEARS;
|
||||
ret += ay * SECPERYEAR;
|
||||
ret += (grub_int64_t) y4 * SECPER4YEARS;
|
||||
ret += (grub_int64_t) ay * SECPERYEAR;
|
||||
|
||||
ret += monthssum[datetime->month - 1] * SECPERDAY;
|
||||
if (ay == 3 && datetime->month >= 3)
|
||||
/*
|
||||
* Correct above calculation (which assumes every 4 years is a leap year)
|
||||
* to remove those "false leap years" that are divisible by 100 but not 400.
|
||||
* Since this logic starts with seconds since 1973, 15 is used because:
|
||||
* - (1973 - 1) / 100 = 19 (floor due to integer math)
|
||||
* - (1973 - 1) / 400 = 4 (floor due to integer math)
|
||||
* - 19 - 4 - 15 = 0 (we want to start with no "false leap years" at time
|
||||
* zero of 1973)
|
||||
*/
|
||||
ret -= ((datetime->year - 1) / 100 - (datetime->year - 1) / 400 - 15)
|
||||
* SECPERDAY;
|
||||
|
||||
ret += (grub_int64_t) monthssum[datetime->month - 1] * SECPERDAY;
|
||||
bisextile = (ay == 3
|
||||
&& (datetime->year % 100 != 0
|
||||
|| datetime->year % 400 == 0)) ? true : false;
|
||||
if (bisextile == true && datetime->month >= 3)
|
||||
ret += SECPERDAY;
|
||||
|
||||
ret += (datetime->day - 1) * SECPERDAY;
|
||||
if ((datetime->day > months[datetime->month - 1]
|
||||
&& (!ay || datetime->month != 2 || datetime->day != 29))
|
||||
&& !(bisextile == true && datetime->month == 2 && datetime->day == 29))
|
||||
|| datetime->day < 1)
|
||||
return 0;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user