Add from-week-date conversion functions
This commit is contained in:
		
							parent
							
								
									fdd856381d
								
							
						
					
					
						commit
						582b960e0b
					
				| 
						 | 
				
			
			@ -16,6 +16,8 @@
 | 
			
		|||
 *  of the next day).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define DAYS_IN_400_YEARS (146097)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    int ndays = date->day;
 | 
			
		||||
 | 
			
		||||
    // Each 400 years have 97 leap days, giving 365*400+97 = 146097 days in 400 years
 | 
			
		||||
    qr = div(ndays, 146097);
 | 
			
		||||
    // Each 400 years have 97 leap days, giving 365*400+97 = DAYS_IN_400_YEARS days in 400 years
 | 
			
		||||
    qr = div(ndays, DAYS_IN_400_YEARS);
 | 
			
		||||
    *year = qr.quot * 400;
 | 
			
		||||
    ndays = qr.rem;
 | 
			
		||||
 | 
			
		||||
    // ensure that we always end up with between 0 and 146096 days remaining
 | 
			
		||||
    if(ndays < 0) {
 | 
			
		||||
        ndays += 146097;
 | 
			
		||||
        ndays += DAYS_IN_400_YEARS;
 | 
			
		||||
        *year -= 400;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -250,14 +252,14 @@ int _from_year(struct iso8601_date* date, int year)
 | 
			
		|||
        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);
 | 
			
		||||
    date->day = qr.quot * 146097;
 | 
			
		||||
    date->day = qr.quot * DAYS_IN_400_YEARS;
 | 
			
		||||
    year = qr.rem;
 | 
			
		||||
 | 
			
		||||
    // ensure we have between 0 and 399 years
 | 
			
		||||
    if(year < 0) {
 | 
			
		||||
        date->day -= 146097;
 | 
			
		||||
        date->day -= DAYS_IN_400_YEARS;
 | 
			
		||||
        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 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -128,7 +128,7 @@ int try_weeks(void)
 | 
			
		|||
    // starting position
 | 
			
		||||
    int weekcount = 52, weekday = 5, isoyear = -1;
 | 
			
		||||
    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) {
 | 
			
		||||
        iso8601_to_week(&y, &wk, &wd, &dt);
 | 
			
		||||
| 
						 | 
				
			
			@ -141,9 +141,17 @@ int try_weeks(void)
 | 
			
		|||
                ++isoyear;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if(wk != weekcount || y != isoyear) goto discont;
 | 
			
		||||
 | 
			
		||||
        if(wk != weekcount || y != isoyear) goto discont;
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
    discont:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue