From 4d422e17df06cc0c83b501880656512330ce1528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Csaba=20Osztrogon=C3=A1c?= Date: Tue, 26 Nov 2019 13:04:11 +0100 Subject: [PATCH] Make Date.parse() ECMA-262 conform (#3314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Parse output of Date.prototype.toString() and Date.prototype.toUTCString() - Date.prototype.toString() is ECMA-262 v9 conform now, only TZ part changed (Before ECMA-262 v9 it was implementation-dependent.) - Reused day_names_p and month_names_p arrays (and made them more efficient) - Tests updated and new tests added Fixes #2946. JerryScript-DCO-1.0-Signed-off-by: Csaba Osztrogonác oszi@inf.u-szeged.hu --- .../ecma/builtin-objects/ecma-builtin-date.c | 355 ++++++++++++++++-- .../ecma-builtin-helpers-date.c | 32 +- .../builtin-objects/ecma-builtin-helpers.h | 3 + tests/jerry/date-parse.js | 45 ++- tests/jerry/date-tostring.js | 38 +- 5 files changed, 407 insertions(+), 66 deletions(-) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-date.c b/jerry-core/ecma/builtin-objects/ecma-builtin-date.c index 52171ee75..89f526a37 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-date.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-date.c @@ -96,6 +96,103 @@ ecma_date_parse_special_char (const lit_utf8_byte_t **str_p, /**< pointer to the return false; } /* ecma_date_parse_special_char */ +/** + * Helper function to try to parse a 4-5-6 digit year with optional negative sign in a date string + * + * Date.prototype.toString() and Date.prototype.toUTCString() emits year + * in this format and Date.parse() should parse this format too. + * + * @return the parsed year or NaN. + */ +static ecma_number_t +ecma_date_parse_year (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 string */ + const lit_utf8_byte_t *str_end_p) /**< pointer to the end of the string */ +{ + bool is_year_sign_negative = ecma_date_parse_special_char (str_p, str_end_p, '-'); + const lit_utf8_byte_t *str_start_p = *str_p; + int32_t parsed_year = 0; + + while (str_start_p - *str_p <= 6) + { + if (*str_p >= str_end_p || !lit_char_is_decimal_digit (*str_start_p)) + { + break; + } + + parsed_year = 10 * parsed_year + *str_start_p - LIT_CHAR_0; + str_start_p++; + } + + if (str_start_p - *str_p >=4) + { + *str_p = str_start_p; + if (is_year_sign_negative) + { + return -parsed_year; + } + return parsed_year; + } + + if (is_year_sign_negative) + { + str_p--; /* Parse failed, revert already parsed '-' sign. */ + } + return ecma_number_make_nan (); +} /* ecma_date_parse_year */ + +/** + * Helper function to try to parse a day name in a date string + * Valid day names: Sun, Mon, Tue, Wed, Thu, Fri, Sat + * See also: + * ECMA-262 v9, 20.3.4.41.2 Table 46 + * + * @return true if the string starts with a valid day name, false otherwise + */ +static bool +ecma_date_parse_day_name (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 string */ + const lit_utf8_byte_t *str_end_p) /**< pointer to the end of the string */ +{ + if (*str_p + 3 < str_end_p) + { + for (uint32_t i = 0; i < 7; i++) + { + if (!memcmp (day_names_p[i], *str_p, 3)) + { + (*str_p) += 3; + return true; + } + } + } + return false; +} /* ecma_date_parse_day_name */ + +/** + * Helper function to try to parse a month name in a date string + * Valid month names: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec + * See also: + * ECMA-262 v9, 20.3.4.41.2 Table 47 + * + * @return number of the month if the string starts with a valid month name, 0 otherwise + */ +static uint32_t +ecma_date_parse_month_name (const lit_utf8_byte_t **str_p, /**< pointer to the cesu8 string */ + const lit_utf8_byte_t *str_end_p) /**< pointer to the end of the string */ +{ + if (*str_p + 3 < str_end_p) + { + for (uint32_t i = 0; i < 12; i++) + { + if (!memcmp (month_names_p[i], *str_p, 3)) + { + (*str_p) += 3; + return (i+1); + } + } + } + return 0; +} /* ecma_date_parse_month_name */ + + /** * Calculate MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)) for Date constructor and UTC * @@ -197,35 +294,18 @@ ecma_date_construct_helper (const ecma_value_t *args, /**< arguments passed to t } /* ecma_date_construct_helper */ /** - * The Date object's 'parse' routine + * Helper function used by ecma_builtin_date_parse * * See also: - * ECMA-262 v5, 15.9.4.2 - * ECMA-262 v5, 15.9.1.15 + * ECMA-262 v5, 15.9.4.2 Date.parse (string) + * ECMA-262 v5, 15.9.1.15 Date Time String Format * - * @return ecma value - * Returned value must be freed with ecma_free_value. + * @return the parsed date as ecma_number_t or NaN otherwise */ -static ecma_value_t -ecma_builtin_date_parse (ecma_value_t this_arg, /**< this argument */ - ecma_value_t arg) /**< string */ +static ecma_number_t +ecma_builtin_date_parse_ISO_string_format (const lit_utf8_byte_t *date_str_curr_p, + const lit_utf8_byte_t *date_str_end_p) { - JERRY_UNUSED (this_arg); - ecma_value_t ret_value = ECMA_VALUE_EMPTY; - ecma_number_t date_num = ecma_number_make_nan (); - - /* Date Time String fromat (ECMA-262 v5, 15.9.1.15) */ - ecma_string_t *date_str_p = ecma_op_to_string (arg); - if (JERRY_UNLIKELY (date_str_p == NULL)) - { - return ECMA_VALUE_ERROR; - } - - ECMA_STRING_TO_UTF8_STRING (date_str_p, date_start_p, date_start_size); - - const lit_utf8_byte_t *date_str_curr_p = date_start_p; - const lit_utf8_byte_t *date_str_end_p = date_start_p + date_start_size; - /* 1. read year */ uint32_t year_digits = 4; @@ -341,16 +421,232 @@ ecma_builtin_date_parse (ecma_value_t this_arg, /**< this argument */ if (date_str_curr_p >= date_str_end_p) { ecma_number_t date = ecma_date_make_day (year, month - 1, day); - date_num = ecma_date_make_date (date, time); + return ecma_date_make_date (date, time); + } + } + return ecma_number_make_nan (); +} /* ecma_builtin_date_parse_ISO_string_format */ + +/** + * Helper function used by ecma_builtin_date_parse + * + * See also: + * ECMA-262 v5, 15.9.4.2 Date.parse (string) + * ECMA-262 v9, 20.3.4.41 Date.prototype.toString () + * ECMA-262 v9, 20.3.4.43 Date.prototype.toUTCString () + * + * Used by: ecma_builtin_date_parse + * + * @return the parsed date as ecma_number_t or NaN otherwise + */ +static ecma_number_t +ecma_builtin_date_parse_toString_formats (const lit_utf8_byte_t *date_str_curr_p, + const lit_utf8_byte_t *date_str_end_p) +{ + const ecma_number_t nan = ecma_number_make_nan (); + + if (!ecma_date_parse_day_name (&date_str_curr_p, date_str_end_p)) + { + return nan; + } + + const bool is_toUTCString_format = ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, ','); + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, ' ')) + { + return nan; + } + + ecma_number_t month = 0; + ecma_number_t day = 0; + if (is_toUTCString_format) + { + day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 31); + if (ecma_number_is_nan (day)) + { + return nan; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, ' ')) + { + return nan; + } + + month = ecma_date_parse_month_name (&date_str_curr_p, date_str_end_p); + if (!(int) month) + { + return nan; + } + } + else + { + month = ecma_date_parse_month_name (&date_str_curr_p, date_str_end_p); + if (!(int) month) + { + return nan; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, ' ')) + { + return nan; + } + + day = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 31); + if (ecma_number_is_nan (day)) + { + return nan; } } - ret_value = ecma_make_number_value (date_num); + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, ' ')) + { + return nan; + } + + ecma_number_t year = ecma_date_parse_year (&date_str_curr_p, date_str_end_p); + if (ecma_number_is_nan (year)) + { + return nan; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, ' ')) + { + return nan; + } + + ecma_number_t hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 24); + if (ecma_number_is_nan (hours)) + { + return nan; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, ':')) + { + return nan; + } + + ecma_number_t minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 59); + if (ecma_number_is_nan (minutes)) + { + return nan; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, ':')) + { + return nan; + } + + ecma_number_t seconds = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 59); + if (ecma_number_is_nan (seconds)) + { + return nan; + } + + if (hours == 24 && (minutes != 0 || seconds != 0)) + { + return nan; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, ' ')) + { + return nan; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, 'G')) + { + return nan; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, 'M')) + { + return nan; + } + + if (!ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, 'T')) + { + return nan; + } + + ecma_number_t time = ecma_date_make_time (hours, minutes, seconds, 0); + + if (!is_toUTCString_format) + { + bool is_timezone_sign_negative = ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, '-'); + if (!is_timezone_sign_negative && !ecma_date_parse_special_char (&date_str_curr_p, date_str_end_p, '+')) + { + return nan; + } + + hours = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 24); + if (ecma_number_is_nan (hours)) + { + return nan; + } + if (hours == 24) + { + hours = ECMA_NUMBER_ZERO; + } + + minutes = ecma_date_parse_date_chars (&date_str_curr_p, date_str_end_p, 2, 0, 59); + if (ecma_number_is_nan (minutes)) + { + return nan; + } + + ecma_number_t timezone_offset = ecma_date_make_time (hours, minutes, ECMA_NUMBER_ZERO, ECMA_NUMBER_ZERO); + time += is_timezone_sign_negative ? timezone_offset : -timezone_offset; + } + + if (date_str_curr_p >= date_str_end_p) + { + ecma_number_t date = ecma_date_make_day (year, month - 1, day); + return ecma_date_make_date (date, time); + } + + return nan; +} /* ecma_builtin_date_parse_toString_formats */ + +/** + * The Date object's 'parse' routine + * + * See also: + * ECMA-262 v5, 15.9.4.2 Date.parse (string) + * ECMA-262 v5, 15.9.1.15 Date Time String Format + * ECMA-262 v9, 20.3.4.41 Date.prototype.toString () + * ECMA-262 v9, 20.3.4.43 Date.prototype.toUTCString () + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_date_parse (ecma_value_t this_arg, /**< this argument */ + ecma_value_t arg) /**< string */ +{ + JERRY_UNUSED (this_arg); + + /* Date Time String fromat (ECMA-262 v5, 15.9.1.15) */ + ecma_string_t *date_str_p = ecma_op_to_string (arg); + if (JERRY_UNLIKELY (date_str_p == NULL)) + { + return ECMA_VALUE_ERROR; + } + + ECMA_STRING_TO_UTF8_STRING (date_str_p, date_start_p, date_start_size); + const lit_utf8_byte_t *date_str_curr_p = date_start_p; + const lit_utf8_byte_t *date_str_end_p = date_start_p + date_start_size; + + // try to parse date string as ISO string - ECMA-262 v5, 15.9.1.15 + ecma_number_t ret_value = ecma_builtin_date_parse_ISO_string_format (date_str_curr_p, date_str_end_p); + + if (ecma_number_is_nan (ret_value)) + { + // try to parse date string in Date.prototype.toString() or toUTCString() format + ret_value = ecma_builtin_date_parse_toString_formats (date_str_curr_p, date_str_end_p); + } ECMA_FINALIZE_UTF8_STRING (date_start_p, date_start_size); ecma_deref_ecma_string (date_str_p); - - return ret_value; + return ecma_make_number_value (ret_value); } /* ecma_builtin_date_parse */ /** @@ -534,4 +830,7 @@ ecma_builtin_date_dispatch_construct (const ecma_value_t *arguments_list_p, /**< * @} */ +#undef BREAK_IF_FALSE +#undef BREAK_IF_NAN + #endif /* ENABLED (JERRY_BUILTIN_DATE) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c index 6c2740b58..356e49c92 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers-date.c @@ -33,6 +33,16 @@ * @{ */ +const char day_names_p[7][3] = +{ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +const char month_names_p[12][3] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + /** * Helper function to get day number from time value. * @@ -577,16 +587,6 @@ static ecma_value_t ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ const char *format_p) /**< format buffer */ { - static const char * const day_names_p[8] = - { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; - - static const char * const month_names_p[13] = - { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; - const uint32_t date_buffer_length = 37; JERRY_VLA (lit_utf8_byte_t, date_buffer, date_buffer_length); @@ -735,13 +735,9 @@ ecma_date_to_string_format (ecma_number_t datetime_number, /**< datetime */ if (str_p != NULL) { - /* Print string values. */ - do - { - *dest_p++ = (lit_utf8_byte_t) *str_p++; - } - while (*str_p != LIT_CHAR_NULL); - + /* Print string values: month or day name which is always 3 characters */ + memcpy (dest_p, str_p, 3); + dest_p += 3; continue; } @@ -791,7 +787,7 @@ ecma_value_t ecma_date_value_to_string (ecma_number_t datetime_number) /**< datetime */ { datetime_number += ecma_date_local_time_zone_adjustment (datetime_number); - return ecma_date_to_string_format (datetime_number, "$W $M $D $Y $h:$m:$s GMT$z:$Z"); + return ecma_date_to_string_format (datetime_number, "$W $M $D $Y $h:$m:$s GMT$z$Z"); } /* ecma_date_value_to_string */ /** diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h index 10c7ab960..7086d4cae 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h @@ -146,6 +146,9 @@ typedef enum } ecma_date_timezone_t; /* ecma-builtin-helpers-date.c */ +extern const char day_names_p[7][3]; +extern const char month_names_p[12][3]; + ecma_number_t ecma_date_day (ecma_number_t time); ecma_number_t ecma_date_time_within_day (ecma_number_t time); ecma_number_t ecma_date_year_from_time (ecma_number_t time); diff --git a/tests/jerry/date-parse.js b/tests/jerry/date-parse.js index 93ebe6451..5c340d9b6 100644 --- a/tests/jerry/date-parse.js +++ b/tests/jerry/date-parse.js @@ -61,7 +61,16 @@ var wrongFormats = ["", "+0002015-01-01", "-0002015-01-01", "2015-01T00:00:00.000-03X00", - "2015-01-01T00-03:00"]; + "2015-01-01T00-03:00", + "Fri Jan 01 1 00:00:00 GMT+0000", + "Fri Jan 01 11 00:00:00 GMT+0000", + "Fri Jan 01 111 00:00:00 GMT+0000", + "Fri Jan 01 1234567 00:00:00 GMT+0000", + "Fri Jan 01 +1000 00:00:00 GMT+0000", + "Fri Jan 01 -1 00:00:00 GMT+0000", + "Fri Jan 01 -11 00:00:00 GMT+0000", + "Fri Jan 01 -111 00:00:00 GMT+0000", + "Fri Jan 01 -1234567 00:00:00 GMT+0000"]; for (i in wrongFormats) { var d = Date.parse(wrongFormats[i]); @@ -125,3 +134,37 @@ assert (Date.parse("9999-12-31T23:59:59.999Z") == 253402300799999) assert (Date.parse("+010000-01-01T00:00:00.000Z") == 253402300800000) assert (Date.parse("+275760-09-13T00:00:00.000Z") == 8640000000000000) assert (Date.parse("+275760-09-13T00:00:00.001Z") == 8640000000000001) + +// Date.toString() format +assert (Date.parse("Tue Apr 20 -271821 00:00:00 GMT+0000") == -8640000000000000) +assert (Date.parse("Fri Dec 31 -0001 23:59:59 GMT+0000") == -62167219201000) +assert (Date.parse("Sat Jan 01 0000 00:00:00 GMT+0000") == -62167219200000) +assert (Date.parse("Thu Dec 31 0009 23:59:59 GMT+0000") == -61851600001000) +assert (Date.parse("Fri Jan 01 0010 00:00:00 GMT+0000") == -61851600000000) +assert (Date.parse("Thu Dec 31 0099 23:59:59 GMT+0000") == -59011459201000) +assert (Date.parse("Fri Jan 01 0100 00:00:00 GMT+0000") == -59011459200000) +assert (Date.parse("Tue Dec 31 0999 23:59:59 GMT+0000") == -30610224001000) +assert (Date.parse("Wed Jan 01 1000 00:00:00 GMT+0000") == -30610224000000) +assert (Date.parse("Wed Dec 31 1969 23:59:59 GMT+0000") == -1000) +assert (Date.parse("Thu Jan 01 1970 00:00:00 GMT+0000") == 0) +assert (Date.parse("Thu Jan 01 1970 00:00:01 GMT+0000") == 1000) +assert (Date.parse("Fri Dec 31 9999 23:59:59 GMT+0000") == 253402300799000) +assert (Date.parse("Sat Jan 01 10000 00:00:00 GMT+0000") == 253402300800000) +assert (Date.parse("Sat Sep 13 275760 00:00:00 GMT+0000") == 8640000000000000) + +// Date.toUTCString() format +assert (Date.parse("Tue, 20 Apr -271821 00:00:00 GMT") == -8640000000000000) +assert (Date.parse("Fri, 31 Dec -0001 23:59:59 GMT") == -62167219201000) +assert (Date.parse("Sat, 01 Jan 0000 00:00:00 GMT") == -62167219200000) +assert (Date.parse("Thu, 31 Dec 0009 23:59:59 GMT") == -61851600001000) +assert (Date.parse("Fri, 01 Jan 0010 00:00:00 GMT") == -61851600000000) +assert (Date.parse("Thu, 31 Dec 0099 23:59:59 GMT") == -59011459201000) +assert (Date.parse("Fri, 01 Jan 0100 00:00:00 GMT") == -59011459200000) +assert (Date.parse("Tue, 31 Dec 0999 23:59:59 GMT") == -30610224001000) +assert (Date.parse("Wed, 01 Jan 1000 00:00:00 GMT") == -30610224000000) +assert (Date.parse("Wed, 31 Dec 1969 23:59:59 GMT") == -1000) +assert (Date.parse("Thu, 01 Jan 1970 00:00:00 GMT") == 0) +assert (Date.parse("Thu, 01 Jan 1970 00:00:01 GMT") == 1000) +assert (Date.parse("Fri, 31 Dec 9999 23:59:59 GMT") == 253402300799000) +assert (Date.parse("Sat, 01 Jan 10000 00:00:00 GMT") == 253402300800000) +assert (Date.parse("Sat, 13 Sep 275760 00:00:00 GMT") == 8640000000000000) diff --git a/tests/jerry/date-tostring.js b/tests/jerry/date-tostring.js index f02676908..b2335fb27 100644 --- a/tests/jerry/date-tostring.js +++ b/tests/jerry/date-tostring.js @@ -19,8 +19,8 @@ assert (new Date (2015, 7, 1, 0, Infinity, 0) == "Invalid Date"); assert (new Date (NaN, 1, 1, 0, 0, 0) == "Invalid Date"); assert (new Date (2015, NaN, 1, 0, 0, 0) == "Invalid Date"); assert (new Date (2015, 7, 1, 0, NaN, 0) == "Invalid Date"); -assert (/Fri Feb 13 2015 \d{2}:\d{2}:\d{2} GMT\+\d{2}:\d{2}/.test (new Date ("2015-02-13"))); -assert (/Wed Jul 08 2015 \d{2}:\d{2}:\d{2} GMT\+\d{2}:\d{2}/.test (new Date ("2015-07-08T11:29:05.023"))); +assert (/Fri Feb 13 2015 \d{2}:\d{2}:\d{2} GMT\+\d{2}\d{2}/.test (new Date ("2015-02-13"))); +assert (/Wed Jul 08 2015 \d{2}:\d{2}:\d{2} GMT\+\d{2}\d{2}/.test (new Date ("2015-07-08T11:29:05.023"))); try { @@ -33,12 +33,12 @@ catch (e) } var date = new Date(0); -assert (/Thu Jan 01 1970 \d{2}:\d{2}:\d{2} GMT\+\d{2}:\d{2}/.test (date.toString())); +assert (/Thu Jan 01 1970 \d{2}:\d{2}:\d{2} GMT\+\d{2}\d{2}/.test (date.toString())); assert (date.toUTCString() === "Thu, 01 Jan 1970 00:00:00 GMT"); assert (date.toISOString() === "1970-01-01T00:00:00.000Z"); date = new Date("2015-08-12T09:40:20.000Z") -assert (/Wed Aug 12 2015 \d{2}:\d{2}:\d{2} GMT\+\d{2}:\d{2}/.test (date.toString())); +assert (/Wed Aug 12 2015 \d{2}:\d{2}:\d{2} GMT\+\d{2}\d{2}/.test (date.toString())); assert (date.toUTCString() === "Wed, 12 Aug 2015 09:40:20 GMT"); assert (date.toISOString() === "2015-08-12T09:40:20.000Z"); @@ -145,26 +145,26 @@ assert (new Date ("2015-07-08T11:29:05.023Z").toISOString() == "2015-07-08T11:29 // corner cases assert (new Date (-8640000000000001).toString() == "Invalid Date") -assert (new Date (-8640000000000000).toString() == "Tue Apr 20 -271821 00:00:00 GMT+00:00") +assert (new Date (-8640000000000000).toString() == "Tue Apr 20 -271821 00:00:00 GMT+0000") -assert (new Date(-62167219200001).toString() == "Fri Dec 31 -0001 23:59:59 GMT+00:00") -assert (new Date(-62167219200000).toString() == "Sat Jan 01 0000 00:00:00 GMT+00:00") +assert (new Date(-62167219200001).toString() == "Fri Dec 31 -0001 23:59:59 GMT+0000") +assert (new Date(-62167219200000).toString() == "Sat Jan 01 0000 00:00:00 GMT+0000") -assert (new Date(-61851600000001).toString() == "Thu Dec 31 0009 23:59:59 GMT+00:00") -assert (new Date(-61851600000000).toString() == "Fri Jan 01 0010 00:00:00 GMT+00:00") +assert (new Date(-61851600000001).toString() == "Thu Dec 31 0009 23:59:59 GMT+0000") +assert (new Date(-61851600000000).toString() == "Fri Jan 01 0010 00:00:00 GMT+0000") -assert (new Date(-59011459200001).toString() == "Thu Dec 31 0099 23:59:59 GMT+00:00") -assert (new Date(-59011459200000).toString() == "Fri Jan 01 0100 00:00:00 GMT+00:00") +assert (new Date(-59011459200001).toString() == "Thu Dec 31 0099 23:59:59 GMT+0000") +assert (new Date(-59011459200000).toString() == "Fri Jan 01 0100 00:00:00 GMT+0000") -assert (new Date(-30610224000001).toString() == "Tue Dec 31 0999 23:59:59 GMT+00:00") -assert (new Date(-30610224000000).toString() == "Wed Jan 01 1000 00:00:00 GMT+00:00") +assert (new Date(-30610224000001).toString() == "Tue Dec 31 0999 23:59:59 GMT+0000") +assert (new Date(-30610224000000).toString() == "Wed Jan 01 1000 00:00:00 GMT+0000") -assert (new Date(-1).toString() == "Wed Dec 31 1969 23:59:59 GMT+00:00") -assert (new Date(0).toString() == "Thu Jan 01 1970 00:00:00 GMT+00:00") -assert (new Date(1).toString() == "Thu Jan 01 1970 00:00:00 GMT+00:00") +assert (new Date(-1).toString() == "Wed Dec 31 1969 23:59:59 GMT+0000") +assert (new Date(0).toString() == "Thu Jan 01 1970 00:00:00 GMT+0000") +assert (new Date(1).toString() == "Thu Jan 01 1970 00:00:00 GMT+0000") -assert (new Date(253402300799999).toString() == "Fri Dec 31 9999 23:59:59 GMT+00:00") -assert (new Date(253402300800000).toString() == "Sat Jan 01 10000 00:00:00 GMT+00:00") +assert (new Date(253402300799999).toString() == "Fri Dec 31 9999 23:59:59 GMT+0000") +assert (new Date(253402300800000).toString() == "Sat Jan 01 10000 00:00:00 GMT+0000") -assert (new Date (8640000000000000).toString() == "Sat Sep 13 275760 00:00:00 GMT+00:00") +assert (new Date (8640000000000000).toString() == "Sat Sep 13 275760 00:00:00 GMT+0000") assert (new Date (8640000000000001).toString() == "Invalid Date")