150 lines
4.2 KiB
C
150 lines
4.2 KiB
C
#include "time_header.h"
|
|
|
|
/**
|
|
* @brief
|
|
* Return the number of leap years through the end of the given year
|
|
* where, to make the math easy, the answer for year zero is defined as zero.
|
|
*
|
|
*/
|
|
static int leaps_thru_end_of(const int y)
|
|
{
|
|
return (y >= 0) ?
|
|
(y / 4 - y / 100 + y / 400) :
|
|
-(leaps_thru_end_of(-(y + 1)) + 1);
|
|
}
|
|
|
|
struct tm *timesub(
|
|
const time_t *const timep,
|
|
const int_fast32_t offset,
|
|
const struct state *const sp,
|
|
struct tm *const tmp)
|
|
{
|
|
register const struct lsinfo* lp;
|
|
register time_t tdays;
|
|
register int idays; // unsigned would be so 2003
|
|
register int_fast64_t rem;
|
|
int y;
|
|
register const int* ip;
|
|
register int_fast64_t corr;
|
|
register bool hit;
|
|
register int i;
|
|
|
|
corr = 0;
|
|
hit = false;
|
|
i = (sp == NULL) ? 0 : sp->leapcnt;
|
|
|
|
while (--i >= 0) {
|
|
lp = &sp->lsis[i];
|
|
if (*timep >= lp->ls_trans) {
|
|
if (*timep == lp->ls_trans) {
|
|
hit = ((i == 0 && lp->ls_corr > 0) ||
|
|
lp->ls_corr > sp->lsis[i - 1].ls_corr);
|
|
if (hit)
|
|
while (i > 0 &&
|
|
sp->lsis[i].ls_trans ==
|
|
sp->lsis[i - 1].ls_trans + 1 &&
|
|
sp->lsis[i].ls_corr ==
|
|
sp->lsis[i - 1].ls_corr + 1) {
|
|
++hit;
|
|
--i;
|
|
}
|
|
}
|
|
corr = lp->ls_corr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
y = EPOCH_YEAR;
|
|
tdays = *timep / SECSPERDAY;
|
|
rem = *timep - tdays * SECSPERDAY;
|
|
|
|
while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
|
|
int newy;
|
|
time_t tdelta;
|
|
int idelta;
|
|
int leapdays;
|
|
|
|
tdelta = tdays / DAYSPERLYEAR;
|
|
if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta)
|
|
&& tdelta <= INT_MAX))
|
|
goto out_of_range;
|
|
idelta = tdelta;
|
|
if (idelta == 0)
|
|
idelta = (tdays < 0) ? -1 : 1;
|
|
newy = y;
|
|
if (increment_overflow(&newy, idelta))
|
|
goto out_of_range;
|
|
leapdays = leaps_thru_end_of(newy - 1) -
|
|
leaps_thru_end_of(y - 1);
|
|
tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
|
|
tdays -= leapdays;
|
|
y = newy;
|
|
}
|
|
{
|
|
int_fast32_t seconds;
|
|
|
|
seconds = tdays * SECSPERDAY;
|
|
tdays = seconds / SECSPERDAY;
|
|
rem += seconds - tdays * SECSPERDAY;
|
|
}
|
|
/*
|
|
** Given the range, we can now fearlessly cast...
|
|
*/
|
|
idays = tdays;
|
|
rem += offset - corr;
|
|
while (rem < 0) {
|
|
rem += SECSPERDAY;
|
|
--idays;
|
|
}
|
|
while (rem >= SECSPERDAY) {
|
|
rem -= SECSPERDAY;
|
|
++idays;
|
|
}
|
|
while (idays < 0) {
|
|
if (increment_overflow(&y, -1))
|
|
goto out_of_range;
|
|
idays += year_lengths[isleap(y)];
|
|
}
|
|
while (idays >= year_lengths[isleap(y)]) {
|
|
idays -= year_lengths[isleap(y)];
|
|
if (increment_overflow(&y, 1))
|
|
goto out_of_range;
|
|
}
|
|
tmp->tm_year = y;
|
|
if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
|
|
goto out_of_range;
|
|
tmp->tm_yday = idays;
|
|
/*
|
|
** The "extra" mods below avoid overflow problems.
|
|
*/
|
|
tmp->tm_wday = EPOCH_WDAY +
|
|
((y - EPOCH_YEAR) % DAYSPERWEEK) *
|
|
(DAYSPERNYEAR % DAYSPERWEEK) +
|
|
leaps_thru_end_of(y - 1) -
|
|
leaps_thru_end_of(EPOCH_YEAR - 1) +
|
|
idays;
|
|
tmp->tm_wday %= DAYSPERWEEK;
|
|
if (tmp->tm_wday < 0)
|
|
tmp->tm_wday += DAYSPERWEEK;
|
|
tmp->tm_hour = (int) (rem / SECSPERHOUR);
|
|
rem %= SECSPERHOUR;
|
|
tmp->tm_min = (int) (rem / SECSPERMIN);
|
|
/*
|
|
** A positive leap second requires a special
|
|
** representation. This uses "... ??:59:60" et seq.
|
|
*/
|
|
tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
|
|
ip = mon_lengths[isleap(y)];
|
|
for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
|
|
idays -= ip[tmp->tm_mon];
|
|
tmp->tm_mday = (int) (idays + 1);
|
|
tmp->tm_isdst = 0;
|
|
tmp->tm_gmtoff = offset;
|
|
|
|
return tmp;
|
|
|
|
out_of_range:
|
|
errno = EOVERFLOW;
|
|
return NULL;
|
|
}
|