mirror of https://github.com/tc39/test262.git
317 lines
10 KiB
JavaScript
317 lines
10 KiB
JavaScript
/*---
|
|
defines: [msPerHour, TZ_ADJUST, UTC_01_JAN_1900, UTC_01_JAN_2000, UTC_29_FEB_2000, UTC_01_JAN_2005, inTimeZone, withLocale, Month, assertDateTime, runDSTOffsetCachingTestsFraction]
|
|
allow_unused: True
|
|
---*/
|
|
|
|
/**
|
|
* Date functions used by tests in Date suite
|
|
*/
|
|
(function(global) {
|
|
const msPerDay = 1000 * 60 * 60 * 24;
|
|
const msPerHour = 1000 * 60 * 60;
|
|
global.msPerHour = msPerHour;
|
|
|
|
// Offset of tester's time zone from UTC.
|
|
const TZ_DIFF = GetRawTimezoneOffset();
|
|
global.TZ_ADJUST = TZ_DIFF * msPerHour;
|
|
|
|
const UTC_01_JAN_1900 = -2208988800000;
|
|
const UTC_01_JAN_2000 = 946684800000;
|
|
const UTC_29_FEB_2000 = UTC_01_JAN_2000 + 31 * msPerDay + 28 * msPerDay;
|
|
const UTC_01_JAN_2005 = UTC_01_JAN_2000 + TimeInYear(2000) + TimeInYear(2001) +
|
|
TimeInYear(2002) + TimeInYear(2003) + TimeInYear(2004);
|
|
global.UTC_01_JAN_1900 = UTC_01_JAN_1900;
|
|
global.UTC_01_JAN_2000 = UTC_01_JAN_2000;
|
|
global.UTC_29_FEB_2000 = UTC_29_FEB_2000;
|
|
global.UTC_01_JAN_2005 = UTC_01_JAN_2005;
|
|
|
|
/*
|
|
* Originally, the test suite used a hard-coded value TZ_DIFF = -8.
|
|
* But that was only valid for testers in the Pacific Standard Time Zone!
|
|
* We calculate the proper number dynamically for any tester. We just
|
|
* have to be careful not to use a date subject to Daylight Savings Time...
|
|
*/
|
|
function GetRawTimezoneOffset() {
|
|
let t1 = new Date(2000, 1, 1).getTimezoneOffset();
|
|
let t2 = new Date(2000, 1 + 6, 1).getTimezoneOffset();
|
|
|
|
// 1) Time zone without daylight saving time.
|
|
// 2) Northern hemisphere with daylight saving time.
|
|
if ((t1 - t2) >= 0)
|
|
return -t1 / 60;
|
|
|
|
// 3) Southern hemisphere with daylight saving time.
|
|
return -t2 / 60;
|
|
}
|
|
|
|
function DaysInYear(y) {
|
|
return y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 366 : 365;
|
|
}
|
|
|
|
function TimeInYear(y) {
|
|
return DaysInYear(y) * msPerDay;
|
|
}
|
|
|
|
function getDefaultTimeZone() {
|
|
var tz = getTimeZone();
|
|
switch (tz) {
|
|
case "EST":
|
|
case "EDT":
|
|
return "EST5EDT";
|
|
|
|
case "CST":
|
|
case "CDT":
|
|
return "CST6CDT";
|
|
|
|
case "MST":
|
|
case "MDT":
|
|
return "MST7MDT";
|
|
|
|
case "PST":
|
|
case "PDT":
|
|
return "PST8PDT";
|
|
|
|
default:
|
|
// Other time zones abbrevations are not supported.
|
|
return tz;
|
|
}
|
|
}
|
|
|
|
function getDefaultLocale() {
|
|
// If the default locale looks like a BCP-47 language tag, return it.
|
|
var locale = global.getDefaultLocale();
|
|
if (locale.match(/^[a-z][a-z0-9\-]+$/i))
|
|
return locale;
|
|
|
|
// Otherwise use undefined to reset to the default locale.
|
|
return undefined;
|
|
}
|
|
|
|
let defaultTimeZone = null;
|
|
let defaultLocale = null;
|
|
|
|
// Run the given test in the requested time zone.
|
|
function inTimeZone(tzname, fn) {
|
|
if (defaultTimeZone === null)
|
|
defaultTimeZone = getDefaultTimeZone();
|
|
|
|
setTimeZone(tzname);
|
|
try {
|
|
fn();
|
|
} finally {
|
|
setTimeZone(defaultTimeZone);
|
|
}
|
|
}
|
|
global.inTimeZone = inTimeZone;
|
|
|
|
// Run the given test with the requested locale.
|
|
function withLocale(locale, fn) {
|
|
if (defaultLocale === null)
|
|
defaultLocale = getDefaultLocale();
|
|
|
|
setDefaultLocale(locale);
|
|
try {
|
|
fn();
|
|
} finally {
|
|
setDefaultLocale(defaultLocale);
|
|
}
|
|
}
|
|
global.withLocale = withLocale;
|
|
|
|
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,
|
|
};
|
|
global.Month = Month;
|
|
|
|
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, ...alternativeTimeZones) {
|
|
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) {
|
|
// Test against the alternative time zone identifiers if necessary.
|
|
if (actualTimeZone !== expectedTimeZone) {
|
|
for (let alternativeTimeZone of alternativeTimeZones) {
|
|
if (actualTimeZone === alternativeTimeZone) {
|
|
expectedTimeZone = alternativeTimeZone;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
assertEq(actualTimeZone, expectedTimeZone);
|
|
}
|
|
}
|
|
global.assertDateTime = assertDateTime;
|
|
})(this);
|
|
|
|
|
|
function runDSTOffsetCachingTestsFraction(part, parts)
|
|
{
|
|
var BUGNUMBER = 563938;
|
|
var summary = 'Cache DST offsets to improve SunSpider score';
|
|
|
|
print(BUGNUMBER + ": " + summary);
|
|
|
|
var MAX_UNIX_TIMET = 2145859200; // "2037-12-31T08:00:00.000Z" (PST8PDT based!)
|
|
var RANGE_EXPANSION_AMOUNT = 30 * 24 * 60 * 60;
|
|
|
|
/**
|
|
* Computes the time zone offset in minutes at the given timestamp.
|
|
*/
|
|
function tzOffsetFromUnixTimestamp(timestamp)
|
|
{
|
|
var d = new Date(NaN);
|
|
d.setTime(timestamp); // local slot = NaN, UTC slot = timestamp
|
|
return d.getTimezoneOffset(); // get UTC, calculate local => diff in minutes
|
|
}
|
|
|
|
/**
|
|
* Clear the DST offset cache, leaving it initialized to include a timestamp
|
|
* completely unlike the provided one (i.e. one very, very far away in time
|
|
* from it). Thus an immediately following lookup for the provided timestamp
|
|
* will cache-miss and compute a clean value.
|
|
*/
|
|
function clearDSTOffsetCache(undesiredTimestamp)
|
|
{
|
|
var opposite = (undesiredTimestamp + MAX_UNIX_TIMET / 2) % MAX_UNIX_TIMET;
|
|
|
|
// Generic purge to known, but not necessarily desired, state
|
|
tzOffsetFromUnixTimestamp(0);
|
|
tzOffsetFromUnixTimestamp(MAX_UNIX_TIMET);
|
|
|
|
// Purge to desired state. Cycle 2x in case opposite or undesiredTimestamp
|
|
// is close to 0 or MAX_UNIX_TIMET.
|
|
tzOffsetFromUnixTimestamp(opposite);
|
|
tzOffsetFromUnixTimestamp(undesiredTimestamp);
|
|
tzOffsetFromUnixTimestamp(opposite);
|
|
tzOffsetFromUnixTimestamp(undesiredTimestamp);
|
|
}
|
|
|
|
function computeCanonicalTZOffset(timestamp)
|
|
{
|
|
clearDSTOffsetCache(timestamp);
|
|
return tzOffsetFromUnixTimestamp(timestamp);
|
|
}
|
|
|
|
var TEST_TIMESTAMPS_SECONDS =
|
|
[
|
|
// Special-ish timestamps
|
|
0,
|
|
RANGE_EXPANSION_AMOUNT,
|
|
MAX_UNIX_TIMET,
|
|
];
|
|
|
|
var ONE_DAY = 24 * 60 * 60;
|
|
var EIGHTY_THREE_HOURS = 83 * 60 * 60;
|
|
var NINETY_EIGHT_HOURS = 98 * 60 * 60;
|
|
function nextIncrement(i)
|
|
{
|
|
return i === EIGHTY_THREE_HOURS ? NINETY_EIGHT_HOURS : EIGHTY_THREE_HOURS;
|
|
}
|
|
|
|
// Now add a long sequence of non-special timestamps, from a fixed range, that
|
|
// overlaps a DST change by "a bit" on each side. 67 days should be enough
|
|
// displacement that we can occasionally exercise the implementation's
|
|
// thirty-day expansion and the DST-offset-change logic. Use two different
|
|
// increments just to be safe and catch something a single increment might not.
|
|
var DST_CHANGE_DATE = 1268553600; // March 14, 2010
|
|
for (var t = DST_CHANGE_DATE - 67 * ONE_DAY,
|
|
i = nextIncrement(NINETY_EIGHT_HOURS),
|
|
end = DST_CHANGE_DATE + 67 * ONE_DAY;
|
|
t < end;
|
|
i = nextIncrement(i), t += i)
|
|
{
|
|
TEST_TIMESTAMPS_SECONDS.push(t);
|
|
}
|
|
|
|
var TEST_TIMESTAMPS =
|
|
TEST_TIMESTAMPS_SECONDS.map(function(v) { return v * 1000; });
|
|
|
|
/**************
|
|
* BEGIN TEST *
|
|
**************/
|
|
|
|
// Compute the correct time zone offsets for all timestamps to be tested.
|
|
var CORRECT_TZOFFSETS = TEST_TIMESTAMPS.map(computeCanonicalTZOffset);
|
|
|
|
// Intentionally and knowingly invoking every single logic path in the cache
|
|
// isn't easy for a human to get right (and know he's gotten it right), so
|
|
// let's do it the easy way: exhaustively try all possible four-date sequences
|
|
// selecting from our array of possible timestamps.
|
|
|
|
var sz = TEST_TIMESTAMPS.length;
|
|
var start = Math.floor((part - 1) / parts * sz);
|
|
var end = Math.floor(part / parts * sz);
|
|
|
|
print("Exhaustively testing timestamps " +
|
|
"[" + start + ", " + end + ") of " + sz + "...");
|
|
|
|
try
|
|
{
|
|
for (var i = start; i < end; i++)
|
|
{
|
|
print("Testing timestamp " + i + "...");
|
|
|
|
var t1 = TEST_TIMESTAMPS[i];
|
|
for (var j = 0; j < sz; j++)
|
|
{
|
|
var t2 = TEST_TIMESTAMPS[j];
|
|
for (var k = 0; k < sz; k++)
|
|
{
|
|
var t3 = TEST_TIMESTAMPS[k];
|
|
for (var w = 0; w < sz; w++)
|
|
{
|
|
var t4 = TEST_TIMESTAMPS[w];
|
|
|
|
clearDSTOffsetCache(t1);
|
|
|
|
var tzo1 = tzOffsetFromUnixTimestamp(t1);
|
|
var tzo2 = tzOffsetFromUnixTimestamp(t2);
|
|
var tzo3 = tzOffsetFromUnixTimestamp(t3);
|
|
var tzo4 = tzOffsetFromUnixTimestamp(t4);
|
|
|
|
assertEq(tzo1, CORRECT_TZOFFSETS[i]);
|
|
assertEq(tzo2, CORRECT_TZOFFSETS[j]);
|
|
assertEq(tzo3, CORRECT_TZOFFSETS[k]);
|
|
assertEq(tzo4, CORRECT_TZOFFSETS[w]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (e)
|
|
{
|
|
assertEq(true, false,
|
|
"Error when testing with timestamps " +
|
|
i + ", " + j + ", " + k + ", " + w +
|
|
" (" + t1 + ", " + t2 + ", " + t3 + ", " + t4 + ")!");
|
|
}
|
|
|
|
reportCompare(true, true);
|
|
print("All tests passed!");
|
|
}
|