Improve Date.parse (#289)

* Improve `Date.parse()`

- rewrite `Date.parse()` with separate parsers
- return `NaN` for out of bounds field values as specified
- add `js_tzabbr` and `string_get_tzabbr` to handle timezone abbreviations
- improve `string_get_milliseconds` readability
- accept up to 9 decimals for millisecond fraction but truncate at 3
- accept many more alternative date/time formats
- add test cases in **tests/test_builtin.js**
- produce readable output for `Date` objects in repl 
- use `JSON.stringify` to output `Date` and `string` values in **repl.js**
- remove `String.prototype.__quote`
- add `minimum_length` macro to specify argument array sizes (C99 except MSVC)
- v8.js: parse all environment variables and output them, update **v8.txt**
This commit is contained in:
Charlie Gordon 2024-03-10 10:34:26 +01:00 committed by GitHub
parent c61336c4fc
commit 648a8f5be1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 12129 additions and 11956 deletions

View file

@ -108,6 +108,12 @@ static void *__builtin_frame_address(unsigned int level) {
#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) #define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member)))
#endif #endif
#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define minimum_length(n) static n
#else
#define minimum_length(n) n
#endif
typedef int BOOL; typedef int BOOL;
#ifndef FALSE #ifndef FALSE

23223
gen/repl.c

File diff suppressed because it is too large Load diff

635
quickjs.c
View file

@ -40114,12 +40114,6 @@ static JSValue js_string_trim(JSContext *ctx, JSValue this_val,
return ret; return ret;
} }
static JSValue js_string___quote(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv)
{
return JS_ToQuotedString(ctx, this_val);
}
/* return 0 if before the first char */ /* return 0 if before the first char */
static int string_prevc(JSString *p, int *pidx) static int string_prevc(JSString *p, int *pidx)
{ {
@ -40530,7 +40524,6 @@ static const JSCFunctionListEntry js_string_proto_funcs[] = {
JS_ALIAS_DEF("trimLeft", "trimStart" ), JS_ALIAS_DEF("trimLeft", "trimStart" ),
JS_CFUNC_DEF("toString", 0, js_string_toString ), JS_CFUNC_DEF("toString", 0, js_string_toString ),
JS_CFUNC_DEF("valueOf", 0, js_string_toString ), JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
JS_CFUNC_DEF("__quote", 1, js_string___quote ),
JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ), JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
JS_CFUNC_DEF("normalize", 0, js_string_normalize ), JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ), JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
@ -40867,7 +40860,7 @@ static int getTimezoneOffset(int64_t time) {
/* disable DST adjustment on the local tm struct */ /* disable DST adjustment on the local tm struct */
tm.tm_isdst = 0; tm.tm_isdst = 0;
return difftime(mktime(&gmt), mktime(&tm)) / 60; return (int)difftime(mktime(&gmt), mktime(&tm)) / 60;
#else #else
return -tm.tm_gmtoff / 60; return -tm.tm_gmtoff / 60;
#endif /* NO_TM_GMTOFF */ #endif /* NO_TM_GMTOFF */
@ -46875,7 +46868,8 @@ static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
static char const day_names[] = "SunMonTueWedThuFriSat"; static char const day_names[] = "SunMonTueWedThuFriSat";
static __exception int get_date_fields(JSContext *ctx, JSValue obj, static __exception int get_date_fields(JSContext *ctx, JSValue obj,
double fields[9], int is_local, int force) double fields[minimum_length(9)],
int is_local, int force)
{ {
double dval; double dval;
int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
@ -46936,7 +46930,7 @@ static double time_clip(double t) {
/* The spec mandates the use of 'double' and it specifies the order /* The spec mandates the use of 'double' and it specifies the order
of the operations */ of the operations */
static double set_date_fields(double fields[], int is_local) { static double set_date_fields(double fields[minimum_length(7)], int is_local) {
double y, m, dt, ym, mn, day, h, s, milli, time, tv; double y, m, dt, ym, mn, day, h, s, milli, time, tv;
int yi, mi, i; int yi, mi, i;
int64_t days; int64_t days;
@ -47259,145 +47253,418 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValue this_val,
return js_float64(set_date_fields(fields, 0)); return js_float64(set_date_fields(fields, 0));
} }
static void string_skip_spaces(JSString *sp, int *pp) { /* Date string parsing */
while (*pp < sp->len && string_get(sp, *pp) == ' ')
static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) {
if (sp[*pp] == c) {
*pp += 1; *pp += 1;
return TRUE;
} else {
return FALSE;
}
} }
static void string_skip_non_spaces(JSString *sp, int *pp) { /* skip spaces, update offset, return next char */
while (*pp < sp->len && string_get(sp, *pp) != ' ') static int string_skip_spaces(const uint8_t *sp, int *pp) {
int c;
while ((c = sp[*pp]) == ' ')
*pp += 1; *pp += 1;
return c;
} }
/* parse a numeric field with an optional sign if accept_sign is TRUE */ /* skip dashes dots and commas */
static int string_get_digits(JSString *sp, int *pp, int64_t *pval) { static int string_skip_separators(const uint8_t *sp, int *pp) {
int64_t v = 0; int c;
while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',')
*pp += 1;
return c;
}
/* skip a word, stop on spaces, digits and separators, update offset */
static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
int c;
while (!strchr(stoplist, c = sp[*pp]))
*pp += 1;
return c;
}
/* parse a numeric field (max_digits = 0 -> no maximum) */
static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
int min_digits, int max_digits)
{
int v = 0;
int c, p = *pp, p_start; int c, p = *pp, p_start;
if (p >= sp->len)
return -1;
p_start = p; p_start = p;
while (p < sp->len) { while ((c = sp[p]) >= '0' && c <= '9') {
c = string_get(sp, p);
if (!(c >= '0' && c <= '9')) {
if (p == p_start)
return -1;
else
break;
}
v = v * 10 + c - '0'; v = v * 10 + c - '0';
p++; p++;
if (p - p_start == max_digits)
break;
} }
if (p - p_start < min_digits)
return FALSE;
*pval = v; *pval = v;
*pp = p; *pp = p;
return 0; return TRUE;
} }
static int string_get_signed_digits(JSString *sp, int *pp, int64_t *pval) { static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
int res, sgn, p = *pp; /* parse optional fractional part as milliseconds and truncate. */
/* spec does not indicate which rounding should be used */
int mul = 100, ms = 0, c, p_start, p = *pp;
if (p >= sp->len) c = sp[p];
return -1; if (c == '.' || c == ',') {
sgn = string_get(sp, p);
if (sgn == '-' || sgn == '+')
p++; p++;
p_start = p;
res = string_get_digits(sp, &p, pval); while ((c = sp[p]) >= '0' && c <= '9') {
if (res == 0 && sgn == '-') { ms += (c - '0') * mul;
if (*pval == 0) mul /= 10;
return -1; // reject negative zero p++;
*pval = -*pval; if (p - p_start == 9)
}
*pp = p;
return res;
}
/* parse a fixed width numeric field */
static int string_get_fixed_width_digits(JSString *sp, int *pp, int n, int64_t *pval) {
int64_t v = 0;
int i, c, p = *pp;
for(i = 0; i < n; i++) {
if (p >= sp->len)
return -1;
c = string_get(sp, p);
if (!(c >= '0' && c <= '9'))
return -1;
v = v * 10 + c - '0';
p++;
}
*pval = v;
*pp = p;
return 0;
}
static int string_get_milliseconds(JSString *sp, int *pp, int64_t *pval) {
/* parse milliseconds as a fractional part, round to nearest */
/* XXX: the spec does not indicate which rounding should be used */
int mul = 1000, ms = 0, p = *pp, c, p_start;
if (p >= sp->len)
return -1;
p_start = p;
while (p < sp->len) {
c = string_get(sp, p);
if (!(c >= '0' && c <= '9')) {
if (p == p_start)
return -1;
else
break; break;
} }
if (mul == 1 && c >= '5') if (p > p_start) {
ms += 1; /* only consume the separator if digits are present */
ms += (c - '0') * (mul /= 10); *pval = ms;
p++; *pp = p;
}
} }
*pval = ms; return TRUE;
*pp = p;
return 0;
} }
static uint8_t upper_ascii(uint8_t c) {
return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c;
}
static int find_abbrev(JSString *sp, int p, const char *list, int count) { static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
int tz = 0, sgn, hh, mm, p = *pp;
sgn = sp[p++];
if (sgn == '+' || sgn == '-') {
int n = p;
if (!string_get_digits(sp, &p, &hh, 1, 9))
return FALSE;
n = p - n;
if (strict && n != 2 && n != 4)
return FALSE;
while (n > 4) {
n -= 2;
hh /= 100;
}
if (n > 2) {
mm = hh % 100;
hh = hh / 100;
} else {
mm = 0;
if (string_skip_char(sp, &p, ':') /* optional separator */
&& !string_get_digits(sp, &p, &mm, 2, 2))
return FALSE;
}
if (hh > 23 || mm > 59)
return FALSE;
tz = hh * 60 + mm;
if (sgn != '+')
tz = -tz;
} else
if (sgn != 'Z') {
return FALSE;
}
*pp = p;
*tzp = tz;
return TRUE;
}
static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
int p = *pp;
while (*s != '\0') {
if (upper_ascii(sp[p]) != upper_ascii(*s++))
return FALSE;
p++;
}
*pp = p;
return TRUE;
}
static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) {
int n, i; int n, i;
if (p + 3 <= sp->len) { for (n = 0; n < count; n++) {
for (n = 0; n < count; n++) { for (i = 0;; i++) {
for (i = 0; i < 3; i++) { if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i]))
if (string_get(sp, p + i) != month_names[n * 3 + i]) break;
goto next; if (i == 2)
} return n;
return n;
next:;
} }
} }
return -1; return -1;
} }
static int string_get_month(JSString *sp, int *pp, int64_t *pval) { static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) {
int n; int n;
string_skip_spaces(sp, pp);
n = find_abbrev(sp, *pp, month_names, 12); n = find_abbrev(sp, *pp, month_names, 12);
if (n < 0) if (n < 0)
return -1; return FALSE;
*pval = n; *pval = n + 1;
*pp += 3; *pp += 3;
return 0; return TRUE;
}
/* parse toISOString format */
static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) {
int sgn, i, p = 0;
/* initialize fields to the beginning of the Epoch */
for (i = 0; i < 9; i++) {
fields[i] = (i == 2);
}
*is_local = FALSE;
/* year is either yyyy digits or [+-]yyyyyy */
sgn = sp[p];
if (sgn == '-' || sgn == '+') {
p++;
if (!string_get_digits(sp, &p, &fields[0], 6, 6))
return FALSE;
if (sgn == '-') {
if (fields[0] == 0)
return FALSE; // reject -000000
fields[0] = -fields[0];
}
} else {
if (!string_get_digits(sp, &p, &fields[0], 4, 4))
return FALSE;
}
if (string_skip_char(sp, &p, '-')) {
if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */
return FALSE;
if (fields[1] < 1)
return FALSE;
fields[1] -= 1;
if (string_skip_char(sp, &p, '-')) {
if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */
return FALSE;
if (fields[2] < 1)
return FALSE;
}
}
if (string_skip_char(sp, &p, 'T')) {
*is_local = TRUE;
if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */
|| !string_skip_char(sp, &p, ':')
|| !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */
fields[3] = 100; // reject unconditionally
return TRUE;
}
if (string_skip_char(sp, &p, ':')) {
if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */
return FALSE;
string_get_milliseconds(sp, &p, &fields[6]);
}
}
/* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
if (sp[p]) {
*is_local = FALSE;
if (!string_get_tzoffset(sp, &p, &fields[8], TRUE))
return FALSE;
}
/* error if extraneous characters */
return sp[p] == '\0';
}
static struct {
char name[6];
int16_t offset;
} const js_tzabbr[] = {
{ "GMT", 0 }, // Greenwich Mean Time
{ "UTC", 0 }, // Coordinated Universal Time
{ "UT", 0 }, // Universal Time
{ "Z", 0 }, // Zulu Time
{ "EDT", -4 * 60 }, // Eastern Daylight Time
{ "EST", -5 * 60 }, // Eastern Standard Time
{ "CDT", -5 * 60 }, // Central Daylight Time
{ "CST", -6 * 60 }, // Central Standard Time
{ "MDT", -6 * 60 }, // Mountain Daylight Time
{ "MST", -7 * 60 }, // Mountain Standard Time
{ "PDT", -7 * 60 }, // Pacific Daylight Time
{ "PST", -8 * 60 }, // Pacific Standard Time
{ "WET", +0 * 60 }, // Western European Time
{ "WEST", +1 * 60 }, // Western European Summer Time
{ "CET", +1 * 60 }, // Central European Time
{ "CEST", +2 * 60 }, // Central European Summer Time
{ "EET", +2 * 60 }, // Eastern European Time
{ "EEST", +3 * 60 }, // Eastern European Summer Time
};
static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
for (size_t i = 0; i < countof(js_tzabbr); i++) {
if (string_match(sp, pp, js_tzabbr[i].name)) {
*offset = js_tzabbr[i].offset;
return TRUE;
}
}
return FALSE;
}
/* parse toString, toUTCString and other formats */
static BOOL js_date_parse_otherstring(const uint8_t *sp,
int fields[minimum_length(9)],
BOOL *is_local) {
int c, i, val, p = 0, p_start;
int num[3];
BOOL has_year = FALSE;
BOOL has_mon = FALSE;
BOOL has_time = FALSE;
int num_index = 0;
/* initialize fields to the beginning of 2001-01-01 */
fields[0] = 2001;
fields[1] = 1;
fields[2] = 1;
for (i = 3; i < 9; i++) {
fields[i] = 0;
}
*is_local = TRUE;
while (string_skip_spaces(sp, &p)) {
p_start = p;
if ((c = sp[p]) == '+' || c == '-') {
if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
*is_local = FALSE;
} else {
p++;
if (string_get_digits(sp, &p, &val, 1, 9)) {
if (c == '-') {
if (val == 0)
return FALSE;
val = -val;
}
fields[0] = val;
has_year = TRUE;
}
}
} else
if (string_get_digits(sp, &p, &val, 1, 9)) {
if (string_skip_char(sp, &p, ':')) {
/* time part */
fields[3] = val;
if (!string_get_digits(sp, &p, &fields[4], 1, 2))
return FALSE;
if (string_skip_char(sp, &p, ':')) {
if (!string_get_digits(sp, &p, &fields[5], 1, 2))
return FALSE;
string_get_milliseconds(sp, &p, &fields[6]);
}
has_time = TRUE;
} else {
if (p - p_start > 2) {
fields[0] = val;
has_year = TRUE;
} else
if (val < 1 || val > 31) {
fields[0] = val + (val < 100) * 1900 + (val < 50) * 100;
has_year = TRUE;
} else {
if (num_index == 3)
return FALSE;
num[num_index++] = val;
}
}
} else
if (string_get_month(sp, &p, &fields[1])) {
has_mon = TRUE;
string_skip_until(sp, &p, "0123456789 -/(");
} else
if (has_time && string_match(sp, &p, "PM")) {
if (fields[3] < 12)
fields[3] += 12;
continue;
} else
if (has_time && string_match(sp, &p, "AM")) {
if (fields[3] == 12)
fields[3] -= 12;
continue;
} else
if (string_get_tzabbr(sp, &p, &fields[8])) {
*is_local = FALSE;
continue;
} else
if (c == '(') { /* skip parenthesized phrase */
int level = 0;
while ((c = sp[p]) != '\0') {
p++;
level += (c == '(');
level -= (c == ')');
if (!level)
break;
}
if (level > 0)
return FALSE;
} else
if (c == ')') {
return FALSE;
} else {
if (has_year + has_mon + has_time + num_index)
return FALSE;
/* skip a word */
string_skip_until(sp, &p, " -/(");
}
string_skip_separators(sp, &p);
}
if (num_index + has_year + has_mon > 3)
return FALSE;
switch (num_index) {
case 0:
if (!has_year)
return FALSE;
break;
case 1:
if (has_mon)
fields[2] = num[0];
else
fields[1] = num[0];
break;
case 2:
if (has_year) {
fields[1] = num[0];
fields[2] = num[1];
} else
if (has_mon) {
fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100;
fields[2] = num[0];
} else {
fields[1] = num[0];
fields[2] = num[1];
}
break;
case 3:
fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100;
fields[1] = num[0];
fields[2] = num[1];
break;
default:
return FALSE;
}
if (fields[1] < 1 || fields[2] < 1)
return FALSE;
fields[1] -= 1;
return TRUE;
} }
static JSValue js_Date_parse(JSContext *ctx, JSValue this_val, static JSValue js_Date_parse(JSContext *ctx, JSValue this_val,
int argc, JSValue *argv) int argc, JSValue *argv)
{ {
// parse(s)
JSValue s, rv; JSValue s, rv;
int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 }; int fields[9];
double fields1[7]; double fields1[9];
int64_t tz, hh, mm;
double d; double d;
int p, i, c, sgn, l; int i, c;
JSString *sp; JSString *sp;
uint8_t buf[128];
BOOL is_local; BOOL is_local;
rv = JS_NAN; rv = JS_NAN;
@ -47407,145 +47674,33 @@ static JSValue js_Date_parse(JSContext *ctx, JSValue this_val,
return JS_EXCEPTION; return JS_EXCEPTION;
sp = JS_VALUE_GET_STRING(s); sp = JS_VALUE_GET_STRING(s);
p = 0; /* convert the string as a byte array */
if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) { for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) {
/* ISO format */ c = string_get(sp, i);
/* year field can be negative */ if (c > 255)
if (string_get_signed_digits(sp, &p, &fields[0])) c = (c == 0x2212) ? '-' : 'x';
goto done; buf[i] = c;
}
for (i = 1; i < 7; i++) { buf[i] = '\0';
if (p >= sp->len) if (js_date_parse_isostring(buf, fields, &is_local)
break; || js_date_parse_otherstring(buf, fields, &is_local)) {
switch(i) { static int const field_max[6] = { 0, 11, 31, 24, 59, 59 };
case 1: BOOL valid = TRUE;
case 2: /* check field maximum values */
c = '-'; for (i = 1; i < 6; i++) {
break; if (fields[i] > field_max[i])
case 3: valid = FALSE;
c = 'T';
break;
case 4:
case 5:
c = ':';
break;
case 6:
c = '.';
break;
}
if (string_get(sp, p) != c)
break;
p++;
if (i == 6) {
if (string_get_milliseconds(sp, &p, &fields[i]))
goto done;
} else {
if (string_get_digits(sp, &p, &fields[i]))
goto done;
}
} }
/* no time: UTC by default */ /* special case 24:00:00.000 */
is_local = (i > 3); if (fields[3] == 24 && (fields[4] | fields[5] | fields[6]))
fields[1] -= 1; valid = FALSE;
if (valid) {
/* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ for(i = 0; i < 7; i++)
tz = 0; fields1[i] = fields[i];
if (p < sp->len) { d = set_date_fields(fields1, is_local) - fields[8] * 60000;
sgn = string_get(sp, p); rv = JS_NewFloat64(ctx, d);
if (sgn == '+' || sgn == '-') {
p++;
l = sp->len - p;
if (l != 4 && l != 5)
goto done;
if (string_get_fixed_width_digits(sp, &p, 2, &hh))
goto done;
if (l == 5) {
if (string_get(sp, p) != ':')
goto done;
p++;
}
if (string_get_fixed_width_digits(sp, &p, 2, &mm))
goto done;
tz = hh * 60 + mm;
if (sgn == '-')
tz = -tz;
is_local = FALSE;
} else if (sgn == 'Z') {
p++;
is_local = FALSE;
} else {
goto done;
}
/* error if extraneous characters */
if (p != sp->len)
goto done;
}
} else {
/* toString or toUTCString format */
/* skip the day of the week */
string_skip_non_spaces(sp, &p);
string_skip_spaces(sp, &p);
if (p >= sp->len)
goto done;
c = string_get(sp, p);
if (c >= '0' && c <= '9') {
/* day of month first */
if (string_get_digits(sp, &p, &fields[2]))
goto done;
if (string_get_month(sp, &p, &fields[1]))
goto done;
} else {
/* month first */
if (string_get_month(sp, &p, &fields[1]))
goto done;
string_skip_spaces(sp, &p);
if (string_get_digits(sp, &p, &fields[2]))
goto done;
}
/* year */
string_skip_spaces(sp, &p);
if (string_get_signed_digits(sp, &p, &fields[0]))
goto done;
/* hour, min, seconds */
string_skip_spaces(sp, &p);
for(i = 0; i < 3; i++) {
if (i == 1 || i == 2) {
if (p >= sp->len)
goto done;
if (string_get(sp, p) != ':')
goto done;
p++;
}
if (string_get_digits(sp, &p, &fields[3 + i]))
goto done;
}
// XXX: parse optional milliseconds?
/* parse the time zone offset if present: [+-]HHmm */
is_local = FALSE;
tz = 0;
for (tz = 0; p < sp->len; p++) {
sgn = string_get(sp, p);
if (sgn == '+' || sgn == '-') {
p++;
if (string_get_fixed_width_digits(sp, &p, 2, &hh))
goto done;
if (string_get_fixed_width_digits(sp, &p, 2, &mm))
goto done;
tz = hh * 60 + mm;
if (sgn == '-')
tz = -tz;
break;
}
} }
} }
for(i = 0; i < 7; i++)
fields1[i] = fields[i];
d = set_date_fields(fields1, is_local) - tz * 60000;
rv = js_float64(d);
done:
JS_FreeValue(ctx, s); JS_FreeValue(ctx, s);
return rv; return rv;
} }

