From 09fd37b4f5e928d7b221771c5e9c8948c4d77525 Mon Sep 17 00:00:00 2001 From: Laurence Withers Date: Mon, 16 Oct 2006 00:23:06 +0100 Subject: [PATCH] Further WIP on parser --- src/libiso8601/calc.c | 81 ++++++++++++++++++++++++++- src/libiso8601/functions.h | 2 + src/libiso8601/parser.c | 112 ++++++++++++++++++++++++++++++++++++- src/libiso8601/print.c | 6 +- src/tests/parser.c | 28 +++++++--- 5 files changed, 215 insertions(+), 14 deletions(-) diff --git a/src/libiso8601/calc.c b/src/libiso8601/calc.c index 88a1796..8e22a2f 100644 --- a/src/libiso8601/calc.c +++ b/src/libiso8601/calc.c @@ -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 diff --git a/src/libiso8601/functions.h b/src/libiso8601/functions.h index 950557c..c49872f 100644 --- a/src/libiso8601/functions.h +++ b/src/libiso8601/functions.h @@ -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, diff --git a/src/libiso8601/parser.c b/src/libiso8601/parser.c index 2d35fe4..ba668b3 100644 --- a/src/libiso8601/parser.c +++ b/src/libiso8601/parser.c @@ -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; } diff --git a/src/libiso8601/print.c b/src/libiso8601/print.c index 1c08aac..4a35784 100644 --- a/src/libiso8601/print.c +++ b/src/libiso8601/print.c @@ -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; } diff --git a/src/tests/parser.c b/src/tests/parser.c index 1489e35..3944689 100644 --- a/src/tests/parser.c +++ b/src/tests/parser.c @@ -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;