mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-30 19:10:36 +08:00
* libc/time/gmtime_r.c (gmtime_r): Fixed bug in calculations for dates
after year 2069 or before year 1901. Ideas for solution taken from musl's __secs_to_tm()
This commit is contained in:
parent
e20a68a56f
commit
37152e9703
@ -1,3 +1,9 @@
|
|||||||
|
2014-12-07 Freddie Chopin <freddie_chopin@op.pl>
|
||||||
|
|
||||||
|
* libc/time/gmtime_r.c (gmtime_r): Fixed bug in calculations for dates
|
||||||
|
after year 2069 or before year 1901. Ideas for solution taken from
|
||||||
|
musl's __secs_to_tm()
|
||||||
|
|
||||||
2014-12-05 Yaakov Selkowitz <yselkowi@redhat.com>
|
2014-12-05 Yaakov Selkowitz <yselkowi@redhat.com>
|
||||||
|
|
||||||
* libc/include/stdlib.h (__bsd_qsort_r): Declare.
|
* libc/include/stdlib.h (__bsd_qsort_r): Declare.
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
* - Fixed bug in mday computations - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
|
* - Fixed bug in mday computations - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
|
||||||
* - Fixed bug in __tzcalc_limits - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
|
* - Fixed bug in __tzcalc_limits - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
|
||||||
* - Move code from _mktm_r() to gmtime_r() - 05/09/14, Freddie Chopin <freddie_chopin@op.pl>
|
* - Move code from _mktm_r() to gmtime_r() - 05/09/14, Freddie Chopin <freddie_chopin@op.pl>
|
||||||
|
* - Fixed bug in calculations for dates after year 2069 or before year 1901. Ideas for
|
||||||
|
* solution taken from musl's __secs_to_tm() - 07/12/2014, Freddie Chopin
|
||||||
|
* <freddie_chopin@op.pl>
|
||||||
*
|
*
|
||||||
* Converts the calendar time pointed to by tim_p into a broken-down time
|
* Converts the calendar time pointed to by tim_p into a broken-down time
|
||||||
* expressed as local time. Returns a pointer to a structure containing the
|
* expressed as local time. Returns a pointer to a structure containing the
|
||||||
@ -14,19 +17,28 @@
|
|||||||
|
|
||||||
#include "local.h"
|
#include "local.h"
|
||||||
|
|
||||||
|
/* move epoch from 01.01.1970 to 01.03.2000 - this is the first day of new
|
||||||
|
* 400-year long cycle, right after additional day of leap year. This adjustment
|
||||||
|
* is required only for date calculation, so instead of modifying time_t value
|
||||||
|
* (which would require 64-bit operations to work correctly) it's enough to
|
||||||
|
* adjust the calculated number of days since epoch. */
|
||||||
|
#define EPOCH_ADJUSTMENT_DAYS 11017
|
||||||
|
/* year to which the adjustment was made */
|
||||||
|
#define ADJUSTED_EPOCH_YEAR 2000
|
||||||
|
/* 1st March of 2000 is Wednesday */
|
||||||
|
#define ADJUSTED_EPOCH_WDAY 3
|
||||||
/* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
|
/* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
|
||||||
#define DAYS_PER_400_YEARS 146097L
|
#define DAYS_PER_400_YEARS 146097L
|
||||||
/* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
|
/* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
|
||||||
#define DAYS_PER_100_YEARS 36524L
|
#define DAYS_PER_100_YEARS 36524L
|
||||||
/* there is one leap year every 4 years */
|
/* there is one leap year every 4 years */
|
||||||
#define DAYS_PER_4_YEARS (3 * 365 + 366)
|
#define DAYS_PER_4_YEARS (3 * 365 + 366)
|
||||||
|
/* number of days in a non-leap year */
|
||||||
static _CONST int days_per_year[4] = {
|
#define DAYS_PER_YEAR 365
|
||||||
365, /* 1970 or 1966 */
|
/* number of days in January */
|
||||||
365, /* 1971 or 1967 */
|
#define DAYS_IN_JANUARY 31
|
||||||
366, /* 1972 or 1968 */
|
/* number of days in non-leap February */
|
||||||
365 /* 1973 or 1969 */
|
#define DAYS_IN_FEBRUARY 28
|
||||||
} ;
|
|
||||||
|
|
||||||
struct tm *
|
struct tm *
|
||||||
_DEFUN (gmtime_r, (tim_p, res),
|
_DEFUN (gmtime_r, (tim_p, res),
|
||||||
@ -35,14 +47,14 @@ _DEFUN (gmtime_r, (tim_p, res),
|
|||||||
{
|
{
|
||||||
long days, rem;
|
long days, rem;
|
||||||
_CONST time_t lcltime = *tim_p;
|
_CONST time_t lcltime = *tim_p;
|
||||||
int year;
|
int year, month, yearday, weekday;
|
||||||
int years400, years100, years4;
|
int years400, years100, years4, remainingyears;
|
||||||
int yearleap;
|
int yearleap;
|
||||||
_CONST int *ip;
|
_CONST int *ip;
|
||||||
|
|
||||||
days = ((long)lcltime) / SECSPERDAY;
|
days = ((long)lcltime) / SECSPERDAY - EPOCH_ADJUSTMENT_DAYS;
|
||||||
rem = ((long)lcltime) % SECSPERDAY;
|
rem = ((long)lcltime) % SECSPERDAY;
|
||||||
while (rem < 0)
|
if (rem < 0)
|
||||||
{
|
{
|
||||||
rem += SECSPERDAY;
|
rem += SECSPERDAY;
|
||||||
--days;
|
--days;
|
||||||
@ -55,45 +67,68 @@ _DEFUN (gmtime_r, (tim_p, res),
|
|||||||
res->tm_sec = (int) (rem % SECSPERMIN);
|
res->tm_sec = (int) (rem % SECSPERMIN);
|
||||||
|
|
||||||
/* compute day of week */
|
/* compute day of week */
|
||||||
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
||||||
res->tm_wday += DAYSPERWEEK;
|
weekday += DAYSPERWEEK;
|
||||||
|
res->tm_wday = weekday;
|
||||||
|
|
||||||
/* compute year & day of year */
|
/* compute year & day of year */
|
||||||
years400 = days / DAYS_PER_400_YEARS;
|
years400 = days / DAYS_PER_400_YEARS;
|
||||||
days -= years400 * DAYS_PER_400_YEARS;
|
days -= years400 * DAYS_PER_400_YEARS;
|
||||||
|
/* simplify by making the values positive */
|
||||||
|
if (days < 0)
|
||||||
|
{
|
||||||
|
days += DAYS_PER_400_YEARS;
|
||||||
|
--years400;
|
||||||
|
}
|
||||||
|
|
||||||
years100 = days / DAYS_PER_100_YEARS;
|
years100 = days / DAYS_PER_100_YEARS;
|
||||||
|
if (years100 == 4) /* required for proper day of year calculation */
|
||||||
|
--years100;
|
||||||
days -= years100 * DAYS_PER_100_YEARS;
|
days -= years100 * DAYS_PER_100_YEARS;
|
||||||
years4 = days / DAYS_PER_4_YEARS;
|
years4 = days / DAYS_PER_4_YEARS;
|
||||||
days -= years4 * DAYS_PER_4_YEARS;
|
days -= years4 * DAYS_PER_4_YEARS;
|
||||||
|
remainingyears = days / DAYS_PER_YEAR;
|
||||||
|
if (remainingyears == 4) /* required for proper day of year calculation */
|
||||||
|
--remainingyears;
|
||||||
|
days -= remainingyears * DAYS_PER_YEAR;
|
||||||
|
|
||||||
year = EPOCH_YEAR + years400 * 400 + years100 * 100 + years4 * 4;
|
year = ADJUSTED_EPOCH_YEAR + years400 * 400 + years100 * 100 + years4 * 4 +
|
||||||
|
remainingyears;
|
||||||
|
|
||||||
/* the value left in days is based in 1970 */
|
/* If remainingyears is zero, it means that the years were completely
|
||||||
if (days < 0)
|
* "consumed" by modulo calculations by 400, 100 and 4, so the year is:
|
||||||
|
* 1. a multiple of 4, but not a multiple of 100 or 400 - it's a leap year,
|
||||||
|
* 2. a multiple of 4 and 100, but not a multiple of 400 - it's not a leap
|
||||||
|
* year,
|
||||||
|
* 3. a multiple of 4, 100 and 400 - it's a leap year.
|
||||||
|
* If years4 is non-zero, it means that the year is not a multiple of 100 or
|
||||||
|
* 400 (case 1), so it's a leap year. If years100 is zero (and years4 is zero
|
||||||
|
* - due to short-circuiting), it means that the year is a multiple of 400
|
||||||
|
* (case 3), so it's also a leap year. */
|
||||||
|
yearleap = remainingyears == 0 && (years4 != 0 || years100 == 0);
|
||||||
|
|
||||||
|
/* adjust back to 1st January */
|
||||||
|
yearday = days + DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + yearleap;
|
||||||
|
if (yearday >= DAYS_PER_YEAR + yearleap)
|
||||||
{
|
{
|
||||||
ip = &days_per_year[3];
|
yearday -= DAYS_PER_YEAR + yearleap;
|
||||||
while (days < 0)
|
++year;
|
||||||
{
|
|
||||||
days += *ip--;
|
|
||||||
--year;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
res->tm_yday = yearday;
|
||||||
{
|
|
||||||
ip = &days_per_year[0];
|
|
||||||
while (days >= *ip)
|
|
||||||
{
|
|
||||||
days -= *ip++;
|
|
||||||
++year;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res->tm_year = year - YEAR_BASE;
|
res->tm_year = year - YEAR_BASE;
|
||||||
res->tm_yday = days;
|
|
||||||
yearleap = isleap(year);
|
/* Because "days" is the number of days since 1st March, the additional leap
|
||||||
ip = __month_lengths[yearleap];
|
* day (29th of February) is the last possible day, so it doesn't matter much
|
||||||
for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon)
|
* whether the year is actually leap or not. */
|
||||||
days -= ip[res->tm_mon];
|
ip = __month_lengths[1];
|
||||||
|
month = 2;
|
||||||
|
while (days >= ip[month])
|
||||||
|
{
|
||||||
|
days -= ip[month];
|
||||||
|
if (++month >= MONSPERYEAR)
|
||||||
|
month = 0;
|
||||||
|
}
|
||||||
|
res->tm_mon = month;
|
||||||
res->tm_mday = days + 1;
|
res->tm_mday = days + 1;
|
||||||
|
|
||||||
res->tm_isdst = 0;
|
res->tm_isdst = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user