View file

@ -938,6 +938,8 @@ import * as os from "os";
std.puts(a); std.puts(a);
} else if (stack.indexOf(a) >= 0) { } else if (stack.indexOf(a) >= 0) {
std.puts("[circular]"); std.puts("[circular]");
} else if (a instanceof Date) {
std.puts(`Date ${JSON.stringify(a.toGMTString())}`);
} else { } else {
stack.push(a); stack.push(a);
if (Array.isArray(a)) { if (Array.isArray(a)) {
@ -975,7 +977,7 @@ import * as os from "os";
stack.pop(a); stack.pop(a);
} }
} else if (type === "string") { } else if (type === "string") {
s = a.__quote(); s = JSON.stringify(s);
if (s.length > 79) if (s.length > 79)
s = s.substring(0, 75) + "...\""; s = s.substring(0, 75) + "...\"";
std.puts(s); std.puts(s);

View file

@ -85,7 +85,7 @@ function toStr(a)
case "undefined": case "undefined":
return "undefined"; return "undefined";
case "string": case "string":
return a.__quote(); return JSON.stringify(a);
case "number": case "number":
if (a == 0 && 1 / a < 0) if (a == 0 && 1 / a < 0)
return "-0"; return "-0";

View file

@ -596,29 +596,74 @@ function test_json()
function test_date() function test_date()
{ {
var d = new Date(1506098258091), a, s; // Date Time String format is YYYY-MM-DDTHH:mm:ss.sssZ
// accepted date formats are: YYYY, YYYY-MM and YYYY-MM-DD
// accepted time formats are: THH:mm, THH:mm:ss, THH:mm:ss.sss
// expanded years are represented with 6 digits prefixed by + or -
// -000000 is invalid.
// A string containing out-of-bounds or nonconforming elements
// is not a valid instance of this format.
// Hence the fractional part after . should have 3 digits and how
// a different number of digits is handled is implementation defined.
assert(Date.parse(""), NaN);
assert(Date.parse("2000"), 946684800000);
assert(Date.parse("2000-01"), 946684800000);
assert(Date.parse("2000-01-01"), 946684800000);
//assert(Date.parse("2000-01-01T"), NaN);
//assert(Date.parse("2000-01-01T00Z"), NaN);
assert(Date.parse("2000-01-01T00:00Z"), 946684800000);
assert(Date.parse("2000-01-01T00:00:00Z"), 946684800000);
assert(Date.parse("2000-01-01T00:00:00.1Z"), 946684800100);
assert(Date.parse("2000-01-01T00:00:00.10Z"), 946684800100);
assert(Date.parse("2000-01-01T00:00:00.100Z"), 946684800100);
assert(Date.parse("2000-01-01T00:00:00.1000Z"), 946684800100);
assert(Date.parse("2000-01-01T00:00:00+00:00"), 946684800000);
//assert(Date.parse("2000-01-01T00:00:00+00:30"), 946686600000);
var d = new Date("2000T00:00"); // Jan 1st 2000, 0:00:00 local time
assert(typeof d === 'object' && d.toString() != 'Invalid Date');
assert((new Date('Jan 1 2000')).toISOString(),
d.toISOString());
assert((new Date('Jan 1 2000 00:00')).toISOString(),
d.toISOString());
assert((new Date('Jan 1 2000 00:00:00')).toISOString(),
d.toISOString());
assert((new Date('Jan 1 2000 00:00:00 GMT+0100')).toISOString(),
'1999-12-31T23:00:00.000Z');
assert((new Date('Jan 1 2000 00:00:00 GMT+0200')).toISOString(),
'1999-12-31T22:00:00.000Z');
assert((new Date('Sat Jan 1 2000')).toISOString(),
d.toISOString());
assert((new Date('Sat Jan 1 2000 00:00')).toISOString(),
d.toISOString());
assert((new Date('Sat Jan 1 2000 00:00:00')).toISOString(),
d.toISOString());
assert((new Date('Sat Jan 1 2000 00:00:00 GMT+0100')).toISOString(),
'1999-12-31T23:00:00.000Z');
assert((new Date('Sat Jan 1 2000 00:00:00 GMT+0200')).toISOString(),
'1999-12-31T22:00:00.000Z');
var d = new Date(1506098258091);
assert(d.toISOString(), "2017-09-22T16:37:38.091Z"); assert(d.toISOString(), "2017-09-22T16:37:38.091Z");
d.setUTCHours(18, 10, 11); d.setUTCHours(18, 10, 11);
assert(d.toISOString(), "2017-09-22T18:10:11.091Z"); assert(d.toISOString(), "2017-09-22T18:10:11.091Z");
a = Date.parse(d.toISOString()); var a = Date.parse(d.toISOString());
assert((new Date(a)).toISOString(), d.toISOString()); assert((new Date(a)).toISOString(), d.toISOString());
s = new Date("2020-01-01T01:01:01.123Z").toISOString();
assert(s, "2020-01-01T01:01:01.123Z"); assert((new Date("2020-01-01T01:01:01.123Z")).toISOString(),
// implementation defined behavior "2020-01-01T01:01:01.123Z");
s = new Date("2020-01-01T01:01:01.1Z").toISOString(); /* implementation defined behavior */
assert(s, "2020-01-01T01:01:01.100Z"); assert((new Date("2020-01-01T01:01:01.1Z")).toISOString(),
s = new Date("2020-01-01T01:01:01.12Z").toISOString(); "2020-01-01T01:01:01.100Z");
assert(s, "2020-01-01T01:01:01.120Z"); assert((new Date("2020-01-01T01:01:01.12Z")).toISOString(),
s = new Date("2020-01-01T01:01:01.1234Z").toISOString(); "2020-01-01T01:01:01.120Z");
assert(s, "2020-01-01T01:01:01.123Z"); assert((new Date("2020-01-01T01:01:01.1234Z")).toISOString(),
s = new Date("2020-01-01T01:01:01.12345Z").toISOString(); "2020-01-01T01:01:01.123Z");
assert(s, "2020-01-01T01:01:01.123Z"); assert((new Date("2020-01-01T01:01:01.12345Z")).toISOString(),
s = new Date("2020-01-01T01:01:01.1235Z").toISOString(); "2020-01-01T01:01:01.123Z");
assert(s == "2020-01-01T01:01:01.124Z" || // QuickJS assert((new Date("2020-01-01T01:01:01.1235Z")).toISOString(),
s == "2020-01-01T01:01:01.123Z"); // nodeJS "2020-01-01T01:01:01.123Z");
s = new Date("2020-01-01T01:01:01.9999Z").toISOString(); assert((new Date("2020-01-01T01:01:01.9999Z")).toISOString(),
assert(s == "2020-01-01T01:01:02.000Z" || // QuickJS "2020-01-01T01:01:01.999Z");
s == "2020-01-01T01:01:01.999Z"); // nodeJS
assert(Date.UTC(2017), 1483228800000); assert(Date.UTC(2017), 1483228800000);
assert(Date.UTC(2017, 9), 1506816000000); assert(Date.UTC(2017, 9), 1506816000000);

11
v8.js
View file

@ -36,9 +36,14 @@ for (const file of files) {
if (source.includes ("--allow-natives-syntax")) continue if (source.includes ("--allow-natives-syntax")) continue
// exclude tests that use V8 extensions // exclude tests that use V8 extensions
if (source.includes ("--expose-externalize-string")) continue if (source.includes ("--expose-externalize-string")) continue
const env = let env = {}, envstr = ""
source.match(/environment variables:.*TZ=(?<TZ>[\S]+)/i)?.groups for (let s of source.matchAll(/environment variables:(.+)/ig)) {
print(`=== ${file}`) for (let m of s[1].matchAll(/\s*([\S]+)=([\S]+)/g)) {
env[m[1]] = m[2]
envstr += ` ${m[1]}=${m[2]}`
}
}
print(`=== ${file}${envstr}`)
// the fixed --stack-size is necessary to keep output of stack overflowing // the fixed --stack-size is necessary to keep output of stack overflowing
// tests stable; their stack traces are somewhat arbitrary otherwise // tests stable; their stack traces are somewhat arbitrary otherwise
const args = [argv0, "--stack-size", `${2048 * 1024}`, const args = [argv0, "--stack-size", `${2048 * 1024}`,

121
v8.txt
View file

@ -183,106 +183,7 @@ Failure (54[]): expected <true> found <false>
Failure (55[]): expected <true> found <false> Failure (55[]): expected <true> found <false>
Failure (56[]): expected <true> found <false> Failure (56[]): expected <true> found <false>
=== date-parse.js === date-parse.js
Failure (parse: Sat, 01-Jan-2000 08:00:00 UT): expected <946713600000> found <NaN> Failure (May 25 2008 1:30( )AM (PM) is not NaN.): expected <true> found <false>
Failure (parse: Jan 01 2000 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Jan 01 08:00:00 UT 2000): expected <946713600000> found <NaN>
Failure (parse: Saturday, 01-Jan-00 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 08:00 +0000): expected <946713600000> found <NaN>
Failure (parse: [Saturday] Jan 01 08:00:00 UT 2000): expected <946713600000> found <NaN>
Failure (parse: Ignore all of this stuff because it is annoying 01 Jan 2000 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: All of this stuff is really annnoying, so it will be ignored Jan 01 2000 08:00:00 UT): expected <946713600000> f
Failure (parse: Sat, 01-Janisamonth-2000 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Janisamonth 2000 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Janisamonth 01 2000 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Janisamonth 01 08:00:00 UT 2000): expected <946713600000> found <NaN>
Failure (parse: Saturday, 01-Janisamonth-00 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: 01 Janisamonth 00 08:00 +0000): expected <946713600000> found <NaN>
Failure (parse: Janisamonthandtherestisignored01 2000 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Jan01 2000 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Sat, 2000/01/01 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01/01/2000 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01/01 2000 08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01,Jan,2000,08:00:00 UT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 08:00 UT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 08:00 UT): expected <946713600000> found <NaN>
Failure (parse: Jan 01 2000 08:00 UT): expected <946713600000> found <NaN>
Failure (parse: Jan 01 08:00 UT 2000): expected <946713600000> found <NaN>
Failure (parse: Saturday, 01-Jan-00 08:00 UT): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 08:00 +0000): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 08:00 AM UT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 08:00 AM UT): expected <946713600000> found <NaN>
Failure (parse: Jan 01 2000 08:00 AM UT): expected <946713600000> found <NaN>
Failure (parse: Jan 01 08:00 AM UT 2000): expected <946713600000> found <NaN>
Failure (parse: Saturday, 01-Jan-00 08:00 AM UT): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 08:00 AM +0000): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: Saturday, 01-Jan-00 08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 08:00 +0000 ): expected <946713600000> found <NaN>
Failure (parse: ()(Sat, 01-Jan-2000) Sat, 01-Jan-2000 08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: Sat()(Sat, 01-Jan-2000)01 Jan 2000 08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: Sat,(02)01 Jan 2000 08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: Sat, 01(02)Jan 2000 08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 (2001)08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 (01)08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 (01:00:00)08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 08:00:00 (CDT)UT ): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 08:00:00 UT((((CDT))))): expected <946713600000> found <NaN>
Failure (parse: Saturday, 01-Jan-00 ()(((asfd)))(Sat, 01-Jan-2000)08:00:00 UT ): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 08:00 ()(((asdf)))(Sat, 01-Jan-2000)+0000 ): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 08:00 +0000()((asfd)(Sat, 01-Jan-2000)) ): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 08:00:00 GMT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 08:00:00 GMT+0): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 08:00:00 GMT+00): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 08:00:00 GMT+000): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 08:00:00 GMT+0000): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 08:00:00 GMT+00:00): expected <946713600000> found <NaN>
Failure (parse: Saturday, 01-Jan-00 08:00:00 GMT): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 08:00 -0000): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 08:00 +0000): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 03:00:00 UTC-0500): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 03:00:00 UTC-05:00): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 03:00:00 EST): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 03:00:00 EST): expected <946713600000> found <946695600000>
Failure (parse: Saturday, 01-Jan-00 03:00:00 EST): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 03:00 -0500): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 04:00:00 EDT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 04:00:00 EDT): expected <946713600000> found <946699200000>
Failure (parse: Saturday, 01-Jan-00 04:00:00 EDT): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 04:00 -0400): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 02:00:00 CST): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 02:00:00 CST): expected <946713600000> found <946692000000>
Failure (parse: Saturday, 01-Jan-00 02:00:00 CST): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 02:00 -0600): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 03:00:00 CDT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 03:00:00 CDT): expected <946713600000> found <946695600000>
Failure (parse: Saturday, 01-Jan-00 03:00:00 CDT): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 03:00 -0500): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 01:00:00 MST): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 01:00:00 MST): expected <946713600000> found <946688400000>
Failure (parse: Saturday, 01-Jan-00 01:00:00 MST): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 01:00 -0700): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 02:00:00 MDT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 02:00:00 MDT): expected <946713600000> found <946692000000>
Failure (parse: Saturday, 01-Jan-00 02:00:00 MDT): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 02:00 -0600): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 00:00:00 PST): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 00:00:00 PST): expected <946713600000> found <946684800000>
Failure (parse: Saturday, 01-Jan-00 00:00:00 PST): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 00:00 -0800): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 PST): expected <946713600000> found <NaN>
Failure (parse: Sat, 01-Jan-2000 01:00:00 PDT): expected <946713600000> found <NaN>
Failure (parse: Sat, 01 Jan 2000 01:00:00 PDT): expected <946713600000> found <946688400000>
Failure (parse: Saturday, 01-Jan-00 01:00:00 PDT): expected <946713600000> found <NaN>
Failure (parse: 01 Jan 00 01:00 -0700): expected <946713600000> found <NaN>
Failure (parse-local-time: Sat, 01-Jan-2000 08:00:00 is NaN.): expected <true> found <false>
Failure (parse-local-time: Sat, 01-Jan-2000 08:00:00 <= 0.): expected <true> found <false>
Failure (parse-local-time: Jan 01 2000 08:00:00 is NaN.): expected <true> found <false>
Failure (parse-local-time: Jan 01 2000 08:00:00 <= 0.): expected <true> found <false>
Failure (parse-local-time: Jan 01 08:00:00 2000 is NaN.): expected <true> found <false>
Failure (parse-local-time: Jan 01 08:00:00 2000 <= 0.): expected <true> found <false>
Failure (parse-local-time: Saturday, 01-Jan-00 08:00:00 is NaN.): expected <true> found <false>
<output elided>
=== declare-locally.js === declare-locally.js
=== deep-recursion.js === deep-recursion.js
=== define-property-gc.js === define-property-gc.js
@ -626,7 +527,7 @@ SyntaxError: unexpected token in expression: '>'
at html-comments.js:1:1 at html-comments.js:1:1
=== html-string-funcs.js === html-string-funcs.js
=== icu-date-lord-howe.js === icu-date-lord-howe.js TZ=Australia/Lord_Howe LC_ALL=en
Failure: Failure:
expected: expected:
"Mon Jan 01 1990 11:00:00 GMT+1100 (Lord Howe Daylight Time)" "Mon Jan 01 1990 11:00:00 GMT+1100 (Lord Howe Daylight Time)"
@ -637,7 +538,7 @@ expected:
"Fri Jun 01 1990 10:30:00 GMT+1030 (Lord Howe Standard Time)" "Fri Jun 01 1990 10:30:00 GMT+1030 (Lord Howe Standard Time)"
found: found:
"Fri Jun 01 1990 10:30:00 GMT+1030" "Fri Jun 01 1990 10:30:00 GMT+1030"
=== icu-date-to-string.js === icu-date-to-string.js TZ=Europe/Madrid LC_ALL=de
Failure: Failure:
expected: expected:
"Sun Dec 31 1989 00:00:00 GMT+0100 (Mitteleuropäische Normalzeit)" "Sun Dec 31 1989 00:00:00 GMT+0100 (Mitteleuropäische Normalzeit)"
@ -718,8 +619,6 @@ Did not throw exception
Did not throw exception Did not throw exception
=== json-stringify-stack.js === json-stringify-stack.js
=== json.js === json.js
Failure: expected <"1979-01-11T08:00:00.000Z"> found <null>
Failure: expected <"2005-05-05T05:05:05.000Z"> found <null>
Did not throw exception Did not throw exception
Did not throw exception Did not throw exception
Did not throw exception Did not throw exception
@ -1531,9 +1430,9 @@ Failure: expected <"[object global]"> found <"[object Object]">
=== try-finally-nested.js === try-finally-nested.js
=== try.js === try.js
=== typeof.js === typeof.js
=== tzoffset-seoul-noi18n.js === tzoffset-seoul-noi18n.js TZ=Asia/Seoul
=== tzoffset-seoul.js === tzoffset-seoul.js TZ=Asia/Seoul
=== tzoffset-transition-apia.js === tzoffset-transition-apia.js TZ=Pacific/Apia
Failure: expected <Date(1316872800000)> found <Date(1316876400000)> Failure: expected <Date(1316872800000)> found <Date(1316876400000)>
Failure: expected <Date(1316874600000)> found <Date(1316878200000)> Failure: expected <Date(1316874600000)> found <Date(1316878200000)>
Failure: expected <Date(1325275200000)> found <Date(1325188800000)> Failure: expected <Date(1325275200000)> found <Date(1325188800000)>
@ -1567,7 +1466,7 @@ Failure: expected <Date(1325323800000)> found <Date(1325237400000)>
Failure: expected <Date(1333198800000)> found <Date(1333202400000)> Failure: expected <Date(1333198800000)> found <Date(1333202400000)>
Failure: expected <Date(1333200600000)> found <Date(1333204200000)> Failure: expected <Date(1333200600000)> found <Date(1333204200000)>
Failure: expected <Date(1333202340000)> found <Date(1333205940000)> Failure: expected <Date(1333202340000)> found <Date(1333205940000)>
=== tzoffset-transition-lord-howe.js === tzoffset-transition-lord-howe.js TZ=Australia/Lord_Howe
Failure: expected <Date(1491056940000)> found <Date(1491058740000)> Failure: expected <Date(1491056940000)> found <Date(1491058740000)>
Failure: expected <Date(1491057000000)> found <Date(1491058800000)> Failure: expected <Date(1491057000000)> found <Date(1491058800000)>
Failure: expected <Date(1491057900000)> found <Date(1491059700000)> Failure: expected <Date(1491057900000)> found <Date(1491059700000)>
@ -1576,7 +1475,7 @@ Failure: expected <Date(1506785340000)> found <Date(1506783540000)>
Failure: expected <Date(1506785400000)> found <Date(1506783600000)> Failure: expected <Date(1506785400000)> found <Date(1506783600000)>
Failure: expected <Date(1506786300000)> found <Date(1506784500000)> Failure: expected <Date(1506786300000)> found <Date(1506784500000)>
Failure: expected <-660> found <-630> Failure: expected <-660> found <-630>
=== tzoffset-transition-moscow.js === tzoffset-transition-moscow.js TZ=Europe/Moscow
Failure: expected <Date(1269730740000)> found <Date(1269727140000)> Failure: expected <Date(1269730740000)> found <Date(1269727140000)>
Failure: expected <Date(1269730800000)> found <Date(1269727200000)> Failure: expected <Date(1269730800000)> found <Date(1269727200000)>
Failure: expected <Date(1269732600000)> found <Date(1269729000000)> Failure: expected <Date(1269732600000)> found <Date(1269729000000)>
@ -1593,12 +1492,12 @@ Failure: expected <Date(1414270740000)> found <Date(1414274340000)>
Failure: expected <Date(1414270800000)> found <Date(1414274400000)> Failure: expected <Date(1414270800000)> found <Date(1414274400000)>
Failure: expected <Date(1414272600000)> found <Date(1414276200000)> Failure: expected <Date(1414272600000)> found <Date(1414276200000)>
Failure: expected <Date(1414274340000)> found <Date(1414277940000)> Failure: expected <Date(1414274340000)> found <Date(1414277940000)>
=== tzoffset-transition-new-york-noi18n.js === tzoffset-transition-new-york-noi18n.js TZ=America/New_York
Failure: expected <Date(1489302000000)> found <Date(1489305600000)> Failure: expected <Date(1489302000000)> found <Date(1489305600000)>
Failure: expected <Date(1489303800000)> found <Date(1489307400000)> Failure: expected <Date(1489303800000)> found <Date(1489307400000)>
Failure: expected <Date(1509865200000)> found <Date(1509861600000)> Failure: expected <Date(1509865200000)> found <Date(1509861600000)>
Failure: expected <Date(1509868800000)> found <Date(1509865200000)> Failure: expected <Date(1509868800000)> found <Date(1509865200000)>
=== tzoffset-transition-new-york.js === tzoffset-transition-new-york.js TZ=America/New_York
Failure: expected <Date(1489302000000)> found <Date(1489305600000)> Failure: expected <Date(1489302000000)> found <Date(1489305600000)>
Failure: expected <Date(1489303800000)> found <Date(1489307400000)> Failure: expected <Date(1489303800000)> found <Date(1489307400000)>
Failure: expected <Date(1509865200000)> found <Date(1509861600000)> Failure: expected <Date(1509865200000)> found <Date(1509861600000)>