1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
* License, v. 2.0. If a copy of the MPL was not distributed with this
4
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8
* Date functions used by tests in Date suite
11
var msPerDay = 86400000;
13
var MinutesPerHour = 60;
14
var SecondsPerMinute = 60;
15
var msPerSecond = 1000;
16
var msPerMinute = 60000; // msPerSecond * SecondsPerMinute
17
var msPerHour = 3600000; // msPerMinute * MinutesPerHour
18
var TZ_DIFF = getTimeZoneDiff(); // offset of tester's timezone from UTC
19
var TZ_ADJUST = TZ_DIFF * msPerHour;
20
var TZ_PST = -8; // offset of Pacific Standard Time from UTC
21
var PST_DIFF = TZ_DIFF - TZ_PST; // offset of tester's timezone from PST
23
var TIME_2000 = 946684800000;
24
var TIME_1900 = -2208988800000;
25
var UTC_29_FEB_2000 = TIME_2000 + 31*msPerDay + 28*msPerDay;
26
var UTC_1_JAN_2005 = TIME_2000 + TimeInYear(2000) + TimeInYear(2001) +
27
TimeInYear(2002) + TimeInYear(2003) + TimeInYear(2004);
29
var TIME_NOW = now.valueOf(); //valueOf() is to accurate to the millisecond
30
//Date.parse() is accurate only to the second
33
* Originally, the test suite used a hard-coded value TZ_DIFF = -8.
34
* But that was only valid for testers in the Pacific Standard Time Zone!
35
* We calculate the proper number dynamically for any tester. We just
36
* have to be careful not to use a date subject to Daylight Savings Time...
38
function getTimeZoneDiff()
40
return -((new Date(2000, 1, 1)).getTimezoneOffset())/60;
45
* Date test "ResultArrays" are hard-coded for Pacific Standard Time.
46
* We must adjust them for the tester's own timezone -
48
function adjustResultArray(ResultArray, msMode)
50
// If the tester's system clock is in PST, no need to continue -
51
if (!PST_DIFF) {return;}
53
/* The date testcases instantiate Date objects in two different ways:
55
* millisecond mode: e.g. dt = new Date(10000000);
56
* year-month-day mode: dt = new Date(2000, 5, 1, ...);
58
* In the first case, the date is measured from Time 0 in Greenwich (i.e. UTC).
59
* In the second case, it is measured with reference to the tester's local timezone.
61
* In the first case we must correct those values expected for local measurements,
62
* like dt.getHours() etc. No correction is necessary for dt.getUTCHours() etc.
64
* In the second case, it is exactly the other way around -
68
// The hard-coded UTC milliseconds from Time 0 derives from a UTC date.
69
// Shift to the right by the offset between UTC and the tester.
70
var t = ResultArray[TIME] + TZ_DIFF*msPerHour;
72
// Use our date arithmetic functions to determine the local hour, day, etc.
73
ResultArray[HOURS] = HourFromTime(t);
74
ResultArray[DAY] = WeekDay(t);
75
ResultArray[DATE] = DateFromTime(t);
76
ResultArray[MONTH] = MonthFromTime(t);
77
ResultArray[YEAR] = YearFromTime(t);
81
// The hard-coded UTC milliseconds from Time 0 derives from a PST date.
82
// Shift to the left by the offset between PST and the tester.
83
var t = ResultArray[TIME] - PST_DIFF*msPerHour;
85
// Use our date arithmetic functions to determine the UTC hour, day, etc.
86
ResultArray[TIME] = t;
87
ResultArray[UTC_HOURS] = HourFromTime(t);
88
ResultArray[UTC_DAY] = WeekDay(t);
89
ResultArray[UTC_DATE] = DateFromTime(t);
90
ResultArray[UTC_MONTH] = MonthFromTime(t);
91
ResultArray[UTC_YEAR] = YearFromTime(t);
97
return ( Math.floor(t/msPerDay ) );
99
function DaysInYear( y ) {
103
if ( (y % 4 == 0) && (y % 100 != 0) ) {
106
if ( (y % 100 == 0) && (y % 400 != 0) ) {
109
if ( (y % 400 == 0) ){
112
return "ERROR: DaysInYear(" + y + ") case not covered";
115
function TimeInYear( y ) {
116
return ( DaysInYear(y) * msPerDay );
118
function DayNumber( t ) {
119
return ( Math.floor( t / msPerDay ) );
121
function TimeWithinDay( t ) {
123
var r = t % msPerDay;
132
function YearNumber( t ) {
134
function TimeFromYear( y ) {
135
return ( msPerDay * DayFromYear(y) );
137
function DayFromYear( y ) {
138
return ( 365*(y-1970) +
139
Math.floor((y-1969)/4) -
140
Math.floor((y-1901)/100) +
141
Math.floor((y-1601)/400) );
143
function InLeapYear( t ) {
144
if ( DaysInYear(YearFromTime(t)) == 365 ) {
147
if ( DaysInYear(YearFromTime(t)) == 366 ) {
150
return "ERROR: InLeapYear("+ t + ") case not covered";
153
function YearFromTime( t ) {
155
var sign = ( t < 0 ) ? -1 : 1;
156
var year = ( sign < 0 ) ? 1969 : 1970;
157
for ( var timeToTimeZero = t; ; ) {
158
// subtract the current year's time from the time that's left.
159
timeToTimeZero -= sign * TimeInYear(year)
161
// if there's less than the current year's worth of time left, then break.
163
if ( sign * timeToTimeZero <= 0 ) {
169
if ( sign * timeToTimeZero < 0 ) {
178
function MonthFromTime( t ) {
179
// i know i could use switch but i'd rather not until it's part of ECMA
180
var day = DayWithinYear( t );
181
var leap = InLeapYear(t);
183
if ( (0 <= day) && (day < 31) ) {
186
if ( (31 <= day) && (day < (59+leap)) ) {
189
if ( ((59+leap) <= day) && (day < (90+leap)) ) {
192
if ( ((90+leap) <= day) && (day < (120+leap)) ) {
195
if ( ((120+leap) <= day) && (day < (151+leap)) ) {
198
if ( ((151+leap) <= day) && (day < (181+leap)) ) {
201
if ( ((181+leap) <= day) && (day < (212+leap)) ) {
204
if ( ((212+leap) <= day) && (day < (243+leap)) ) {
207
if ( ((243+leap) <= day) && (day < (273+leap)) ) {
210
if ( ((273+leap) <= day) && (day < (304+leap)) ) {
213
if ( ((304+leap) <= day) && (day < (334+leap)) ) {
216
if ( ((334+leap) <= day) && (day < (365+leap)) ) {
219
return "ERROR: MonthFromTime("+t+") not known";
222
function DayWithinYear( t ) {
223
return( Day(t) - DayFromYear(YearFromTime(t)));
225
function DateFromTime( t ) {
226
var day = DayWithinYear(t);
227
var month = MonthFromTime(t);
236
return ( day - 58 - InLeapYear(t) );
239
return ( day - 89 - InLeapYear(t));
242
return ( day - 119 - InLeapYear(t));
245
return ( day - 150- InLeapYear(t));
248
return ( day - 180- InLeapYear(t));
251
return ( day - 211- InLeapYear(t));
254
return ( day - 242- InLeapYear(t));
257
return ( day - 272- InLeapYear(t));
260
return ( day - 303- InLeapYear(t));
263
return ( day - 333- InLeapYear(t));
266
return ("ERROR: DateFromTime("+t+") not known" );
268
function WeekDay( t ) {
269
var weekday = (Day(t)+4) % 7;
270
return( weekday < 0 ? 7 + weekday : weekday );
273
// missing daylight savings time adjustment
275
function HourFromTime( t ) {
276
var h = Math.floor( t / msPerHour ) % HoursPerDay;
277
return ( (h<0) ? HoursPerDay + h : h );
279
function MinFromTime( t ) {
280
var min = Math.floor( t / msPerMinute ) % MinutesPerHour;
281
return( ( min < 0 ) ? MinutesPerHour + min : min );
283
function SecFromTime( t ) {
284
var sec = Math.floor( t / msPerSecond ) % SecondsPerMinute;
285
return ( (sec < 0 ) ? SecondsPerMinute + sec : sec );
287
function msFromTime( t ) {
288
var ms = t % msPerSecond;
289
return ( (ms < 0 ) ? msPerSecond + ms : ms );
291
function LocalTZA() {
292
return ( TZ_DIFF * msPerHour );
295
return ( t - LocalTZA() - DaylightSavingTA(t - LocalTZA()) );
298
function DaylightSavingTA( t ) {
301
var dst_start = GetDSTStart(t);
302
var dst_end = GetDSTEnd(t);
304
if ( t >= dst_start && t < dst_end )
310
function GetFirstSundayInMonth( t, m ) {
311
var year = YearFromTime(t);
312
var leap = InLeapYear(t);
318
// set time to first day of month m
319
var time = TimeFromYear(year);
320
for (var i = 0; i < m; ++i)
322
time += TimeInMonth(i, leap);
325
for ( var first_sunday = time; WeekDay(first_sunday) > 0;
326
first_sunday += msPerDay )
334
function GetLastSundayInMonth( t, m ) {
335
var year = YearFromTime(t);
336
var leap = InLeapYear(t);
342
// first day of following month
343
var time = TimeFromYear(year);
344
for (var i = 0; i <= m; ++i)
346
time += TimeInMonth(i, leap);
348
// prev day == last day of month
351
for ( var last_sunday = time; WeekDay(last_sunday) > 0;
352
last_sunday -= msPerDay )
360
15.9.1.9 Daylight Saving Time Adjustment
362
The implementation of ECMAScript should not try to determine whether
363
the exact time was subject to daylight saving time, but just whether
364
daylight saving time would have been in effect if the current
365
daylight saving time algorithm had been used at the time. This avoids
366
complications such as taking into account the years that the locale
367
observed daylight saving time year round.
373
Before 2007, DST starts first Sunday in April at 2 AM and ends last
374
Sunday in October at 2 AM
376
Starting in 2007, DST starts second Sunday in March at 2 AM and ends
377
first Sunday in November at 2 AM
379
Note that different operating systems behave differently.
381
Fully patched Windows XP uses the 2007 algorithm for all dates while
382
fully patched Fedora Core 6 and RHEL 4 Linux use the algorithm in
385
Since pre-2007 DST is a subset of 2007 DST rules, this only affects
386
tests that occur in the period Mar-Apr and Oct-Nov where the two
387
algorithms do not agree.
391
function GetDSTStart( t )
393
return (GetFirstSundayInMonth(t, 2) + 7*msPerDay + 2*msPerHour - LocalTZA());
396
function GetDSTEnd( t )
398
return (GetFirstSundayInMonth(t, 10) + 2*msPerHour - LocalTZA());
401
function GetOldDSTStart( t )
403
return (GetFirstSundayInMonth(t, 3) + 2*msPerHour - LocalTZA());
406
function GetOldDSTEnd( t )
408
return (GetLastSundayInMonth(t, 9) + 2*msPerHour - LocalTZA());
411
function LocalTime( t ) {
412
return ( t + LocalTZA() + DaylightSavingTA(t) );
414
function MakeTime( hour, min, sec, ms ) {
415
if ( isNaN( hour ) || isNaN( min ) || isNaN( sec ) || isNaN( ms ) ) {
419
hour = ToInteger(hour);
420
min = ToInteger( min);
421
sec = ToInteger( sec);
422
ms = ToInteger( ms );
424
return( (hour*msPerHour) + (min*msPerMinute) +
425
(sec*msPerSecond) + ms );
427
function MakeDay( year, month, date ) {
428
if ( isNaN(year) || isNaN(month) || isNaN(date) ) {
431
year = ToInteger(year);
432
month = ToInteger(month);
433
date = ToInteger(date );
435
var sign = ( year < 1970 ) ? -1 : 1;
436
var t = ( year < 1970 ) ? 1 : 0;
437
var y = ( year < 1970 ) ? 1969 : 1970;
439
var result5 = year + Math.floor( month/12 );
440
var result6 = month % 12;
443
for ( y = 1969; y >= year; y += sign ) {
444
t += sign * TimeInYear(y);
447
for ( y = 1970 ; y < year; y += sign ) {
448
t += sign * TimeInYear(y);
452
var leap = InLeapYear( t );
454
for ( var m = 0; m < month; m++ ) {
455
t += TimeInMonth( m, leap );
458
if ( YearFromTime(t) != result5 ) {
461
if ( MonthFromTime(t) != result6 ) {
464
if ( DateFromTime(t) != 1 ) {
468
return ( (Day(t)) + date - 1 );
470
function TimeInMonth( month, leap ) {
471
// september april june november
472
// jan 0 feb 1 mar 2 apr 3 may 4 june 5 jul 6
473
// aug 7 sep 8 oct 9 nov 10 dec 11
475
if ( month == 3 || month == 5 || month == 8 || month == 10 ) {
476
return ( 30*msPerDay );
480
if ( month == 0 || month == 2 || month == 4 || month == 6 ||
481
month == 7 || month == 9 || month == 11 ) {
482
return ( 31*msPerDay );
486
return ( (leap == 0) ? 28*msPerDay : 29*msPerDay );
488
function MakeDate( day, time ) {
489
if ( day == Number.POSITIVE_INFINITY ||
490
day == Number.NEGATIVE_INFINITY ) {
493
if ( time == Number.POSITIVE_INFINITY ||
494
time == Number.NEGATIVE_INFINITY ) {
497
return ( day * msPerDay ) + time;
499
function TimeClip( t ) {
501
return ( Number.NaN );
503
if ( Math.abs( t ) > 8.64e15 ) {
504
return ( Number.NaN );
507
return ( ToInteger( t ) );
509
function ToInteger( t ) {
513
return ( Number.NaN );
515
if ( t == 0 || t == -0 ||
516
t == Number.POSITIVE_INFINITY || t == Number.NEGATIVE_INFINITY ) {
520
var sign = ( t < 0 ) ? -1 : 1;
522
return ( sign * Math.floor( Math.abs( t ) ) );
524
function Enumerate ( o ) {
527
print( p +": " + o[p] );