Further WIP on parser

This commit is contained in:
Laurence Withers 2006-10-16 00:23:06 +01:00
parent 02d47dbdf2
commit 09fd37b4f5
5 changed files with 215 additions and 14 deletions

View File

@ -71,6 +71,21 @@ static const struct monthcount _days_in_month_leap[] = {
static int _days_in_month(int year, int month)
{
const struct monthcount* mc;
if(month < 1 || month > 12) {
errno = EDOM;
return -1;
}
mc = iso8601_isleap(year) ? _days_in_month_leap : _days_in_month_common;
return mc[month - 1].days;
}
static void _to_year(int* year, int* days_left, const struct iso8601_date* date)
{
div_t qr;
@ -278,7 +293,7 @@ int iso8601_from_cal(struct iso8601_date* date, int year, int month, int day)
// check for domain errors
mc = iso8601_isleap(year) ? _days_in_month_leap : _days_in_month_common;
if(month < 1 || month > 12 || day < 0 || day > mc[month - 1].days) {
if(month < 1 || month > 12 || day < 1 || day > mc[month - 1].days) {
errno = EDOM;
return -1;
}
@ -318,14 +333,22 @@ 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;
int day1off, maxwk;
// compute year part
_from_year(date, isoyear);
// 400-year cycle; ensure we're between 0-400 years
isoyear %= 400;
if(isoyear < 0) isoyear += 400;
// domain check
maxwk = (((isoyear % 7) == 52) || ((isoyear % 28) == 24)) ? 53 : 52;
if(wday < 1 || wday > 7 || week < 1 || week > maxwk) {
errno = EDOM;
return -1;
}
/* 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
@ -346,6 +369,60 @@ int iso8601_from_week(struct iso8601_date* date, int isoyear, int week, int wday
void iso8601_to_clocktime(int* hour, int* min, int* sec, const struct iso8601_date* date)
{
div_t qr;
// two special cases: leap second and midnight
switch(date->sec) {
case 86400:
*hour = 23;
*min = 59;
*sec = 60;
return;
case 86401:
*hour = 24;
*min = 0;
*sec = 0;
return;
}
// normal case
qr = div(date->sec, 3600);
*hour = qr.quot;
qr = div(qr.rem, 60);
*min = qr.quot;
*sec = qr.rem;
}
int iso8601_from_clocktime(struct iso8601_date* date, int hour, int min, int sec)
{
// two special cases: leap second and midnight
if(hour == 23 && min == 59 && sec == 60) {
date->sec = 86400;
return 0;
}
if(hour == 24 && !min && !sec) {
date->sec = 86401;
return 0;
}
// domain check
if(hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 59) {
errno = EDOM;
return -1;
}
// normal calculation
date->sec = hour * 3600 + min * 60 + sec;
return 0;
}
/* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
vim: expandtab:ts=4:sw=4

View File

@ -24,6 +24,8 @@ void iso8601_to_ord(int* year, int* oday, const struct iso8601_date* date);
int iso8601_from_ord(struct iso8601_date* date, int year, int oday);
void iso8601_to_week(int* year, int* week, int* wday, const struct iso8601_date* date);
int iso8601_from_week(struct iso8601_date* date, int year, int week, int wday);
void iso8601_to_clocktime(int* hour, int* min, int* sec, const struct iso8601_date* date);
int iso8601_from_clocktime(struct iso8601_date* date, int hour, int min, int sec);
/* print.c */
void iso8601_print(char* str, int amt, const struct iso8601_date* date,

View File

@ -35,7 +35,8 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
div_t qr;
char ch;
int num = 0, neg = 0, dig = 0, tz_neg = 0, nsec = -1, nsec_dig = -1;
int y = 0, m = 0, d = 0, w = 0, wd = 0, hour = -1, min = -1, sec = -1, tz_sec = 0;
int y = 0, m = -1, d = -1, w = -1, wd = -1, hour = -1, min = -1, sec = -1, tz_sec = 0;
double frac;
if(earliest) memset(earliest, 0, sizeof(struct iso8601_date));
if(latest) memset(latest, 0, sizeof(struct iso8601_date));
@ -520,7 +521,114 @@ done:
if(neg) y *= -1;
if(tz_neg) tz_sec *= -1;
// TODO
if(m != -1) {
if(d == -1) {
if(earliest && iso8601_from_cal(earliest, y, m, 1)) return -1;
if(latest && iso8601_from_cal(latest, y, m, _days_in_month(y, m))) return -1;
if(details) details->date_prec = iso8601_prec_month;
} else {
if(earliest && iso8601_from_cal(earliest, y, m, d)) return -1;
if(latest && iso8601_from_cal(latest, y, m, d)) return -1;
if(details) details->date_prec = iso8601_prec_day;
}
} else if(d != -1) {
if(earliest && iso8601_from_ord(earliest, y, d)) return -1;
if(latest && iso8601_from_ord(latest, y, d)) return -1;
if(details) details->date_prec = iso8601_prec_ord;
} else if(w != -1) {
if(wd == -1) {
if(earliest && iso8601_from_week(earliest, y, w, 1)) return -1;
if(latest && iso8601_from_week(latest, y, w, 7)) return -1;
if(details) details->date_prec = iso8601_prec_week;
} else {
if(earliest && iso8601_from_week(earliest, y, w, wd)) return -1;
if(latest && iso8601_from_week(latest, y, w, wd)) return -1;
if(details) details->date_prec = iso8601_prec_wday;
}
} else {
if(earliest && iso8601_from_cal(earliest, y, 1, 1)) return -1;
if(latest && iso8601_from_cal(latest, y, 12, 31)) return -1;
if(details) details->date_prec = iso8601_prec_year;
}
if(nsec_dig != -1) while(nsec_dig++ < 9) nsec *= 10;
if(hour == -1) {
if(earliest && iso8601_from_clocktime(earliest, 0, 0, 0)) return -1;
if(latest && iso8601_from_clocktime(latest, 24, 0, 0)) return -1;
if(details) details->time_prec = iso8601_prec_none;
} else if(sec != -1) {
if(earliest && iso8601_from_clocktime(earliest, hour, min, sec)) return -1;
if(latest && iso8601_from_clocktime(latest, hour, min, sec)) return -1;
if(nsec_dig == -1) {
if(latest) latest->nsec = 999999999;
if(details) details->time_prec = iso8601_prec_sec;
} else {
if(earliest) earliest->nsec = nsec;
if(latest) latest->nsec = nsec;
if(details) details->time_prec = iso8601_prec_secfrac;
}
} else if(min != -1) {
if(nsec_dig == -1) {
if(earliest && iso8601_from_clocktime(earliest, hour, min, 0)) return -1;
if(latest && iso8601_from_clocktime(latest, hour, min, 59)) return -1;
if(latest) latest->nsec = 999999999;
if(details) details->time_prec = iso8601_prec_min;
} else {
frac = nsec * 60.0 / 1e9;
sec = (int)frac;
nsec = (frac - sec) * 1e9;
if(earliest && iso8601_from_clocktime(earliest, hour, min, sec)) return -1;
if(earliest) earliest->nsec = nsec;
if(latest && iso8601_from_clocktime(latest, hour, min, sec)) return -1;
if(latest) latest->nsec = nsec;
if(details) details->time_prec = iso8601_prec_minfrac;
}
} else {
if(nsec_dig == -1) {
if(earliest && iso8601_from_clocktime(earliest, hour, 0, 0)) return -1;
if(latest && iso8601_from_clocktime(latest, hour, 59, 59)) return -1;
if(latest) latest->nsec = 999999999;
if(details) details->time_prec = iso8601_prec_hour;
} else {
frac = nsec * 60.0 / 1e9;
min = (int)frac;
frac -= min;
frac *= 60;
sec = (int)frac;
nsec = (frac - sec) * 1e9;
if(earliest && iso8601_from_clocktime(earliest, hour, min, sec)) return -1;
if(earliest) earliest->nsec = nsec;
if(latest && iso8601_from_clocktime(latest, hour, min, sec)) return -1;
if(latest) latest->nsec = nsec;
if(details) details->time_prec = iso8601_prec_hourfrac;
}
}
if(details) details->tz_sec = tz_sec;
if(earliest) iso8601_add_seconds(earliest, -tz_sec);
if(latest) iso8601_add_seconds(latest, -tz_sec);
return 0;
}

View File

@ -114,17 +114,17 @@ void iso8601_print(char* str, int amt, const struct iso8601_date* date,
case iso8601_prec_hourfrac:
frac = y + m / 60.0 + (d + date->nsec / 1e9) / 3600.0;
ret = snprintf(str, amt, "T%#09.6f", frac);
ret = snprintf(str, amt, "T%#012.9f", frac);
break;
case iso8601_prec_minfrac:
frac = m + (d + date->nsec / 1e9) / 60.0;
ret = snprintf(str, amt, extended ? "T%02d:%#09.6f" : "T%02d%#09.6f", y, frac);
ret = snprintf(str, amt, extended ? "T%02d:%#012.9f" : "T%02d%#012.9f", y, frac);
break;
case iso8601_prec_secfrac:
frac = d + date->nsec / 1e9;
ret = snprintf(str, amt, extended ? "T%02d:%02d:%#09.6f" : "T%02d%02d%#09.6f", y, m, frac);
ret = snprintf(str, amt, extended ? "T%02d:%02d:%#012.9f" : "T%02d%02d%#012.9f", y, m, frac);
break;
}

View File

@ -15,25 +15,39 @@
int do_date(const char* str)
{
struct iso8601_date earliest, latest;
struct iso8601_details details;
char buf[100], buf2[100];
struct iso8601_details details, fulldetails;
char buf[100], buf2[100], buf3[100], buf4[100];
if(iso8601_parse(str, &earliest, &latest, &details)) {
perror("iso8601_parse");
return 1;
}
memset(&fulldetails, 0, sizeof(fulldetails));
switch((fulldetails.date_prec = details.date_prec)) {
case iso8601_prec_month:
case iso8601_prec_year:
fulldetails.date_prec = iso8601_prec_day;
break;
case iso8601_prec_week:
fulldetails.date_prec = iso8601_prec_wday;
break;
}
fulldetails.time_prec = iso8601_prec_secfrac;
iso8601_print(buf, sizeof(buf), &earliest, &details);
iso8601_print(buf2, sizeof(buf2), &latest, &details);
iso8601_print(buf2, sizeof(buf2), &earliest, &fulldetails);
iso8601_print(buf3, sizeof(buf3), &latest, &details);
iso8601_print(buf4, sizeof(buf4), &latest, &fulldetails);
printf("Results for ``%s'':\n"
" Earliest: day=%d, sec=%05d, nsec=%09d; %s\n"
" Latest : day=%d, sec=%05d, nsec=%09d; %s\n"
" Earliest: day=%d, sec=%05d, nsec=%09d; %-20s %s\n"
" Latest : day=%d, sec=%05d, nsec=%09d; %-20s %s\n"
" Details : date_prec=%d, time_prec=%d, extended=%d, tz_sec=%d\n"
"\n",
str,
earliest.day, earliest.sec, earliest.nsec, buf,
latest.day, latest.sec, latest.nsec, buf2,
earliest.day, earliest.sec, earliest.nsec, buf, buf2,
latest.day, latest.sec, latest.nsec, buf3, buf4,
details.date_prec, details.time_prec, details.extended, details.tz_sec);
return 0;