Bug 830304 - Compute correct time zone offsets for Date methods. r=Waldo

--HG--
extra : rebase_source : 480e087c1e19771f18ccb7146b55f063f347b817
This commit is contained in:
André Bargull 2017-10-11 06:54:44 -07:00
Родитель 1bba75e15f
Коммит f5c8496af4
8 изменённых файлов: 855 добавлений и 10 удалений

Просмотреть файл

@ -13,6 +13,8 @@
#include "mozilla/Unused.h"
#include <cmath>
#include <cstdlib>
#include <ctime>
#include "jsapi.h"
#include "jscntxt.h"
@ -4737,6 +4739,118 @@ DisableForEach(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
GetTimeZone(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject callee(cx, &args.callee());
if (args.length() != 0) {
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
return false;
}
auto getTimeZone = [](std::time_t* now) -> const char* {
std::tm local{};
#if defined(_WIN32)
_tzset();
if (localtime_s(&local, now) == 0)
return _tzname[local.tm_isdst > 0];
#else
tzset();
#if defined(HAVE_LOCALTIME_R)
if (localtime_r(now, &local)) {
#else
std::tm* localtm = std::localtime(now);
if (localtm) {
*local = *localtm;
#endif /* HAVE_LOCALTIME_R */
#if defined(HAVE_TM_ZONE_TM_GMTOFF)
return local.tm_zone;
#else
return tzname[local.tm_isdst > 0];
#endif /* HAVE_TM_ZONE_TM_GMTOFF */
}
#endif /* _WIN32 */
return nullptr;
};
std::time_t now = std::time(nullptr);
if (now != static_cast<std::time_t>(-1)) {
if (const char* tz = getTimeZone(&now)) {
JSString* str = JS_NewStringCopyZ(cx, tz);
if (!str)
return false;
args.rval().setString(str);
return true;
}
}
args.rval().setUndefined();
return true;
}
static bool
SetTimeZone(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject callee(cx, &args.callee());
if (args.length() != 1) {
ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
return false;
}
if (!args[0].isString() && !args[0].isUndefined()) {
ReportUsageErrorASCII(cx, callee, "First argument should be a string or undefined");
return false;
}
auto setTimeZone = [](const char* value) {
#if defined(_WIN32)
return _putenv_s("TZ", value) == 0;
#else
return setenv("TZ", value, true) == 0;
#endif /* _WIN32 */
};
auto unsetTimeZone = []() {
#if defined(_WIN32)
return _putenv_s("TZ", "") == 0;
#else
return unsetenv("TZ") == 0;
#endif /* _WIN32 */
};
if (args[0].isString() && !args[0].toString()->empty()) {
JSAutoByteString timeZone;
if (!timeZone.encodeLatin1(cx, args[0].toString()))
return false;
if (!setTimeZone(timeZone.ptr())) {
JS_ReportErrorASCII(cx, "Failed to set 'TZ' environment variable");
return false;
}
} else {
if (!unsetTimeZone()) {
JS_ReportErrorASCII(cx, "Failed to unset 'TZ' environment variable");
return false;
}
}
#if defined(_WIN32)
_tzset();
#else
tzset();
#endif /* _WIN32 */
JS::ResetTimeZone();
args.rval().setUndefined();
return true;
}
#if defined(FUZZING) && defined(__AFL_COMPILER)
static bool
AflLoop(JSContext* cx, unsigned argc, Value* vp)
@ -5414,6 +5528,10 @@ gc::ZealModeHelpText),
"isLegacyIterator(value)",
" Returns whether the value is considered is a legacy iterator.\n"),
JS_FN_HELP("getTimeZone", GetTimeZone, 0, 0,
"getTimeZone()",
" Get the current time zone.\n"),
JS_FS_HELP_END
};
@ -5432,6 +5550,12 @@ static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions[] = {
"getErrorNotes(error)",
" Returns an array of error notes."),
JS_FN_HELP("setTimeZone", SetTimeZone, 1, 0,
"setTimeZone(tzname)",
" Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n"
" An empty string or undefined resets the time zone to its default value.\n"
" NOTE: The input string is not validated and will be passed verbatim to setenv()."),
JS_FS_HELP_END
};

Просмотреть файл

