Add from-week-date conversion functions

This commit is contained in:
Laurence Withers 2006-10-15 19:41:04 +01:00
parent fdd856381d
commit 582b960e0b
2 changed files with 40 additions and 8 deletions

View File

@ -16,6 +16,8 @@
* of the next day). * of the next day).
*/ */
#define DAYS_IN_400_YEARS (146097)
int iso8601_isleap(int year) int iso8601_isleap(int year)
@ -74,14 +76,14 @@ static void _to_year(int* year, int* days_left, const struct iso8601_date* date)
div_t qr; div_t qr;
int ndays = date->day; int ndays = date->day;
// Each 400 years have 97 leap days, giving 365*400+97 = 146097 days in 400 years // Each 400 years have 97 leap days, giving 365*400+97 = DAYS_IN_400_YEARS days in 400 years
qr = div(ndays, 146097); qr = div(ndays, DAYS_IN_400_YEARS);
*year = qr.quot * 400; *year = qr.quot * 400;
ndays = qr.rem; ndays = qr.rem;
// ensure that we always end up with between 0 and 146096 days remaining // ensure that we always end up with between 0 and 146096 days remaining
if(ndays < 0) { if(ndays < 0) {
ndays += 146097; ndays += DAYS_IN_400_YEARS;
*year -= 400; *year -= 400;
} }
@ -250,14 +252,14 @@ int _from_year(struct iso8601_date* date, int year)
return -1; return -1;
} }
// Each 400 years have 97 leap days, giving 365*400+97 = 146097 days in 400 years // Each 400 years have 97 leap days, giving 365*400+97 = DAYS_IN_400_YEARS days in 400 years
qr = div(year, 400); qr = div(year, 400);
date->day = qr.quot * 146097; date->day = qr.quot * DAYS_IN_400_YEARS;
year = qr.rem; year = qr.rem;
// ensure we have between 0 and 399 years // ensure we have between 0 and 399 years
if(year < 0) { if(year < 0) {
date->day -= 146097; date->day -= DAYS_IN_400_YEARS;
year += 400; year += 400;
} }
@ -316,6 +318,28 @@ int iso8601_from_ord(struct iso8601_date* date, int year, int oday)
int iso8601_from_week(struct iso8601_date* date, int isoyear, int week, int wday) int iso8601_from_week(struct iso8601_date* date, int isoyear, int week, int wday)
{ {
int day1off;
_from_year(date, isoyear);
// 400-year cycle; ensure we're between 0-400 years
isoyear %= 400;
if(isoyear < 0) isoyear += 400;
/* Algorithm notes:
* We now compute the offset between the start day of the ISO year and the start day of the
* real year. Year 0000 starts on a Saturday, meaning the ISO year 0000 starts on 0000-003
* (offset of +2 days). Each year reduces the offset by one day, and each leap year reduces it
* by a further day; the offset wraps from -3 to +3.
*/
day1off = 2 - isoyear; // reduce offset by 1 for each year
day1off += (isoyear - 1) / 100; // cancel out 1 leap year for each century past the first
day1off -= (isoyear + 3) / 4; // 1 leap year every 4 years
day1off %= 7;
if(day1off < -3) day1off += 7;
// now simply add in the day offset and days/weeks elapsed
date->day += day1off + (week - 1) * 7 + wday - 1;
} }

View File

@ -128,7 +128,7 @@ int try_weeks(void)
// starting position // starting position
int weekcount = 52, weekday = 5, isoyear = -1; int weekcount = 52, weekday = 5, isoyear = -1;
int y, wk, wd, ret = 0; int y, wk, wd, ret = 0;
struct iso8601_date dt; struct iso8601_date dt, dt2;
for(dt.day = 0; dt.day < MAX_YEAR_COUNT * 366; ++dt.day) { for(dt.day = 0; dt.day < MAX_YEAR_COUNT * 366; ++dt.day) {
iso8601_to_week(&y, &wk, &wd, &dt); iso8601_to_week(&y, &wk, &wd, &dt);
@ -141,9 +141,17 @@ int try_weeks(void)
++isoyear; ++isoyear;
} }
} }
if(wk != weekcount || y != isoyear) goto discont;
if(wk != weekcount || y != isoyear) goto discont;
weekday = wd; weekday = wd;
iso8601_from_week(&dt2, isoyear, weekcount, weekday);
if(dt2.day != dt.day) {
printf("[weekmismatch ] %04d-W%02d-%d: should be %d, got %d\n",
isoyear, weekcount, weekday, dt.day, dt2.day);
if(++ret > 20) return ret;
}
continue; continue;
discont: discont: