From: Jarkko Hietaniemi Date: Wed, 18 Apr 2001 23:11:03 +0000 (+0000) Subject: Move the init_tm() and mini_mktime() up from POSIX.xs to util.c X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=e72cf795050cdfe9905e00270c38ba2547626581;p=p5sagit%2Fp5-mst-13.2.git Move the init_tm() and mini_mktime() up from POSIX.xs to util.c in preparation of Time::Piece. p4raw-id: //depot/perl@9745 --- diff --git a/embed.h b/embed.h index 685f9e8..a5bb53e 100644 --- a/embed.h +++ b/embed.h @@ -271,6 +271,7 @@ #define ingroup Perl_ingroup #define init_debugger Perl_init_debugger #define init_stacks Perl_init_stacks +#define init_tm Perl_init_tm #define intro_my Perl_intro_my #define instr Perl_instr #define io_close Perl_io_close @@ -409,6 +410,7 @@ #define mg_magical Perl_mg_magical #define mg_set Perl_mg_set #define mg_size Perl_mg_size +#define mini_mktime Perl_mini_mktime #define mod Perl_mod #define mode_from_discipline Perl_mode_from_discipline #define moreswitches Perl_moreswitches @@ -1766,6 +1768,7 @@ #define ingroup(a,b) Perl_ingroup(aTHX_ a,b) #define init_debugger() Perl_init_debugger(aTHX) #define init_stacks() Perl_init_stacks(aTHX) +#define init_tm(a) Perl_init_tm(aTHX_ a) #define intro_my() Perl_intro_my(aTHX) #define instr(a,b) Perl_instr(aTHX_ a,b) #define io_close(a,b) Perl_io_close(aTHX_ a,b) @@ -1902,6 +1905,7 @@ #define mg_magical(a) Perl_mg_magical(aTHX_ a) #define mg_set(a) Perl_mg_set(aTHX_ a) #define mg_size(a) Perl_mg_size(aTHX_ a) +#define mini_mktime(a) Perl_mini_mktime(aTHX_ a) #define mod(a,b) Perl_mod(aTHX_ a,b) #define mode_from_discipline(a) Perl_mode_from_discipline(aTHX_ a) #define moreswitches(a) Perl_moreswitches(aTHX_ a) @@ -3462,6 +3466,8 @@ #define init_debugger Perl_init_debugger #define Perl_init_stacks CPerlObj::Perl_init_stacks #define init_stacks Perl_init_stacks +#define Perl_init_tm CPerlObj::Perl_init_tm +#define init_tm Perl_init_tm #define Perl_intro_my CPerlObj::Perl_intro_my #define intro_my Perl_intro_my #define Perl_instr CPerlObj::Perl_instr @@ -3732,6 +3738,8 @@ #define mg_set Perl_mg_set #define Perl_mg_size CPerlObj::Perl_mg_size #define mg_size Perl_mg_size +#define Perl_mini_mktime CPerlObj::Perl_mini_mktime +#define mini_mktime Perl_mini_mktime #define Perl_mod CPerlObj::Perl_mod #define mod Perl_mod #define Perl_mode_from_discipline CPerlObj::Perl_mode_from_discipline diff --git a/embed.pl b/embed.pl index 8cc04b0..3d0160d 100755 --- a/embed.pl +++ b/embed.pl @@ -1610,6 +1610,7 @@ Ap |I32 |ibcmp_locale |const char* a|const char* b|I32 len p |bool |ingroup |Gid_t testgid|Uid_t effective p |void |init_debugger Ap |void |init_stacks +p |void |init_tm |struct tm *ptm p |U32 |intro_my Ap |char* |instr |const char* big|const char* little p |bool |io_close |IO* io|bool not_implicit @@ -1748,6 +1749,7 @@ Apd |U32 |mg_length |SV* sv Apd |void |mg_magical |SV* sv Apd |int |mg_set |SV* sv Ap |I32 |mg_size |SV* sv +p |void |mini_mktime |struct tm *pm p |OP* |mod |OP* o|I32 type p |int |mode_from_discipline|SV* discp Ap |char* |moreswitches |char* s diff --git a/ext/POSIX/POSIX.xs b/ext/POSIX/POSIX.xs index 145dab7..7658b39 100644 --- a/ext/POSIX/POSIX.xs +++ b/ext/POSIX/POSIX.xs @@ -300,246 +300,6 @@ unsigned long strtoul (const char *, char **, int); #define localeconv() not_here("localeconv") #endif -#ifdef HAS_TZNAME -# if !defined(WIN32) && !defined(__CYGWIN__) -extern char *tzname[]; -# endif -#else -#if !defined(WIN32) || (defined(__MINGW32__) && !defined(tzname)) -char *tzname[] = { "" , "" }; -#endif -#endif - -/* XXX struct tm on some systems (SunOS4/BSD) contains extra (non POSIX) - * fields for which we don't have Configure support yet: - * char *tm_zone; -- abbreviation of timezone name - * long tm_gmtoff; -- offset from GMT in seconds - * To workaround core dumps from the uninitialised tm_zone we get the - * system to give us a reasonable struct to copy. This fix means that - * strftime uses the tm_zone and tm_gmtoff values returned by - * localtime(time()). That should give the desired result most of the - * time. But probably not always! - * - * This is a temporary workaround to be removed once Configure - * support is added and NETaa14816 is considered in full. - * It does not address tzname aspects of NETaa14816. - */ -#ifdef HAS_GNULIBC -# ifndef STRUCT_TM_HASZONE -# define STRUCT_TM_HASZONE -# endif -#endif - -#ifdef STRUCT_TM_HASZONE -static void -init_tm(struct tm *ptm) /* see mktime, strftime and asctime */ -{ - Time_t now; - (void)time(&now); - Copy(localtime(&now), ptm, 1, struct tm); -} - -#else -# define init_tm(ptm) -#endif - -/* - * mini_mktime - normalise struct tm values without the localtime() - * semantics (and overhead) of mktime(). - */ -static void -mini_mktime(struct tm *ptm) -{ - int yearday; - int secs; - int month, mday, year, jday; - int odd_cent, odd_year; - -#define DAYS_PER_YEAR 365 -#define DAYS_PER_QYEAR (4*DAYS_PER_YEAR+1) -#define DAYS_PER_CENT (25*DAYS_PER_QYEAR-1) -#define DAYS_PER_QCENT (4*DAYS_PER_CENT+1) -#define SECS_PER_HOUR (60*60) -#define SECS_PER_DAY (24*SECS_PER_HOUR) -/* parentheses deliberately absent on these two, otherwise they don't work */ -#define MONTH_TO_DAYS 153/5 -#define DAYS_TO_MONTH 5/153 -/* offset to bias by March (month 4) 1st between month/mday & year finding */ -#define YEAR_ADJUST (4*MONTH_TO_DAYS+1) -/* as used here, the algorithm leaves Sunday as day 1 unless we adjust it */ -#define WEEKDAY_BIAS 6 /* (1+6)%7 makes Sunday 0 again */ - -/* - * Year/day algorithm notes: - * - * With a suitable offset for numeric value of the month, one can find - * an offset into the year by considering months to have 30.6 (153/5) days, - * using integer arithmetic (i.e., with truncation). To avoid too much - * messing about with leap days, we consider January and February to be - * the 13th and 14th month of the previous year. After that transformation, - * we need the month index we use to be high by 1 from 'normal human' usage, - * so the month index values we use run from 4 through 15. - * - * Given that, and the rules for the Gregorian calendar (leap years are those - * divisible by 4 unless also divisible by 100, when they must be divisible - * by 400 instead), we can simply calculate the number of days since some - * arbitrary 'beginning of time' by futzing with the (adjusted) year number, - * the days we derive from our month index, and adding in the day of the - * month. The value used here is not adjusted for the actual origin which - * it normally would use (1 January A.D. 1), since we're not exposing it. - * We're only building the value so we can turn around and get the - * normalised values for the year, month, day-of-month, and day-of-year. - * - * For going backward, we need to bias the value we're using so that we find - * the right year value. (Basically, we don't want the contribution of - * March 1st to the number to apply while deriving the year). Having done - * that, we 'count up' the contribution to the year number by accounting for - * full quadracenturies (400-year periods) with their extra leap days, plus - * the contribution from full centuries (to avoid counting in the lost leap - * days), plus the contribution from full quad-years (to count in the normal - * leap days), plus the leftover contribution from any non-leap years. - * At this point, if we were working with an actual leap day, we'll have 0 - * days left over. This is also true for March 1st, however. So, we have - * to special-case that result, and (earlier) keep track of the 'odd' - * century and year contributions. If we got 4 extra centuries in a qcent, - * or 4 extra years in a qyear, then it's a leap day and we call it 29 Feb. - * Otherwise, we add back in the earlier bias we removed (the 123 from - * figuring in March 1st), find the month index (integer division by 30.6), - * and the remainder is the day-of-month. We then have to convert back to - * 'real' months (including fixing January and February from being 14/15 in - * the previous year to being in the proper year). After that, to get - * tm_yday, we work with the normalised year and get a new yearday value for - * January 1st, which we subtract from the yearday value we had earlier, - * representing the date we've re-built. This is done from January 1 - * because tm_yday is 0-origin. - * - * Since POSIX time routines are only guaranteed to work for times since the - * UNIX epoch (00:00:00 1 Jan 1970 UTC), the fact that this algorithm - * applies Gregorian calendar rules even to dates before the 16th century - * doesn't bother me. Besides, you'd need cultural context for a given - * date to know whether it was Julian or Gregorian calendar, and that's - * outside the scope for this routine. Since we convert back based on the - * same rules we used to build the yearday, you'll only get strange results - * for input which needed normalising, or for the 'odd' century years which - * were leap years in the Julian calander but not in the Gregorian one. - * I can live with that. - * - * This algorithm also fails to handle years before A.D. 1 gracefully, but - * that's still outside the scope for POSIX time manipulation, so I don't - * care. - */ - - year = 1900 + ptm->tm_year; - month = ptm->tm_mon; - mday = ptm->tm_mday; - /* allow given yday with no month & mday to dominate the result */ - if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) { - month = 0; - mday = 0; - jday = 1 + ptm->tm_yday; - } - else { - jday = 0; - } - if (month >= 2) - month+=2; - else - month+=14, year--; - yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400; - yearday += month*MONTH_TO_DAYS + mday + jday; - /* - * Note that we don't know when leap-seconds were or will be, - * so we have to trust the user if we get something which looks - * like a sensible leap-second. Wild values for seconds will - * be rationalised, however. - */ - if ((unsigned) ptm->tm_sec <= 60) { - secs = 0; - } - else { - secs = ptm->tm_sec; - ptm->tm_sec = 0; - } - secs += 60 * ptm->tm_min; - secs += SECS_PER_HOUR * ptm->tm_hour; - if (secs < 0) { - if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) { - /* got negative remainder, but need positive time */ - /* back off an extra day to compensate */ - yearday += (secs/SECS_PER_DAY)-1; - secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1); - } - else { - yearday += (secs/SECS_PER_DAY); - secs -= SECS_PER_DAY * (secs/SECS_PER_DAY); - } - } - else if (secs >= SECS_PER_DAY) { - yearday += (secs/SECS_PER_DAY); - secs %= SECS_PER_DAY; - } - ptm->tm_hour = secs/SECS_PER_HOUR; - secs %= SECS_PER_HOUR; - ptm->tm_min = secs/60; - secs %= 60; - ptm->tm_sec += secs; - /* done with time of day effects */ - /* - * The algorithm for yearday has (so far) left it high by 428. - * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to - * bias it by 123 while trying to figure out what year it - * really represents. Even with this tweak, the reverse - * translation fails for years before A.D. 0001. - * It would still fail for Feb 29, but we catch that one below. - */ - jday = yearday; /* save for later fixup vis-a-vis Jan 1 */ - yearday -= YEAR_ADJUST; - year = (yearday / DAYS_PER_QCENT) * 400; - yearday %= DAYS_PER_QCENT; - odd_cent = yearday / DAYS_PER_CENT; - year += odd_cent * 100; - yearday %= DAYS_PER_CENT; - year += (yearday / DAYS_PER_QYEAR) * 4; - yearday %= DAYS_PER_QYEAR; - odd_year = yearday / DAYS_PER_YEAR; - year += odd_year; - yearday %= DAYS_PER_YEAR; - if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */ - month = 1; - yearday = 29; - } - else { - yearday += YEAR_ADJUST; /* recover March 1st crock */ - month = yearday*DAYS_TO_MONTH; - yearday -= month*MONTH_TO_DAYS; - /* recover other leap-year adjustment */ - if (month > 13) { - month-=14; - year++; - } - else { - month-=2; - } - } - ptm->tm_year = year - 1900; - if (yearday) { - ptm->tm_mday = yearday; - ptm->tm_mon = month; - } - else { - ptm->tm_mday = 31; - ptm->tm_mon = month - 1; - } - /* re-build yearday based on Jan 1 to get tm_yday */ - year--; - yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400; - yearday += 14*MONTH_TO_DAYS + 1; - ptm->tm_yday = jday - yearday; - /* fix tm_wday if not overridden by caller */ - if ((unsigned)ptm->tm_wday > 6) - ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7; -} - #ifdef HAS_LONG_DOUBLE # if LONG_DOUBLESIZE > NVSIZE # undef HAS_LONG_DOUBLE /* XXX until we figure out how to use them */ diff --git a/proto.h b/proto.h index 92bb520..644b6b9 100644 --- a/proto.h +++ b/proto.h @@ -333,6 +333,7 @@ PERL_CALLCONV I32 Perl_ibcmp_locale(pTHX_ const char* a, const char* b, I32 len) PERL_CALLCONV bool Perl_ingroup(pTHX_ Gid_t testgid, Uid_t effective); PERL_CALLCONV void Perl_init_debugger(pTHX); PERL_CALLCONV void Perl_init_stacks(pTHX); +PERL_CALLCONV void Perl_init_tm(pTHX_ struct tm *ptm); PERL_CALLCONV U32 Perl_intro_my(pTHX); PERL_CALLCONV char* Perl_instr(pTHX_ const char* big, const char* little); PERL_CALLCONV bool Perl_io_close(pTHX_ IO* io, bool not_implicit); @@ -475,6 +476,7 @@ PERL_CALLCONV U32 Perl_mg_length(pTHX_ SV* sv); PERL_CALLCONV void Perl_mg_magical(pTHX_ SV* sv); PERL_CALLCONV int Perl_mg_set(pTHX_ SV* sv); PERL_CALLCONV I32 Perl_mg_size(pTHX_ SV* sv); +PERL_CALLCONV void Perl_mini_mktime(pTHX_ struct tm *pm); PERL_CALLCONV OP* Perl_mod(pTHX_ OP* o, I32 type); PERL_CALLCONV int Perl_mode_from_discipline(pTHX_ SV* discp); PERL_CALLCONV char* Perl_moreswitches(pTHX_ char* s); diff --git a/util.c b/util.c index c5a3af3..542d0dd 100644 --- a/util.c +++ b/util.c @@ -4124,3 +4124,240 @@ Perl_ebcdic_control(pTHX_ int ch) } } #endif + +#ifdef HAS_TZNAME +# if !defined(WIN32) && !defined(__CYGWIN__) +extern char *tzname[]; +# endif +#else +#if !defined(WIN32) || (defined(__MINGW32__) && !defined(tzname)) +char *tzname[] = { "" , "" }; +#endif +#endif + +/* XXX struct tm on some systems (SunOS4/BSD) contains extra (non POSIX) + * fields for which we don't have Configure support yet: + * char *tm_zone; -- abbreviation of timezone name + * long tm_gmtoff; -- offset from GMT in seconds + * To workaround core dumps from the uninitialised tm_zone we get the + * system to give us a reasonable struct to copy. This fix means that + * strftime uses the tm_zone and tm_gmtoff values returned by + * localtime(time()). That should give the desired result most of the + * time. But probably not always! + * + * This is a temporary workaround to be removed once Configure + * support is added and NETaa14816 is considered in full. + * It does not address tzname aspects of NETaa14816. + */ +#ifdef HAS_GNULIBC +# ifndef STRUCT_TM_HASZONE +# define STRUCT_TM_HASZONE +# endif +#endif + +void +init_tm(struct tm *ptm) /* see mktime, strftime and asctime */ +{ +#ifdef STRUCT_TM_HASZONE + Time_t now; + (void)time(&now); + Copy(localtime(&now), ptm, 1, struct tm); +#endif +} + +/* + * mini_mktime - normalise struct tm values without the localtime() + * semantics (and overhead) of mktime(). + */ +void +mini_mktime(struct tm *ptm) +{ + int yearday; + int secs; + int month, mday, year, jday; + int odd_cent, odd_year; + +#define DAYS_PER_YEAR 365 +#define DAYS_PER_QYEAR (4*DAYS_PER_YEAR+1) +#define DAYS_PER_CENT (25*DAYS_PER_QYEAR-1) +#define DAYS_PER_QCENT (4*DAYS_PER_CENT+1) +#define SECS_PER_HOUR (60*60) +#define SECS_PER_DAY (24*SECS_PER_HOUR) +/* parentheses deliberately absent on these two, otherwise they don't work */ +#define MONTH_TO_DAYS 153/5 +#define DAYS_TO_MONTH 5/153 +/* offset to bias by March (month 4) 1st between month/mday & year finding */ +#define YEAR_ADJUST (4*MONTH_TO_DAYS+1) +/* as used here, the algorithm leaves Sunday as day 1 unless we adjust it */ +#define WEEKDAY_BIAS 6 /* (1+6)%7 makes Sunday 0 again */ + +/* + * Year/day algorithm notes: + * + * With a suitable offset for numeric value of the month, one can find + * an offset into the year by considering months to have 30.6 (153/5) days, + * using integer arithmetic (i.e., with truncation). To avoid too much + * messing about with leap days, we consider January and February to be + * the 13th and 14th month of the previous year. After that transformation, + * we need the month index we use to be high by 1 from 'normal human' usage, + * so the month index values we use run from 4 through 15. + * + * Given that, and the rules for the Gregorian calendar (leap years are those + * divisible by 4 unless also divisible by 100, when they must be divisible + * by 400 instead), we can simply calculate the number of days since some + * arbitrary 'beginning of time' by futzing with the (adjusted) year number, + * the days we derive from our month index, and adding in the day of the + * month. The value used here is not adjusted for the actual origin which + * it normally would use (1 January A.D. 1), since we're not exposing it. + * We're only building the value so we can turn around and get the + * normalised values for the year, month, day-of-month, and day-of-year. + * + * For going backward, we need to bias the value we're using so that we find + * the right year value. (Basically, we don't want the contribution of + * March 1st to the number to apply while deriving the year). Having done + * that, we 'count up' the contribution to the year number by accounting for + * full quadracenturies (400-year periods) with their extra leap days, plus + * the contribution from full centuries (to avoid counting in the lost leap + * days), plus the contribution from full quad-years (to count in the normal + * leap days), plus the leftover contribution from any non-leap years. + * At this point, if we were working with an actual leap day, we'll have 0 + * days left over. This is also true for March 1st, however. So, we have + * to special-case that result, and (earlier) keep track of the 'odd' + * century and year contributions. If we got 4 extra centuries in a qcent, + * or 4 extra years in a qyear, then it's a leap day and we call it 29 Feb. + * Otherwise, we add back in the earlier bias we removed (the 123 from + * figuring in March 1st), find the month index (integer division by 30.6), + * and the remainder is the day-of-month. We then have to convert back to + * 'real' months (including fixing January and February from being 14/15 in + * the previous year to being in the proper year). After that, to get + * tm_yday, we work with the normalised year and get a new yearday value for + * January 1st, which we subtract from the yearday value we had earlier, + * representing the date we've re-built. This is done from January 1 + * because tm_yday is 0-origin. + * + * Since POSIX time routines are only guaranteed to work for times since the + * UNIX epoch (00:00:00 1 Jan 1970 UTC), the fact that this algorithm + * applies Gregorian calendar rules even to dates before the 16th century + * doesn't bother me. Besides, you'd need cultural context for a given + * date to know whether it was Julian or Gregorian calendar, and that's + * outside the scope for this routine. Since we convert back based on the + * same rules we used to build the yearday, you'll only get strange results + * for input which needed normalising, or for the 'odd' century years which + * were leap years in the Julian calander but not in the Gregorian one. + * I can live with that. + * + * This algorithm also fails to handle years before A.D. 1 gracefully, but + * that's still outside the scope for POSIX time manipulation, so I don't + * care. + */ + + year = 1900 + ptm->tm_year; + month = ptm->tm_mon; + mday = ptm->tm_mday; + /* allow given yday with no month & mday to dominate the result */ + if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) { + month = 0; + mday = 0; + jday = 1 + ptm->tm_yday; + } + else { + jday = 0; + } + if (month >= 2) + month+=2; + else + month+=14, year--; + yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400; + yearday += month*MONTH_TO_DAYS + mday + jday; + /* + * Note that we don't know when leap-seconds were or will be, + * so we have to trust the user if we get something which looks + * like a sensible leap-second. Wild values for seconds will + * be rationalised, however. + */ + if ((unsigned) ptm->tm_sec <= 60) { + secs = 0; + } + else { + secs = ptm->tm_sec; + ptm->tm_sec = 0; + } + secs += 60 * ptm->tm_min; + secs += SECS_PER_HOUR * ptm->tm_hour; + if (secs < 0) { + if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) { + /* got negative remainder, but need positive time */ + /* back off an extra day to compensate */ + yearday += (secs/SECS_PER_DAY)-1; + secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1); + } + else { + yearday += (secs/SECS_PER_DAY); + secs -= SECS_PER_DAY * (secs/SECS_PER_DAY); + } + } + else if (secs >= SECS_PER_DAY) { + yearday += (secs/SECS_PER_DAY); + secs %= SECS_PER_DAY; + } + ptm->tm_hour = secs/SECS_PER_HOUR; + secs %= SECS_PER_HOUR; + ptm->tm_min = secs/60; + secs %= 60; + ptm->tm_sec += secs; + /* done with time of day effects */ + /* + * The algorithm for yearday has (so far) left it high by 428. + * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to + * bias it by 123 while trying to figure out what year it + * really represents. Even with this tweak, the reverse + * translation fails for years before A.D. 0001. + * It would still fail for Feb 29, but we catch that one below. + */ + jday = yearday; /* save for later fixup vis-a-vis Jan 1 */ + yearday -= YEAR_ADJUST; + year = (yearday / DAYS_PER_QCENT) * 400; + yearday %= DAYS_PER_QCENT; + odd_cent = yearday / DAYS_PER_CENT; + year += odd_cent * 100; + yearday %= DAYS_PER_CENT; + year += (yearday / DAYS_PER_QYEAR) * 4; + yearday %= DAYS_PER_QYEAR; + odd_year = yearday / DAYS_PER_YEAR; + year += odd_year; + yearday %= DAYS_PER_YEAR; + if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */ + month = 1; + yearday = 29; + } + else { + yearday += YEAR_ADJUST; /* recover March 1st crock */ + month = yearday*DAYS_TO_MONTH; + yearday -= month*MONTH_TO_DAYS; + /* recover other leap-year adjustment */ + if (month > 13) { + month-=14; + year++; + } + else { + month-=2; + } + } + ptm->tm_year = year - 1900; + if (yearday) { + ptm->tm_mday = yearday; + ptm->tm_mon = month; + } + else { + ptm->tm_mday = 31; + ptm->tm_mon = month - 1; + } + /* re-build yearday based on Jan 1 to get tm_yday */ + year--; + yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400; + yearday += 14*MONTH_TO_DAYS + 1; + ptm->tm_yday = jday - yearday; + /* fix tm_wday if not overridden by caller */ + if ((unsigned)ptm->tm_wday > 6) + ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7; +}