Add from weekdate functions.
This commit is contained in:
		
							parent
							
								
									19d55ad54c
								
							
						
					
					
						commit
						106f60b5c0
					
				| 
						 | 
					@ -127,7 +127,7 @@ void iso8601_to_cal(int* year, int* month, int* day, const struct iso8601_date*
 | 
				
			||||||
    mc = iso8601_isleap(*year) ? _days_in_month_leap : _days_in_month_common;
 | 
					    mc = iso8601_isleap(*year) ? _days_in_month_leap : _days_in_month_common;
 | 
				
			||||||
    *month = 1;
 | 
					    *month = 1;
 | 
				
			||||||
    while(ndays >= mc->days) {
 | 
					    while(ndays >= mc->days) {
 | 
				
			||||||
        ++*month;
 | 
					        *month += 1;
 | 
				
			||||||
        ndays -= mc->days;
 | 
					        ndays -= mc->days;
 | 
				
			||||||
        ++mc;
 | 
					        ++mc;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -149,6 +149,97 @@ void iso8601_to_ord(int* year, int* oday, const struct iso8601_date* date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int _weekday_of_year(int year)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int w = 6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Algorithm notes:
 | 
				
			||||||
 | 
					     *  - 0 = sun, 1 = mon, ..., 6 = sat
 | 
				
			||||||
 | 
					     *  - 0000-001 is a Saturday, day 6
 | 
				
			||||||
 | 
					     *  - every year we pass gives us one additional day (364 is divisible by 7)
 | 
				
			||||||
 | 
					     *  - but of course every leap year we pass gives us a further day
 | 
				
			||||||
 | 
					     *  - so for every 400 years, we add 497 (400 common years, 97 leap years); 497 % 7 = 0
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    year %= 400;
 | 
				
			||||||
 | 
					    if(year < 0) year += 400; // end up with between 0-399 years left
 | 
				
			||||||
 | 
					    w += year; // excluding leap years, we increase by 1 day a year
 | 
				
			||||||
 | 
					    w += (year + 3) / 4; // there is one leap year for every four years
 | 
				
			||||||
 | 
					    w -= (year - 1) / 100; // but one less for every century over 0
 | 
				
			||||||
 | 
					    w %= 7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return w;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void iso8601_to_week(int* year, int* week, int* wday, const struct iso8601_date* date)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ndays, w, has53 = 0;
 | 
				
			||||||
 | 
					    div_t d;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // perform year and weekday calculation
 | 
				
			||||||
 | 
					    _to_year(year, &ndays, date);
 | 
				
			||||||
 | 
					    w = _weekday_of_year(*year);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // find out what day jan 1 was; from there, we can find the ISO week and year number
 | 
				
			||||||
 | 
					    switch(w) {
 | 
				
			||||||
 | 
					    case 4: // W01 starts XXXY-12-28
 | 
				
			||||||
 | 
					        w += 7;
 | 
				
			||||||
 | 
					        has53 = 1; // years starting Thursday have 53 weeks
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 5: // W01 starts XXXZ-01-03
 | 
				
			||||||
 | 
					    case 6: // W01 starts XXXZ-01-02
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 0: // W01 starts XXXZ-01-01
 | 
				
			||||||
 | 
					    case 1: // W01 starts XXXY-12-31
 | 
				
			||||||
 | 
					    case 2: // W01 starts XXXY-12-30
 | 
				
			||||||
 | 
					        w += 7; // for week calculation
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 3: // W01 starts XXXY-12-29
 | 
				
			||||||
 | 
					        w += 7;
 | 
				
			||||||
 | 
					        if(iso8601_isleap(*year)) has53 = 1; // leap years starting Wednesday have 53 weeks
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // now we simply add the number of days elapsed since the start of the year, and % 7
 | 
				
			||||||
 | 
					    w += ndays;
 | 
				
			||||||
 | 
					    d = div(w, 7); // w can never be 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // do Sunday correction
 | 
				
			||||||
 | 
					    if(!d.rem) {
 | 
				
			||||||
 | 
					        d.rem = 7;
 | 
				
			||||||
 | 
					        --d.quot;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *wday = d.rem;
 | 
				
			||||||
 | 
					    if(d.quot) {
 | 
				
			||||||
 | 
					        if(d.quot == 53 && !has53) {
 | 
				
			||||||
 | 
					            d.quot = 1;
 | 
				
			||||||
 | 
					            *year += 1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        *week = d.quot;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        *year -= 1;
 | 
				
			||||||
 | 
					        switch(_weekday_of_year(*year)) {
 | 
				
			||||||
 | 
					        case 3:
 | 
				
			||||||
 | 
					            *week = iso8601_isleap(*year) ? 53 : 52;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case 4:
 | 
				
			||||||
 | 
					            *week = 53;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            *week = 52;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int _from_year(struct iso8601_date* date, int year)
 | 
					int _from_year(struct iso8601_date* date, int year)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    div_t qr;
 | 
					    div_t qr;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,8 @@ void iso8601_to_time_t(time_t* t, const struct iso8601_date* date);
 | 
				
			||||||
int iso8601_isleap(int year);
 | 
					int iso8601_isleap(int year);
 | 
				
			||||||
void iso8601_to_cal(int* year, int* month, int* day, const struct iso8601_date* date);
 | 
					void iso8601_to_cal(int* year, int* month, int* day, const struct iso8601_date* date);
 | 
				
			||||||
int iso8601_from_cal(struct iso8601_date* date, int year, int month, int day);
 | 
					int iso8601_from_cal(struct iso8601_date* date, int year, int month, int day);
 | 
				
			||||||
 | 
					void iso8601_to_ord(int* year, int* oday, const struct iso8601_date* date);
 | 
				
			||||||
 | 
					void iso8601_to_week(int* year, int* week, int* wday, const struct iso8601_date* date);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* print.c */
 | 
					/* print.c */
 | 
				
			||||||
void iso8601_print(char* str, int amt, const struct iso8601_date* date, 
 | 
					void iso8601_print(char* str, int amt, const struct iso8601_date* date, 
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ static const struct iso8601_details _default_details = {
 | 
				
			||||||
void iso8601_print(char* str, int amt, const struct iso8601_date* date, 
 | 
					void iso8601_print(char* str, int amt, const struct iso8601_date* date, 
 | 
				
			||||||
    const struct iso8601_details* details)
 | 
					    const struct iso8601_details* details)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int y, m, d, ret, extended;
 | 
					    int y, m, d, ret = 0, extended;
 | 
				
			||||||
    struct iso8601_date dttz;
 | 
					    struct iso8601_date dttz;
 | 
				
			||||||
    double frac;
 | 
					    double frac;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,13 +41,11 @@ void iso8601_print(char* str, int amt, const struct iso8601_date* date,
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case iso8601_prec_month:
 | 
					    case iso8601_prec_month:
 | 
				
			||||||
        iso8601_to_cal(&y, &m, &d, &dttz);
 | 
					 | 
				
			||||||
        if(y < 0) snprintf(str, amt, "%05d-%02d", y, m);
 | 
					        if(y < 0) snprintf(str, amt, "%05d-%02d", y, m);
 | 
				
			||||||
        else snprintf(str, amt, extended ? "%04d-%02d" : "%04d%02d", y, m);
 | 
					        else snprintf(str, amt, extended ? "%04d-%02d" : "%04d%02d", y, m);
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case iso8601_prec_day:
 | 
					    case iso8601_prec_day:
 | 
				
			||||||
        iso8601_to_cal(&y, &m, &d, &dttz);
 | 
					 | 
				
			||||||
        if(y < 0) ret = snprintf(str, amt, "%05d-%02d-%02d", y, m, d);
 | 
					        if(y < 0) ret = snprintf(str, amt, "%05d-%02d-%02d", y, m, d);
 | 
				
			||||||
        else ret = snprintf(str, amt, extended ? "%04d-%02d-%02d" : "%04d%02d%02d", y, m, d);
 | 
					        else ret = snprintf(str, amt, extended ? "%04d-%02d-%02d" : "%04d%02d%02d", y, m, d);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
| 
						 | 
					@ -58,9 +56,19 @@ void iso8601_print(char* str, int amt, const struct iso8601_date* date,
 | 
				
			||||||
        else ret = snprintf(str, amt, extended ? "%04d-%03d" : "%04d%03d", y, d);
 | 
					        else ret = snprintf(str, amt, extended ? "%04d-%03d" : "%04d%03d", y, d);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    default:
 | 
					    case iso8601_prec_week:
 | 
				
			||||||
        fprintf(stderr, "<<< NOT IMPLEMENTED >>>\n");
 | 
					        iso8601_to_week(&y, &m, &d, &dttz);
 | 
				
			||||||
        abort();
 | 
					        extended = y < 0 || y > 9999 || details->extended; // ISO year is different
 | 
				
			||||||
 | 
					        if(y < 0) snprintf(str, amt, "%05d-W%02d", y, m);
 | 
				
			||||||
 | 
					        else snprintf(str, amt, extended ? "%04d-W%02d" : "%04dW%02d", y, m);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case iso8601_prec_wday:
 | 
				
			||||||
 | 
					        iso8601_to_week(&y, &m, &d, &dttz);
 | 
				
			||||||
 | 
					        extended = y < 0 || y > 9999 || details->extended; // ISO year is different
 | 
				
			||||||
 | 
					        if(y < 0) ret = snprintf(str, amt, "%05d-W%02d-%d", y, m, d);
 | 
				
			||||||
 | 
					        else ret = snprintf(str, amt, extended ? "%04d-W%02d-%d" : "%04dW%02d%d", y, m, d);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if(ret < 1 || ret >= amt) return;
 | 
					    if(ret < 1 || ret >= amt) return;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -123,6 +123,43 @@ int try_neg_year(int year)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int try_weeks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // starting position
 | 
				
			||||||
 | 
					    int weekcount = 52, weekday = 5, isoyear = -1;
 | 
				
			||||||
 | 
					    int y, wk, wd, ret = 0;
 | 
				
			||||||
 | 
					    struct iso8601_date dt;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for(dt.day = 0; dt.day < MAX_YEAR_COUNT * 366; ++dt.day) {
 | 
				
			||||||
 | 
					        iso8601_to_week(&y, &wk, &wd, &dt);
 | 
				
			||||||
 | 
					        if(wd == 1) {
 | 
				
			||||||
 | 
					            if(weekday != 7) goto discont;
 | 
				
			||||||
 | 
					            ++weekcount;
 | 
				
			||||||
 | 
					            if(wk == 1) {
 | 
				
			||||||
 | 
					                if(weekcount != 53 && weekcount != 54) goto discont;
 | 
				
			||||||
 | 
					                weekcount = 1;
 | 
				
			||||||
 | 
					                ++isoyear;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if(wk != weekcount || y != isoyear) goto discont;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        weekday = wd;
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    discont:
 | 
				
			||||||
 | 
					        printf("[weekdiscont     ] %04d-W%02d-%d ==> %04d-W%02d-%d (%d)\n",
 | 
				
			||||||
 | 
					            isoyear, weekcount, weekday, y, wk, wd, dt.day);
 | 
				
			||||||
 | 
					        isoyear = y;
 | 
				
			||||||
 | 
					        weekcount = wk;
 | 
				
			||||||
 | 
					        weekday = wd;
 | 
				
			||||||
 | 
					        if(++ret > 20) return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char* argv[])
 | 
					int main(int argc, char* argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int ret = 0, year;
 | 
					    int ret = 0, year;
 | 
				
			||||||
| 
						 | 
					@ -142,6 +179,9 @@ int main(int argc, char* argv[])
 | 
				
			||||||
        if(ret > 20) return ret;
 | 
					        if(ret > 20) return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret += try_weeks();
 | 
				
			||||||
 | 
					    if(ret > 20) return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue