diff options
Diffstat (limited to 'js/src/tests/non262/Date/shell.js')
-rw-r--r-- | js/src/tests/non262/Date/shell.js | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/js/src/tests/non262/Date/shell.js b/js/src/tests/non262/Date/shell.js new file mode 100644 index 0000000000..9246c1eaaa --- /dev/null +++ b/js/src/tests/non262/Date/shell.js @@ -0,0 +1,311 @@ +/** + * 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!"); +} |