Add from weekdate functions.

This commit is contained in:
Laurence Withers 2006-10-15 12:10:16 +01:00
parent 19d55ad54c
commit 106f60b5c0
4 changed files with 148 additions and 7 deletions

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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;
} }