ref: 9eb0a65fcf0c4f16d12f0af62389648651397400
parent: e4cafa6d265d4ff76ed3e34db41a03a9dfc03866
author: Tor Andersson <tor@ccxvii.net>
date: Mon Mar 3 18:47:57 EST 2014
Improve date parsing. Handle partial year specifications, as in the spec. Date part: YYYY YYYY-MM YYYY-MM-DD Optional time part: THH:mm THH:mm:ss THH:mm:ss.sss Optional timezone part: Z +HH:mm -HH:mm
--- a/jsdate.c
+++ b/jsdate.c
@@ -230,85 +230,72 @@
return t < 0 ? -floor(-t) : floor(t);
}
-static int toint(const char *s, int w, int *v)
+static int toint(const char **sp, int w, int *v)
{
char *e;
- *v = strtol(s, &e, 10);
- return e == s + w;
+ *v = strtol(*sp, &e, 10);
+ if (e != *sp + w)
+ return 0;
+ *sp += w;
+ return 1;
}
-static double parseDate(const char *s)
+static double parseDateTime(const char *s)
{
- char *e;
int y = 1970, m = 1, d = 1, H = 0, M = 0, S = 0, ms = 0;
- int tzs = 0, tzm = 0, tzh = 0;
+ int tza = 0;
double t;
/* Parse ISO 8601 formatted date and time: */
- /* YYYY("-"mm("-"dd("T"HH":"MM(":"SS("."lll)?)?("Z"|[+-]zz(":"?zz)?)?)?)?)? */
+ /* YYYY("-"MM("-"DD)?)?("T"HH":"mm(":"ss("."sss)?)?("Z"|[+-]HH(":"mm)?)?)? */
- if (!toint(s+0, 4, &y))
- return NAN;
- if (s[4] == '-') {
- if (!toint(s+5, 2, &m))
- return NAN;
- if (s[7] == '-') {
- if (!toint(s+8, 2, &d))
- return NAN;
- if (s[10] == 'T') {
- if (!toint(s+11, 2, &H))
- return NAN;
- if (s[13] != ':')
- return NAN;
- if (!toint(s+14, 2, &M))
- return NAN;
- if (s[16] == ':') {
- if (!toint(s + 17, 2, &S))
- return NAN;
- if (s[19] == '.') {
- if (!toint(s + 20, 3, &ms))
- return NAN;
- s = s + 23;
- } else {
- s = s + 19;
- }
- } else {
- s = s + 16;
- }
- if (s[0] == 'Z') {
- tzs = 1;
- tzh = tzm = 0;
- if (s[1])
- return NAN;
- } else if (s[0] == '+' || s[0] == '-') {
- tzs = s[0] == '+' ? 1 : -1;
- tzh = strtol(s + 1, &e, 10);
- if (e == s + 3) {
- if (s[3] == ':') {
- if (!toint(s + 4, 2, &tzm))
- return NAN;
- if (s[6])
- return NAN;
- } else if (s[3]) return NAN;
- } else if (e == s + 5) {
- tzm = tzh % 100;
- tzh = tzh / 100;
- if (s[5])
- return NAN;
- } else {
- return NAN;
- }
- } else if (s[0]) {
- return NAN;
- }
- } else if (s[10]) return NAN;
- } else if (s[7]) return NAN;
- } else if (s[4]) return NAN;
+ if (!toint(&s, 4, &y)) return NAN;
+ if (*s == '-') {
+ s += 1;
+ if (!toint(&s, 2, &m)) return NAN;
+ if (*s == '-') {
+ s += 1;
+ if (!toint(&s, 2, &d)) return NAN;
+ }
+ }
- t = MakeDate(MakeDay(y, m-1, d-1), MakeTime(H, M, S, ms));
- if (tzs)
- return t - tzs * (tzh * msPerHour + tzm + msPerDay);
- return UTC(t);
+ if (*s == 'T') {
+ s += 1;
+ if (!toint(&s, 2, &H)) return NAN;
+ if (*s != ':') return NAN;
+ s += 1;
+ if (!toint(&s, 2, &M)) return NAN;
+ if (*s == ':') {
+ s += 1;
+ if (!toint(&s, 2, &S)) return NAN;
+ if (*s == '.') {
+ s += 1;
+ if (!toint(&s, 3, &ms)) return NAN;
+ }
+ }
+ if (*s == 'Z') {
+ s += 1;
+ tza = 0;
+ } else if (*s == '+' || *s == '-') {
+ int tzh = 0, tzm = 0;
+ int tzs = *s == '+' ? 1 : -1;
+ s += 1;
+ if (!toint(&s, 2, &tzh)) return NAN;
+ if (*s == ':') {
+ s += 1;
+ if (!toint(&s, 2, &tzm)) return NAN;
+ }
+ tza = tzs * (tzh * msPerHour + tzm * msPerMinute);
+ } else {
+ tza = LocalTZA();
+ }
+ }
+
+ if (*s) return NAN;
+
+ // TODO: DaylightSavingTA on local times
+ t = MakeDate(MakeDay(y, m-1, d), MakeTime(H, M, S, ms));
+ return t - tza;
}
/* date formatting */
@@ -333,9 +320,9 @@
if (tza == 0)
sprintf(buf, "%02d:%02d:%02d.%03dZ", H, M, S, ms);
else if (tza < 0)
- sprintf(buf, "%02d:%02d:%02d.%03d-%02d%02d", H, M, S, ms, tzh, tzm);
+ sprintf(buf, "%02d:%02d:%02d.%03d-%02d:%02d", H, M, S, ms, tzh, tzm);
else
- sprintf(buf, "%02d:%02d:%02d.%03d+%02d%02d", H, M, S, ms, tzh, tzm);
+ sprintf(buf, "%02d:%02d:%02d.%03d+%02d:%02d", H, M, S, ms, tzh, tzm);
return buf;
}
@@ -369,7 +356,7 @@
static void D_parse(js_State *J, unsigned int argc)
{
- double t = parseDate(js_tostring(J, 1));
+ double t = parseDateTime(js_tostring(J, 1));
js_pushnumber(J, t);
}
@@ -411,7 +398,7 @@
else if (argc == 1) {
v = js_toprimitive(J, 1, JS_HNONE);
if (v.type == JS_TSTRING)
- t = parseDate(v.u.string);
+ t = parseDateTime(v.u.string);
else
t = TimeClip(jsV_tonumber(J, &v));
} else {