/*--- { "slow": true } ---*/ var ms_day = 24 * 60 * 60 * 1000; var days_in_month_noleap = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; var days_in_month_leap = [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; function isLeapYear(x) { if (Math.floor(x) !== x) { throw new Error('unexpected input: ' + x); } if ((x % 4) != 0) { return false; } if ((x % 100) != 0) { return true; } if ((x % 400) != 0) { return false; } return true; } /*=== brute force conversion test ===*/ /* Brute force back and forth conversion test for each start-of-month * for years in the range [-10000, 10000]. * * Testing the entire E5 valid range (+/- 100e6 days from Jan 1, 1970) * takes ~4 seconds V8, but with Duktape running under valgrind it * takes way too long, so we only test a shorter range here and rely * on spot checks in other parts of the range. */ function bruteForceConversionTest() { var timeval; var year, month; var dt; var isLeap; var dayNumber; var dayLimit; function msg(x) { return 'brute force conversion test failed: ' + x + ' (year=' + year + ', month=' + month + ', dayNumber=' + dayNumber + ', timeval=' + timeval + ')'; } function testYearAndMonth() { // Date from UTC breakdown; note that using Date.UTC() is a bad idea // because it assumes years 0-99 mean years 1900-1999. // // dt = new Date(Date.UTC(year, month /*zero-based month*/, 1, 0, 0, 0, 0)); // // Also note that if you set fields individually, and any intermediate // timestamp is out of 'E5 range' (i.e. will cause TimeClip() to return // a NaN), the internal time value will become NaN and won't recover to a // valid value even if another setter call would make the time valid again. // To avoid this issue, set year last. /* This would be one alternative (slower): dt = new Date(0); dt.setUTCMilliseconds(0); dt.setUTCSeconds(0); dt.setUTCMinutes(0); dt.setUTCHours(0); dt.setUTCDate(1); dt.setUTCMonth(month); dt.setUTCFullYear(year); */ dt = new Date(Date.UTC(year, month /*zero-based month*/, 1, 0, 0, 0, 0)); dt.setUTCFullYear(year); // fix Date if year in [0,99] if (dt.getTime() !== timeval) { throw new Error(msg('getTime !== timeVal: ' + dt.getTime() + ' vs ' + timeval)); } if (dt.getUTCFullYear() !== year) { throw new Error(msg('getUTCFullYear !== year')); } if (dt.getUTCMonth() !== month) { throw new Error(msg('getUTCMonth !== month')); } if (dt.getUTCDate() !== 1) { throw new Error(msg('getUTCDate !== 1')); } } timeval = 0; dayNumber = 0; dayLimit = 2933263; // Mon, 01 Jan 10001 00:00:00 GMT year = 1970; month = 0; for (;;) { if (dayNumber > dayLimit) { break; } testYearAndMonth(); isLeap = isLeapYear(year); timeval += (isLeap ? days_in_month_leap : days_in_month_noleap)[month] * ms_day; dayNumber += (isLeap ? days_in_month_leap : days_in_month_noleap)[month]; month++; if (month == 12) { year++; month = 0; } } timeval = 0; dayNumber = 0; dayLimit = -4371953; // Sat, 01 Jan -10000 00:00:00 GMT year = 1970; month = 0; for (;;) { if (dayNumber < dayLimit) { break; } testYearAndMonth(); month--; if (month == -1) { year--; month = 11; } isLeap = isLeapYear(year); timeval -= (isLeap ? days_in_month_leap : days_in_month_noleap)[month] * ms_day; dayNumber -= (isLeap ? days_in_month_leap : days_in_month_noleap)[month]; } } try { print('brute force conversion test'); bruteForceConversionTest(); } catch (e) { print(e.name); }