Improve parser's dealing of midnight etc.

This commit is contained in:
Laurence Withers 2006-11-25 01:17:09 +00:00
parent 6a55a63695
commit 994a28d35b
1 changed files with 26 additions and 40 deletions

View File

@ -34,7 +34,7 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
div_t qr; div_t qr;
char ch; char ch;
int num = 0, neg = 0, dig = 0, tz_neg = 0, nsec = -1, nsec_dig = -1; int num = 0, neg = 0, dig = 0, tz_neg = 0, nsec = -1, nsec_dig = -1, leap_sec_req = 0;
int y = 0, m = -1, d = -1, w = -1, wd = -1, 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; double frac;
@ -47,14 +47,12 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
num *= 10; \ num *= 10; \
num += ch - '0'; \ num += ch - '0'; \
if(num > 1000000000) { \ if(num > 1000000000) { \
errno = EDOM; \
return -1; \ return -1; \
} \ } \
}while(0) }while(0)
#define ERROR_IF(x) do { \ #define ERROR_IF(x) do { \
if(x) { \ if(x) { \
errno = EILSEQ; \
return -1; \ return -1; \
} \ } \
}while(0) }while(0)
@ -77,7 +75,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
break; break;
default: default:
errno = EILSEQ;
return -1; return -1;
} }
break; break;
@ -127,7 +124,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
break; break;
default: default:
errno = EILSEQ;
return -1; return -1;
} }
if(!ch) goto done; if(!ch) goto done;
@ -162,7 +158,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
case 'W': case 'W':
if(dig) { if(dig) {
errno = EILSEQ;
return -1; return -1;
} }
state = state_week_extended; state = state_week_extended;
@ -173,7 +168,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
case 2: m = num; goto done; case 2: m = num; goto done;
case 3: d = num; goto done; case 3: d = num; goto done;
} }
errno = EILSEQ;
return -1; return -1;
case 'T': case 'T':
@ -185,7 +179,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
break; break;
default: default:
errno = EILSEQ;
return -1; return -1;
} }
break; break;
@ -208,7 +201,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
break; break;
default: default:
errno = EILSEQ;
return -1; return -1;
} }
break; break;
@ -232,7 +224,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
goto done; goto done;
} }
errno = EILSEQ;
return -1; return -1;
case 'T': case 'T':
@ -247,7 +238,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
break; break;
default: default:
errno = EILSEQ;
return -1; return -1;
} }
break; break;
@ -311,7 +301,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
break; break;
default: default:
errno = EILSEQ;
return -1; return -1;
} }
@ -324,7 +313,7 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
case 'Z': state = state_tz_utc; break; case 'Z': state = state_tz_utc; break;
case '+': state = state_tz_basic; break; case '+': state = state_tz_basic; break;
case '-': tz_neg = 1; state = state_tz_basic; break; case '-': tz_neg = 1; state = state_tz_basic; break;
default: errno = EILSEQ; return -1; default: return -1;
} }
break; break;
@ -346,7 +335,7 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
case 'Z': state = state_tz_utc; break; case 'Z': state = state_tz_utc; break;
case '+': state = state_tz_hour; break; case '+': state = state_tz_hour; break;
case '-': tz_neg = 1; state = state_tz_hour; break; case '-': tz_neg = 1; state = state_tz_hour; break;
default: errno = EILSEQ; return -1; default: return -1;
} }
break; break;
@ -368,7 +357,7 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
case 'Z': state = state_tz_utc; break; case 'Z': state = state_tz_utc; break;
case '+': state = state_tz_hour; break; case '+': state = state_tz_hour; break;
case '-': tz_neg = 1; state = state_tz_hour; break; case '-': tz_neg = 1; state = state_tz_hour; break;
default: errno = EILSEQ; return -1; default: return -1;
} }
break; break;
@ -389,7 +378,7 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
case 'Z': state = state_tz_utc; break; case 'Z': state = state_tz_utc; break;
case '+': state = state_tz_hour; break; case '+': state = state_tz_hour; break;
case '-': tz_neg = 1; state = state_tz_hour; break; case '-': tz_neg = 1; state = state_tz_hour; break;
default: errno = EILSEQ; return -1; default: return -1;
} }
break; break;
@ -450,13 +439,11 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
break; break;
default: default:
errno = EILSEQ;
return -1; return -1;
} }
goto done; goto done;
default: default:
errno = EILSEQ;
return -1; return -1;
} }
break; break;
@ -479,7 +466,7 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
switch(ch) { switch(ch) {
case ':': state = state_tz_min; break; case ':': state = state_tz_min; break;
case 0: goto done; case 0: goto done;
default: errno = EILSEQ; return -1; default: return -1;
} }
break; break;
@ -497,7 +484,7 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
switch(ch) { switch(ch) {
case ':': state = state_tz_sec; break; case ':': state = state_tz_sec; break;
case 0: goto done; case 0: goto done;
default: errno = EILSEQ; return -1; default: return -1;
} }
break; break;
@ -515,9 +502,6 @@ int iso8601_parse(const char* str, struct iso8601_date* earliest, struct iso8601
} }
} }
#undef ERROR_IF
#undef INCNUM
done: done:
if(neg) y *= -1; if(neg) y *= -1;
if(tz_neg) tz_sec *= -1; if(tz_neg) tz_sec *= -1;
@ -568,6 +552,11 @@ done:
if(details) details->time_prec = iso8601_prec_none; if(details) details->time_prec = iso8601_prec_none;
} else if(sec != -1) { } else if(sec != -1) {
if(sec == 60) {
leap_sec_req++;
sec = 59;
}
if(earliest && iso8601_from_clocktime(earliest, hour, min, sec)) return -1; if(earliest && iso8601_from_clocktime(earliest, hour, min, sec)) return -1;
if(latest && iso8601_from_clocktime(latest, hour, min, sec)) return -1; if(latest && iso8601_from_clocktime(latest, hour, min, sec)) return -1;
@ -625,28 +614,25 @@ done:
} }
// correct for timezone offset (note trickery to end up at 23:59:60 iff a leap second was
// requested)
if(details) details->tz_sec = tz_sec; if(details) details->tz_sec = tz_sec;
if(tz_sec && earliest) iso8601_add_seconds(earliest, -tz_sec); if(tz_sec && earliest) iso8601_add_seconds(earliest, - leap_sec_req - tz_sec);
if(tz_sec && latest) iso8601_add_seconds(latest, -tz_sec); if(tz_sec && latest) iso8601_add_seconds(latest, - leap_sec_req - tz_sec);
return 0; // check that, if a leap second was requested, it was a valid leap second
if(leap_sec_req) {
ERROR_IF(earliest && earliest->sec != 86400);
ERROR_IF(latest && latest->sec != 86400);
} }
// we can't have e.g. 240000.5
ERROR_IF(earliest && earliest->sec == 86401 && earliest->nsec);
int iso8601_valid(const struct iso8601_date* date) ERROR_IF(latest && latest->sec == 86401 && latest->nsec);
{
if(date->nsec < 0 || date->nsec > 1000000000) return 0;
switch(date->sec) {
case 0 ... 86399:
return 1;
case 86401: /* 24:00:00 */
return !date->nsec;
}
return 0; return 0;
#undef ERROR_IF
#undef INCNUM
} }