@ -483,7 +483,14 @@ LocalTime(double t)
static double
UTC(double t)
{
return t - AdjustTime(t - DateTimeInfo::localTZA());
// Following the ES2017 specification creates undesirable results at DST
// transitions. For example when transitioning from PST to PDT,
// |new Date(2016,2,13,2,0,0).toTimeString()| returns the string value
// "01:00:00 GMT-0800 (PST)" instead of "03:00:00 GMT-0700 (PDT)". Follow
// V8 and subtract one hour before computing the offset.
// Spec bug: https://bugs.ecmascript.org/show_bug.cgi?id=4007
return t - AdjustTime(t - DateTimeInfo::localTZA() - msPerHour);
}
/* ES5 15.9.1.10. */
@ -2584,7 +2591,7 @@ date_toJSON(JSContext* cx, unsigned argc, Value* vp)
/* Interface to PRMJTime date struct. */
static PRMJTime
ToPRMJTime(double localTime)
ToPRMJTime(double localTime, double utcTime)
{
double year = YearFromTime(localTime);
@ -2598,9 +2605,7 @@ ToPRMJTime(double localTime)
prtm.tm_wday = int8_t(WeekDay(localTime));
prtm.tm_year = year;
prtm.tm_yday = int16_t(DayWithinYear(localTime, year));
// XXX: DaylightSavingTA expects utc-time argument.
prtm.tm_isdst = (DaylightSavingTA(localTime) != 0);
prtm.tm_isdst = (DaylightSavingTA(utcTime) != 0);
return prtm;
}
@ -2647,7 +2652,7 @@ FormatDate(JSContext* cx, double utcTime, FormatSpec format, MutableHandleValue
*/
/* get a time zone string from the OS to include as a comment. */
PRMJTime prtm = ToPRMJTime(utcTime);
PRMJTime prtm = ToPRMJTime(localTime, utcTime);
size_t tzlen = PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &prtm);
if (tzlen != 0) {
/*
@ -2727,7 +2732,7 @@ ToLocaleFormatHelper(JSContext* cx, HandleObject obj, const char* format, Mutabl
strcpy(buf, js_NaN_date_str);
} else {
double localTime = LocalTime(utcTime);
PRMJTime prtm = ToPRMJTime(localTime);
PRMJTime prtm = ToPRMJTime(localTime, utcTime);
/* Let PRMJTime format it. */
size_t result_len = PRMJ_FormatTime(buf, sizeof buf, format, &prtm);

Просмотреть файл

@ -0,0 +1,3 @@
if (typeof setTimeZone === "undefined") {
var setTimeZone = SpecialPowers.Cu.getJSTestingFunctions().setTimeZone;
}

Просмотреть файл

@ -0,0 +1,134 @@
// |reftest| skip-if(!xulRuntime.shell)
// Note: The default time zone is set to PST8PDT for all jstests (when run in the shell).
assertEq(/^(PST|PDT)$/.test(getTimeZone()), true);
const msPerMinute = 60 * 1000;
const msPerHour = 60 * 60 * 1000;
const Month = {
January: 0,
February: 1,
March: 2,
April: 3,
May: 4,
June: 5,
July: 6,
August: 7,
September: 8,
October: 9,
November: 10,
December: 11,
};
// PDT -> PST, using milliseconds from epoch.
{
let midnight = new Date(2016, Month.November, 6, 0, 0, 0, 0);
let midnightUTC = Date.UTC(2016, Month.November, 6, 0, 0, 0, 0);
// Ensure midnight time is correct.
assertEq(midnightUTC - midnight.getTime(), -7 * msPerHour);
let tests = [
{ offset: 0 * 60, date: "Sun Nov 06 2016", time: "00:00:00 GMT-0700 (PDT)" },
{ offset: 0 * 60 + 30, date: "Sun Nov 06 2016", time: "00:30:00 GMT-0700 (PDT)" },
{ offset: 1 * 60, date: "Sun Nov 06 2016", time: "01:00:00 GMT-0700 (PDT)" },
{ offset: 1 * 60 + 30, date: "Sun Nov 06 2016", time: "01:30:00 GMT-0700 (PDT)" },
{ offset: 2 * 60, date: "Sun Nov 06 2016", time: "01:00:00 GMT-0800 (PST)" },
{ offset: 2 * 60 + 30, date: "Sun Nov 06 2016", time: "01:30:00 GMT-0800 (PST)" },
{ offset: 3 * 60, date: "Sun Nov 06 2016", time: "02:00:00 GMT-0800 (PST)" },
{ offset: 3 * 60 + 30, date: "Sun Nov 06 2016", time: "02:30:00 GMT-0800 (PST)" },
{ offset: 4 * 60, date: "Sun Nov 06 2016", time: "03:00:00 GMT-0800 (PST)" },
{ offset: 4 * 60 + 30, date: "Sun Nov 06 2016", time: "03:30:00 GMT-0800 (PST)" },
];
for (let {offset, date, time} of tests) {
let dt = new Date(midnight.getTime() + offset * msPerMinute);
assertEq(dt.toString(), `${date} ${time}`);
assertEq(dt.toDateString(), date);
assertEq(dt.toTimeString(), time);
}
}
// PDT -> PST, using local date-time.
{
let tests = [
{ offset: 0 * 60, date: "Sun Nov 06 2016", time: "00:00:00 GMT-0700 (PDT)" },
{ offset: 0 * 60 + 30, date: "Sun Nov 06 2016", time: "00:30:00 GMT-0700 (PDT)" },
{ offset: 1 * 60, date: "Sun Nov 06 2016", time: "01:00:00 GMT-0700 (PDT)" },
{ offset: 1 * 60 + 30, date: "Sun Nov 06 2016", time: "01:30:00 GMT-0700 (PDT)" },
{ offset: 2 * 60, date: "Sun Nov 06 2016", time: "02:00:00 GMT-0800 (PST)" },
{ offset: 2 * 60 + 30, date: "Sun Nov 06 2016", time: "02:30:00 GMT-0800 (PST)" },
{ offset: 3 * 60, date: "Sun Nov 06 2016", time: "03:00:00 GMT-0800 (PST)" },
{ offset: 3 * 60 + 30, date: "Sun Nov 06 2016", time: "03:30:00 GMT-0800 (PST)" },
{ offset: 4 * 60, date: "Sun Nov 06 2016", time: "04:00:00 GMT-0800 (PST)" },
{ offset: 4 * 60 + 30, date: "Sun Nov 06 2016", time: "04:30:00 GMT-0800 (PST)" },
];
for (let {offset, date, time} of tests) {
let dt = new Date(2016, Month.November, 6, (offset / 60)|0, (offset % 60), 0, 0);
assertEq(dt.toString(), `${date} ${time}`);
assertEq(dt.toDateString(), date);
assertEq(dt.toTimeString(), time);
}
}
// PST -> PDT, using milliseconds from epoch.
{
let midnight = new Date(2016, Month.March, 13, 0, 0, 0, 0);
let midnightUTC = Date.UTC(2016, Month.March, 13, 0, 0, 0, 0);
// Ensure midnight time is correct.
assertEq(midnightUTC - midnight.getTime(), -8 * msPerHour);
let tests = [
{ offset: 0 * 60, date: "Sun Mar 13 2016", time: "00:00:00 GMT-0800 (PST)" },
{ offset: 0 * 60 + 30, date: "Sun Mar 13 2016", time: "00:30:00 GMT-0800 (PST)" },
{ offset: 1 * 60, date: "Sun Mar 13 2016", time: "01:00:00 GMT-0800 (PST)" },
{ offset: 1 * 60 + 30, date: "Sun Mar 13 2016", time: "01:30:00 GMT-0800 (PST)" },
{ offset: 2 * 60, date: "Sun Mar 13 2016", time: "03:00:00 GMT-0700 (PDT)" },
{ offset: 2 * 60 + 30, date: "Sun Mar 13 2016", time: "03:30:00 GMT-0700 (PDT)" },
{ offset: 3 * 60, date: "Sun Mar 13 2016", time: "04:00:00 GMT-0700 (PDT)" },
{ offset: 3 * 60 + 30, date: "Sun Mar 13 2016", time: "04:30:00 GMT-0700 (PDT)" },
{ offset: 4 * 60, date: "Sun Mar 13 2016", time: "05:00:00 GMT-0700 (PDT)" },
{ offset: 4 * 60 + 30, date: "Sun Mar 13 2016", time: "05:30:00 GMT-0700 (PDT)" },
];
for (let {offset, date, time} of tests) {
let dt = new Date(midnight.getTime() + offset * msPerMinute);
assertEq(dt.toString(), `${date} ${time}`);
assertEq(dt.toDateString(), date);
assertEq(dt.toTimeString(), time);
}
}
// PST -> PDT, using local date-time.
{
let tests = [
{ offset: 0 * 60, date: "Sun Mar 13 2016", time: "00:00:00 GMT-0800 (PST)" },
{ offset: 0 * 60 + 30, date: "Sun Mar 13 2016", time: "00:30:00 GMT-0800 (PST)" },
{ offset: 1 * 60, date: "Sun Mar 13 2016", time: "01:00:00 GMT-0800 (PST)" },
{ offset: 1 * 60 + 30, date: "Sun Mar 13 2016", time: "01:30:00 GMT-0800 (PST)" },
{ offset: 2 * 60, date: "Sun Mar 13 2016", time: "03:00:00 GMT-0700 (PDT)" },
{ offset: 2 * 60 + 30, date: "Sun Mar 13 2016", time: "03:30:00 GMT-0700 (PDT)" },
{ offset: 3 * 60, date: "Sun Mar 13 2016", time: "03:00:00 GMT-0700 (PDT)" },
{ offset: 3 * 60 + 30, date: "Sun Mar 13 2016", time: "03:30:00 GMT-0700 (PDT)" },
{ offset: 4 * 60, date: "Sun Mar 13 2016", time: "04:00:00 GMT-0700 (PDT)" },
{ offset: 4 * 60 + 30, date: "Sun Mar 13 2016", time: "04:30:00 GMT-0700 (PDT)" },
];
for (let {offset, date, time} of tests) {
let dt = new Date(2016, Month.March, 13, (offset / 60)|0, (offset % 60), 0, 0);
assertEq(dt.toString(), `${date} ${time}`);
assertEq(dt.toDateString(), date);
assertEq(dt.toTimeString(), time);
}
}
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,64 @@
// |reftest| skip-if(xulRuntime.OS=="WINNT"||xulRuntime.OS=="Darwin") -- Skip on OS X in addition to Windows
// Contains the tests from "time-zones.js" which fail on OS X.
const msPerHour = 60 * 60 * 1000;
const Month = {
January: 0,
February: 1,
March: 2,
April: 3,
May: 4,
June: 5,
July: 6,
August: 7,
September: 8,
October: 9,
November: 10,
December: 11,
};
function inTimeZone(tzname, fn) {
setTimeZone(tzname);
try {
fn();
} finally {
setTimeZone(undefined);
}
}
const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].join("|");
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].join("|");
const datePart = String.raw `(?:${weekdays}) (?:${months}) \d{2}`;
const timePart = String.raw `\d{4,6} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}`;
const dateTimeRE = new RegExp(String.raw `^(${datePart} ${timePart})(?: \((.+)\))?$`);
function assertDateTime(date, expected) {
let actual = date.toString();
assertEq(dateTimeRE.test(expected), true, `${expected}`);
assertEq(dateTimeRE.test(actual), true, `${actual}`);
let [, expectedDateTime, expectedTimeZone] = dateTimeRE.exec(expected);
let [, actualDateTime, actualTimeZone] = dateTimeRE.exec(actual);
assertEq(actualDateTime, expectedDateTime);
// The time zone identifier is optional, so only compare its value if it's
// present in |actual| and |expected|.
if (expectedTimeZone !== undefined && actualTimeZone !== undefined) {
assertEq(actualTimeZone, expectedTimeZone);
}
}
// bug 637244
inTimeZone("Asia/Novosibirsk", () => {
let dt1 = new Date(1984, Month.April, 1, -1);
assertDateTime(dt1, "Sat Mar 31 1984 23:00:00 GMT+0700 (NOVT)");
let dt2 = new Date(1984, Month.April, 1);
assertDateTime(dt2, "Sun Apr 01 1984 01:00:00 GMT+0800 (NOVST)");
});
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,198 @@
// |reftest| skip-if(xulRuntime.OS=="WINNT"&&!xulRuntime.shell) -- Windows browser in automation doesn't pick up new time zones correctly
// Repeats the test from "time-zones.js", but uses POSIX instead of IANA names
// for the time zones. This allows to run these tests on Windows, too.
// From bug 1330149:
//
// Windows only supports a very limited set of IANA time zone names for the TZ
// environment variable.
//
// TZ format supported by Windows: "TZ=tzn[+|-]hh[:mm[:ss]][dzn]".
//
// Complete list of all IANA time zone ids matching that format.
//
// From tzdata's "northamerica" file:
// EST5EDT
// CST6CDT
// MST7MDT
// PST8PDT
//
// From tzdata's "backward" file:
// GMT+0
// GMT-0
// GMT0
// Perform the following replacements:
// America/New_York -> EST5EDT
// America/Chicago -> CST6CDT
// America/Denver -> MST7MDT
// America/Los_Angeles -> PST8PDT
//
// And remove any tests not matching one of the four time zones from above.
const msPerHour = 60 * 60 * 1000;
const Month = {
January: 0,
February: 1,
March: 2,
April: 3,
May: 4,
June: 5,
July: 6,
August: 7,
September: 8,
October: 9,
November: 10,
December: 11,
};
function inTimeZone(tzname, fn) {
setTimeZone(tzname);
try {
fn();
} finally {
setTimeZone("PST8PDT");
}
}
const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].join("|");
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].join("|");
const datePart = String.raw `(?:${weekdays}) (?:${months}) \d{2}`;
const timePart = String.raw `\d{4,6} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}`;
const dateTimeRE = new RegExp(String.raw `^(${datePart} ${timePart})(?: \((.+)\))?$`);
function assertDateTime(date, expected) {
let actual = date.toString();
assertEq(dateTimeRE.test(expected), true, `${expected}`);
assertEq(dateTimeRE.test(actual), true, `${actual}`);
let [, expectedDateTime, expectedTimeZone] = dateTimeRE.exec(expected);
let [, actualDateTime, actualTimeZone] = dateTimeRE.exec(actual);
assertEq(actualDateTime, expectedDateTime);
// The time zone identifier is optional, so only compare its value if it's
// present in |actual| and |expected|.
if (expectedTimeZone !== undefined && actualTimeZone !== undefined) {
assertEq(actualTimeZone, expectedTimeZone);
}
}
// bug 294908
inTimeZone("EST5EDT", () => {
let dt = new Date(2003, Month.April, 6, 2, 30, 00);
assertDateTime(dt, "Sun Apr 06 2003 03:30:00 GMT-0400 (EDT)");
});
// bug 610183
inTimeZone("PST8PDT", () => {
let dt = new Date(2014, Month.November, 2, 1, 47, 42);
assertDateTime(dt, "Sun Nov 02 2014 01:47:42 GMT-0700 (PDT)");
});
// bug 629465
inTimeZone("MST7MDT", () => {
let dt1 = new Date(Date.UTC(2015, Month.November, 1, 0, 0, 0) + 6 * msPerHour);
assertDateTime(dt1, "Sun Nov 01 2015 00:00:00 GMT-0600 (MDT)");
let dt2 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 6 * msPerHour);
assertDateTime(dt2, "Sun Nov 01 2015 01:00:00 GMT-0600 (MDT)");
let dt3 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 7 * msPerHour);
assertDateTime(dt3, "Sun Nov 01 2015 01:00:00 GMT-0700 (MST)");
});
// bug 742427
inTimeZone("EST5EDT", () => {
let dt = new Date(2009, Month.March, 8, 1, 0, 0);
assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0500 (EST)");
dt.setHours(dt.getHours() + 1);
assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0400 (EDT)");
});
inTimeZone("MST7MDT", () => {
let dt = new Date(2009, Month.March, 8, 1, 0, 0);
assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0700 (MST)");
dt.setHours(dt.getHours() + 1);
assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0600 (MDT)");
});
inTimeZone("EST5EDT", () => {
let dt1 = new Date(Date.UTC(2008, Month.March, 9, 0, 0, 0) + 5 * msPerHour);
assertDateTime(dt1, "Sun Mar 09 2008 00:00:00 GMT-0500 (EST)");
let dt2 = new Date(Date.UTC(2008, Month.March, 9, 1, 0, 0) + 5 * msPerHour);
assertDateTime(dt2, "Sun Mar 09 2008 01:00:00 GMT-0500 (EST)");
let dt3 = new Date(Date.UTC(2008, Month.March, 9, 4, 0, 0) + 4 * msPerHour);
assertDateTime(dt3, "Sun Mar 09 2008 04:00:00 GMT-0400 (EDT)");
});
// bug 802627
inTimeZone("EST5EDT", () => {
let dt = new Date(0);
assertDateTime(dt, "Wed Dec 31 1969 19:00:00 GMT-0500 (EST)");
});
// bug 879261
inTimeZone("EST5EDT", () => {
let dt1 = new Date(1362891600000);
assertDateTime(dt1, "Sun Mar 10 2013 00:00:00 GMT-0500 (EST)");
let dt2 = new Date(dt1.setHours(dt1.getHours() + 24));
assertDateTime(dt2, "Mon Mar 11 2013 00:00:00 GMT-0400 (EDT)");
});
inTimeZone("PST8PDT", () => {
let dt1 = new Date(2014, Month.January, 1);
assertDateTime(dt1, "Wed Jan 01 2014 00:00:00 GMT-0800 (PST)");
let dt2 = new Date(2014, Month.August, 1);
assertDateTime(dt2, "Fri Aug 01 2014 00:00:00 GMT-0700 (PDT)");
});
inTimeZone("EST5EDT", () => {
let dt1 = new Date(2016, Month.October, 14, 3, 5, 9);
assertDateTime(dt1, "Fri Oct 14 2016 03:05:09 GMT-0400 (EDT)");
let dt2 = new Date(2016, Month.January, 9, 23, 26, 40);
assertDateTime(dt2, "Sat Jan 09 2016 23:26:40 GMT-0500 (EST)");
});
// bug 1084547
inTimeZone("EST5EDT", () => {
let dt = new Date(Date.parse("2014-11-02T02:00:00-04:00"));
assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0500 (EST)");
dt.setMilliseconds(0);
assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0400 (EDT)");
});
// bug 1303306
inTimeZone("EST5EDT", () => {
let dt = new Date(2016, Month.September, 15, 16, 14, 48);
assertDateTime(dt, "Thu Sep 15 2016 16:14:48 GMT-0400 (EDT)");
});
// bug 1317364
inTimeZone("PST8PDT", () => {
let dt = new Date(2016, Month.March, 13, 2, 30, 0, 0);
assertDateTime(dt, "Sun Mar 13 2016 03:30:00 GMT-0700 (PDT)");
let dt2 = new Date(2016, Month.January, 5, 0, 30, 30, 500);
assertDateTime(dt2, "Tue Jan 05 2016 00:30:30 GMT-0800 (PST)");
let dt3 = new Date(dt2.getTime());
dt3.setMonth(dt2.getMonth() + 2);
dt3.setDate(dt2.getDate() + 7 + 1);
dt3.setHours(dt2.getHours() + 2);
assertEq(dt3.getHours(), 3);
});
// bug 1355272
inTimeZone("PST8PDT", () => {
let dt = new Date(2017, Month.April, 10, 17, 25, 07);
assertDateTime(dt, "Mon Apr 10 2017 17:25:07 GMT-0700 (PDT)");
});
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -0,0 +1,310 @@
// |reftest| skip-if(xulRuntime.OS=="WINNT") -- Windows doesn't accept IANA names for the TZ env variable
const msPerHour = 60 * 60 * 1000;
const Month = {
January: 0,
February: 1,
March: 2,
April: 3,
May: 4,
June: 5,
July: 6,
August: 7,
September: 8,
October: 9,
November: 10,
December: 11,
};
function inTimeZone(tzname, fn) {
setTimeZone(tzname);
try {
fn();
} finally {
setTimeZone(undefined);
}
}
const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].join("|");
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].join("|");
const datePart = String.raw `(?:${weekdays}) (?:${months}) \d{2}`;
const timePart = String.raw `\d{4,6} \d{2}:\d{2}:\d{2} GMT[+-]\d{4}`;
const dateTimeRE = new RegExp(String.raw `^(${datePart} ${timePart})(?: \((.+)\))?$`);
function assertDateTime(date, expected) {
let actual = date.toString();
assertEq(dateTimeRE.test(expected), true, `${expected}`);
assertEq(dateTimeRE.test(actual), true, `${actual}`);
let [, expectedDateTime, expectedTimeZone] = dateTimeRE.exec(expected);
let [, actualDateTime, actualTimeZone] = dateTimeRE.exec(actual);
assertEq(actualDateTime, expectedDateTime);
// The time zone identifier is optional, so only compare its value if it's
// present in |actual| and |expected|.
if (expectedTimeZone !== undefined && actualTimeZone !== undefined) {
assertEq(actualTimeZone, expectedTimeZone);
}
}
// bug 158328
inTimeZone("Europe/London", () => {
let dt1 = new Date(2002, Month.July, 19, 16, 10, 55);
assertDateTime(dt1, "Fri Jul 19 2002 16:10:55 GMT+0100 (BST)");
let dt2 = new Date(2009, Month.December, 24, 13, 44, 52);
assertDateTime(dt2, "Thu Dec 24 2009 13:44:52 GMT+0000 (GMT)");
});
// bug 294908
inTimeZone("America/New_York", () => {
let dt = new Date(2003, Month.April, 6, 2, 30, 00);
assertDateTime(dt, "Sun Apr 06 2003 03:30:00 GMT-0400 (EDT)");
});
// bug 610183
inTimeZone("America/Los_Angeles", () => {
let dt = new Date(2014, Month.November, 2, 1, 47, 42);
assertDateTime(dt, "Sun Nov 02 2014 01:47:42 GMT-0700 (PDT)");
});
// bug 629465
inTimeZone("America/Denver", () => {
let dt1 = new Date(Date.UTC(2015, Month.November, 1, 0, 0, 0) + 6 * msPerHour);
assertDateTime(dt1, "Sun Nov 01 2015 00:00:00 GMT-0600 (MDT)");
let dt2 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 6 * msPerHour);
assertDateTime(dt2, "Sun Nov 01 2015 01:00:00 GMT-0600 (MDT)");
let dt3 = new Date(Date.UTC(2015, Month.November, 1, 1, 0, 0) + 7 * msPerHour);
assertDateTime(dt3, "Sun Nov 01 2015 01:00:00 GMT-0700 (MST)");
});
// bug 637244
inTimeZone("Europe/Helsinki", () => {
let dt1 = new Date(2016, Month.March, 27, 2, 59);
assertDateTime(dt1, "Sun Mar 27 2016 02:59:00 GMT+0200 (EET)");
let dt2 = new Date(2016, Month.March, 27, 3, 0);
assertDateTime(dt2, "Sun Mar 27 2016 04:00:00 GMT+0300 (EEST)");
});
// bug 718175
inTimeZone("Europe/London", () => {
let dt = new Date(0);
assertEq(dt.getHours(), 1);
});
// bug 719274
inTimeZone("Pacific/Auckland", () => {
let dt = new Date(2012, Month.January, 19, 12, 54, 27);
assertDateTime(dt, "Thu Jan 19 2012 12:54:27 GMT+1300 (NZDT)");
});
// bug 742427
inTimeZone("Europe/Paris", () => {
let dt1 = new Date(2009, Month.March, 29, 1, 0, 0);
assertDateTime(dt1, "Sun Mar 29 2009 01:00:00 GMT+0100 (CET)");
dt1.setHours(dt1.getHours() + 1);
assertDateTime(dt1, "Sun Mar 29 2009 03:00:00 GMT+0200 (CEST)");
let dt2 = new Date(2010, Month.March, 29, 1, 0, 0);
assertDateTime(dt2, "Mon Mar 29 2010 01:00:00 GMT+0200 (CEST)");
dt2.setHours(dt2.getHours() + 1);
assertDateTime(dt2, "Mon Mar 29 2010 02:00:00 GMT+0200 (CEST)");
});
inTimeZone("America/New_York", () => {
let dt = new Date(2009, Month.March, 8, 1, 0, 0);
assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0500 (EST)");
dt.setHours(dt.getHours() + 1);
assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0400 (EDT)");
});
inTimeZone("America/Denver", () => {
let dt = new Date(2009, Month.March, 8, 1, 0, 0);
assertDateTime(dt, "Sun Mar 08 2009 01:00:00 GMT-0700 (MST)");
dt.setHours(dt.getHours() + 1);
assertDateTime(dt, "Sun Mar 08 2009 03:00:00 GMT-0600 (MDT)");
});
inTimeZone("America/New_York", () => {
let dt1 = new Date(Date.UTC(2008, Month.March, 9, 0, 0, 0) + 5 * msPerHour);
assertDateTime(dt1, "Sun Mar 09 2008 00:00:00 GMT-0500 (EST)");
let dt2 = new Date(Date.UTC(2008, Month.March, 9, 1, 0, 0) + 5 * msPerHour);
assertDateTime(dt2, "Sun Mar 09 2008 01:00:00 GMT-0500 (EST)");
let dt3 = new Date(Date.UTC(2008, Month.March, 9, 4, 0, 0) + 4 * msPerHour);
assertDateTime(dt3, "Sun Mar 09 2008 04:00:00 GMT-0400 (EDT)");
});
inTimeZone("Europe/Paris", () => {
let dt1 = new Date(Date.UTC(2008, Month.March, 30, 0, 0, 0) - 1 * msPerHour);
assertDateTime(dt1, "Sun Mar 30 2008 00:00:00 GMT+0100 (CET)");
let dt2 = new Date(Date.UTC(2008, Month.March, 30, 1, 0, 0) - 1 * msPerHour);
assertDateTime(dt2, "Sun Mar 30 2008 01:00:00 GMT+0100 (CET)");
let dt3 = new Date(Date.UTC(2008, Month.March, 30, 3, 0, 0) - 2 * msPerHour);
assertDateTime(dt3, "Sun Mar 30 2008 03:00:00 GMT+0200 (CEST)");
let dt4 = new Date(Date.UTC(2008, Month.March, 30, 4, 0, 0) - 2 * msPerHour);
assertDateTime(dt4, "Sun Mar 30 2008 04:00:00 GMT+0200 (CEST)");
});
// bug 802627
inTimeZone("America/New_York", () => {
let dt = new Date(0);
assertDateTime(dt, "Wed Dec 31 1969 19:00:00 GMT-0500 (EST)");
});
// bug 819820
inTimeZone("Europe/London", () => {
let dt1 = new Date(Date.UTC(2012, Month.October, 28, 0, 59, 59));
assertDateTime(dt1, "Sun Oct 28 2012 01:59:59 GMT+0100 (BST)");
let dt2 = new Date(Date.UTC(2012, Month.October, 28, 1, 0, 0));
assertDateTime(dt2, "Sun Oct 28 2012 01:00:00 GMT+0000 (GMT)");
let dt3 = new Date(Date.UTC(2012, Month.October, 28, 1, 59, 59));
assertDateTime(dt3, "Sun Oct 28 2012 01:59:59 GMT+0000 (GMT)");
let dt4 = new Date(Date.UTC(2012, Month.October, 28, 2, 0, 0));
assertDateTime(dt4, "Sun Oct 28 2012 02:00:00 GMT+0000 (GMT)");
});
// bug 879261
inTimeZone("America/New_York", () => {
let dt1 = new Date(1362891600000);
assertDateTime(dt1, "Sun Mar 10 2013 00:00:00 GMT-0500 (EST)");
let dt2 = new Date(dt1.setHours(dt1.getHours() + 24));
assertDateTime(dt2, "Mon Mar 11 2013 00:00:00 GMT-0400 (EDT)");
});
inTimeZone("America/Los_Angeles", () => {
let dt1 = new Date(2014, Month.January, 1);
assertDateTime(dt1, "Wed Jan 01 2014 00:00:00 GMT-0800 (PST)");
let dt2 = new Date(2014, Month.August, 1);
assertDateTime(dt2, "Fri Aug 01 2014 00:00:00 GMT-0700 (PDT)");
});
inTimeZone("America/New_York", () => {
let dt1 = new Date(2016, Month.October, 14, 3, 5, 9);
assertDateTime(dt1, "Fri Oct 14 2016 03:05:09 GMT-0400 (EDT)");
let dt2 = new Date(2016, Month.January, 9, 23, 26, 40);
assertDateTime(dt2, "Sat Jan 09 2016 23:26:40 GMT-0500 (EST)");
});
// bug 994086
inTimeZone("Europe/Vienna", () => {
let dt1 = new Date(2014, Month.March, 30, 2, 0);
assertDateTime(dt1, "Sun Mar 30 2014 03:00:00 GMT+0200 (CEST)");
let dt2 = new Date(2014, Month.March, 30, 3, 0);
assertDateTime(dt2, "Sun Mar 30 2014 03:00:00 GMT+0200 (CEST)");
let dt3 = new Date(2014, Month.March, 30, 4, 0);
assertDateTime(dt3, "Sun Mar 30 2014 04:00:00 GMT+0200 (CEST)");
});
// bug 1084434
inTimeZone("America/Sao_Paulo", () => {
let dt = new Date(2014, Month.October, 19);
assertEq(dt.getDate(), 19);
assertEq(dt.getHours(), 1);
assertDateTime(dt, "Sun Oct 19 2014 01:00:00 GMT-0200 (BRST)");
});
// bug 1084547
inTimeZone("America/New_York", () => {
let dt = new Date(Date.parse("2014-11-02T02:00:00-04:00"));
assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0500 (EST)");
dt.setMilliseconds(0);
assertDateTime(dt, "Sun Nov 02 2014 01:00:00 GMT-0400 (EDT)");
});
// bug 1118690
inTimeZone("Europe/London", () => {
let dt = new Date(1965, Month.January, 1);
assertEq(dt.getFullYear(), 1965);
});
// bug 1155096
inTimeZone("Europe/Moscow", () => {
let dt1 = new Date(1981, Month.March, 32);
assertEq(dt1.getDate(), 1);
let dt2 = new Date(1982, Month.March, 32);
assertEq(dt2.getDate(), 1);
let dt3 = new Date(1983, Month.March, 32);
assertEq(dt3.getDate(), 1);
let dt4 = new Date(1984, Month.March, 32);
assertEq(dt4.getDate(), 1);
});
// bug 1284507
inTimeZone("Atlantic/Azores", () => {
let dt1 = new Date(2017, Month.March, 25, 0, 0, 0);
assertDateTime(dt1, "Sat Mar 25 2017 00:00:00 GMT-0100 (AZOT)");
let dt2 = new Date(2016, Month.October, 30, 0, 0, 0);
assertDateTime(dt2, "Sun Oct 30 2016 00:00:00 GMT+0000 (AZOST)");
let dt3 = new Date(2016, Month.October, 30, 23, 0, 0);
assertDateTime(dt3, "Sun Oct 30 2016 23:00:00 GMT-0100 (AZOT)");
});
// bug 1303306
inTimeZone("America/New_York", () => {
let dt = new Date(2016, Month.September, 15, 16, 14, 48);
assertDateTime(dt, "Thu Sep 15 2016 16:14:48 GMT-0400 (EDT)");
});
// bug 1317364
inTimeZone("America/Los_Angeles", () => {
let dt = new Date(2016, Month.March, 13, 2, 30, 0, 0);
assertDateTime(dt, "Sun Mar 13 2016 03:30:00 GMT-0700 (PDT)");
let dt2 = new Date(2016, Month.January, 5, 0, 30, 30, 500);
assertDateTime(dt2, "Tue Jan 05 2016 00:30:30 GMT-0800 (PST)");
let dt3 = new Date(dt2.getTime());
dt3.setMonth(dt2.getMonth() + 2);
dt3.setDate(dt2.getDate() + 7 + 1);
dt3.setHours(dt2.getHours() + 2);
assertEq(dt3.getHours(), 3);
});
// bug 1335818
inTimeZone("Asia/Jerusalem", () => {
let dt1 = new Date(2013, Month.March, 22, 1, 0, 0, 0);
assertDateTime(dt1, "Fri Mar 22 2013 01:00:00 GMT+0200 (IST)");
let dt2 = new Date(2013, Month.March, 22, 2, 0, 0, 0);
assertDateTime(dt2, "Fri Mar 22 2013 02:00:00 GMT+0200 (IST)");
let dt3 = new Date(2013, Month.March, 22, 3, 0, 0, 0);
assertDateTime(dt3, "Fri Mar 22 2013 03:00:00 GMT+0200 (IST)");
let dt4 = new Date(2013, Month.March, 29, 1, 0, 0, 0);
assertDateTime(dt4, "Fri Mar 29 2013 01:00:00 GMT+0200 (IST)");
let dt5 = new Date(2013, Month.March, 29, 2, 0, 0, 0);
assertDateTime(dt5, "Fri Mar 29 2013 03:00:00 GMT+0300 (IDT)");
let dt6 = new Date(2013, Month.March, 29, 3, 0, 0, 0);
assertDateTime(dt6, "Fri Mar 29 2013 03:00:00 GMT+0300 (IDT)");
});
// bug 1355272
inTimeZone("America/Los_Angeles", () => {
let dt = new Date(2017, Month.April, 10, 17, 25, 07);
assertDateTime(dt, "Mon Apr 10 2017 17:25:07 GMT-0700 (PDT)");
});
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -90,8 +90,11 @@ UTCToLocalStandardOffsetSeconds()
currentNoDST = currentMaybeWithDST;
} else {
// If |local| respected DST, we need a time broken down into components
// ignoring DST. Turn off DST in the broken-down time.
local.tm_isdst = 0;
// ignoring DST. Turn off DST in the broken-down time. Create a fresh
// copy of |local|, because mktime() will reset tm_isdst = 1 and will
// adjust tm_hour and tm_hour accordingly.
struct tm localNoDST = local;
localNoDST.tm_isdst = 0;
// Compute a |time_t t| corresponding to the broken-down time with DST
// off. This has boundary-condition issues (for about the duration of
@ -99,7 +102,7 @@ UTCToLocalStandardOffsetSeconds()
// zone. But 1) errors will be transient; 2) locations rarely change
// time zone; and 3) in the absence of an API that provides the time
// zone offset directly, this may be the best we can do.
currentNoDST = mktime(&local);
currentNoDST = mktime(&localNoDST);
if (currentNoDST == time_t(-1))
return 0;
}
@ -177,6 +180,8 @@ js::DateTimeInfo::computeDSTOffsetMilliseconds(int64_t utcSeconds)
if (!ComputeLocalTime(static_cast<time_t>(utcSeconds), &tm))
return 0;
// NB: The offset isn't computed correctly when the standard local offset
// at |utcSeconds| is different from |utcToLocalStandardOffsetSeconds|.
int32_t dayoff = int32_t((utcSeconds + utcToLocalStandardOffsetSeconds) % SecondsPerDay);
int32_t tmoff = tm.tm_sec + (tm.tm_min * SecondsPerMinute) + (tm.tm_hour * SecondsPerHour);
@ -184,6 +189,8 @@ js::DateTimeInfo::computeDSTOffsetMilliseconds(int64_t utcSeconds)
if (diff < 0)
diff += SecondsPerDay;
else if (uint32_t(diff) >= SecondsPerDay)
diff -= SecondsPerDay;
return diff * msPerSecond;
}