blob: 2ffd006024d0c629406fbf1ac07dd590e03d4122 [file] [log] [blame]
Andrei Popescu31002712010-02-23 13:46:05 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29// This file relies on the fact that the following declarations have been made
30// in v8natives.js:
31// const $isFinite = GlobalIsFinite;
32
33// -------------------------------------------------------------------
34
35// This file contains date support implemented in JavaScript.
36
37
38// Keep reference to original values of some global properties. This
39// has the added benefit that the code in this file is isolated from
40// changes to these properties.
41const $Date = global.Date;
42
43// Helper function to throw error.
44function ThrowDateTypeError() {
45 throw new $TypeError('this is not a Date object.');
46}
47
48// ECMA 262 - 5.2
49function Modulo(value, remainder) {
50 var mod = value % remainder;
51 // Guard against returning -0.
52 if (mod == 0) return 0;
53 return mod >= 0 ? mod : mod + remainder;
54}
55
56
57function TimeWithinDay(time) {
58 return Modulo(time, msPerDay);
59}
60
61
62// ECMA 262 - 15.9.1.3
63function DaysInYear(year) {
64 if (year % 4 != 0) return 365;
65 if ((year % 100 == 0) && (year % 400 != 0)) return 365;
66 return 366;
67}
68
69
70function DayFromYear(year) {
71 return 365 * (year-1970)
72 + FLOOR((year-1969)/4)
73 - FLOOR((year-1901)/100)
74 + FLOOR((year-1601)/400);
75}
76
77
78function TimeFromYear(year) {
79 return msPerDay * DayFromYear(year);
80}
81
82
83function InLeapYear(time) {
84 return DaysInYear(YEAR_FROM_TIME(time)) == 366 ? 1 : 0;
85}
86
87
88function DayWithinYear(time) {
89 return DAY(time) - DayFromYear(YEAR_FROM_TIME(time));
90}
91
92
93// ECMA 262 - 15.9.1.9
94function EquivalentYear(year) {
95 // Returns an equivalent year in the range [2008-2035] matching
96 // - leap year.
97 // - week day of first day.
98 var time = TimeFromYear(year);
99 var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) +
100 (WeekDay(time) * 12) % 28;
101 // Find the year in the range 2008..2037 that is equivalent mod 28.
102 // Add 3*28 to give a positive argument to the modulus operator.
103 return 2008 + (recent_year + 3*28 - 2008) % 28;
104}
105
106
107function EquivalentTime(t) {
108 // The issue here is that some library calls don't work right for dates
109 // that cannot be represented using a non-negative signed 32 bit integer
110 // (measured in whole seconds based on the 1970 epoch).
111 // We solve this by mapping the time to a year with same leap-year-ness
112 // and same starting day for the year. The ECMAscript specification says
113 // we must do this, but for compatibility with other browsers, we use
114 // the actual year if it is in the range 1970..2037
115 if (t >= 0 && t <= 2.1e12) return t;
116 var day = MakeDay(EquivalentYear(YEAR_FROM_TIME(t)), MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
117 return TimeClip(MakeDate(day, TimeWithinDay(t)));
118}
119
120
121// Because computing the DST offset is a pretty expensive operation
122// we keep a cache of last computed offset along with a time interval
123// where we know the cache is valid.
124var DST_offset_cache = {
125 // Cached DST offset.
126 offset: 0,
127 // Time interval where the cached offset is valid.
128 start: 0, end: -1,
129 // Size of next interval expansion.
130 increment: 0
131};
132
133
134// NOTE: The implementation relies on the fact that no time zones have
135// more than one daylight savings offset change per month.
136// If this function is called with NaN it returns NaN.
137function DaylightSavingsOffset(t) {
138 // Load the cache object from the builtins object.
139 var cache = DST_offset_cache;
140
141 // Cache the start and the end in local variables for fast access.
142 var start = cache.start;
143 var end = cache.end;
144
145 if (start <= t) {
146 // If the time fits in the cached interval, return the cached offset.
147 if (t <= end) return cache.offset;
148
149 // Compute a possible new interval end.
150 var new_end = end + cache.increment;
151
152 if (t <= new_end) {
153 var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end));
154 if (cache.offset == end_offset) {
155 // If the offset at the end of the new interval still matches
156 // the offset in the cache, we grow the cached time interval
157 // and return the offset.
158 cache.end = new_end;
159 cache.increment = msPerMonth;
160 return end_offset;
161 } else {
162 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
163 if (offset == end_offset) {
164 // The offset at the given time is equal to the offset at the
165 // new end of the interval, so that means that we've just skipped
166 // the point in time where the DST offset change occurred. Updated
167 // the interval to reflect this and reset the increment.
168 cache.start = t;
169 cache.end = new_end;
170 cache.increment = msPerMonth;
171 } else {
172 // The interval contains a DST offset change and the given time is
173 // before it. Adjust the increment to avoid a linear search for
174 // the offset change point and change the end of the interval.
175 cache.increment /= 3;
176 cache.end = t;
177 }
178 // Update the offset in the cache and return it.
179 cache.offset = offset;
180 return offset;
181 }
182 }
183 }
184
185 // Compute the DST offset for the time and shrink the cache interval
186 // to only contain the time. This allows fast repeated DST offset
187 // computations for the same time.
188 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
189 cache.offset = offset;
190 cache.start = cache.end = t;
191 cache.increment = msPerMonth;
192 return offset;
193}
194
195
196var timezone_cache_time = $NaN;
197var timezone_cache_timezone;
198
199function LocalTimezone(t) {
200 if (NUMBER_IS_NAN(t)) return "";
201 if (t == timezone_cache_time) {
202 return timezone_cache_timezone;
203 }
204 var timezone = %DateLocalTimezone(EquivalentTime(t));
205 timezone_cache_time = t;
206 timezone_cache_timezone = timezone;
207 return timezone;
208}
209
210
211function WeekDay(time) {
212 return Modulo(DAY(time) + 4, 7);
213}
214
215var local_time_offset = %DateLocalTimeOffset();
216
217function LocalTime(time) {
218 if (NUMBER_IS_NAN(time)) return time;
219 return time + local_time_offset + DaylightSavingsOffset(time);
220}
221
222function LocalTimeNoCheck(time) {
223 // Inline the DST offset cache checks for speed.
224 var cache = DST_offset_cache;
225 if (cache.start <= time && time <= cache.end) {
226 var dst_offset = cache.offset;
227 } else {
228 var dst_offset = DaylightSavingsOffset(time);
229 }
230 return time + local_time_offset + dst_offset;
231}
232
233
234function UTC(time) {
235 if (NUMBER_IS_NAN(time)) return time;
236 var tmp = time - local_time_offset;
237 return tmp - DaylightSavingsOffset(tmp);
238}
239
240
241// ECMA 262 - 15.9.1.11
242function MakeTime(hour, min, sec, ms) {
243 if (!$isFinite(hour)) return $NaN;
244 if (!$isFinite(min)) return $NaN;
245 if (!$isFinite(sec)) return $NaN;
246 if (!$isFinite(ms)) return $NaN;
247 return TO_INTEGER(hour) * msPerHour
248 + TO_INTEGER(min) * msPerMinute
249 + TO_INTEGER(sec) * msPerSecond
250 + TO_INTEGER(ms);
251}
252
253
254// ECMA 262 - 15.9.1.12
255function TimeInYear(year) {
256 return DaysInYear(year) * msPerDay;
257}
258
259
260// Compute modified Julian day from year, month, date.
261function ToJulianDay(year, month, date) {
262 var jy = (month > 1) ? year : year - 1;
263 var jm = (month > 1) ? month + 2 : month + 14;
264 var ja = FLOOR(jy / 100);
265 return FLOOR(FLOOR(365.25*jy) + FLOOR(30.6001*jm) + date + 1720995) + 2 - ja + FLOOR(0.25*ja);
266}
267
268var four_year_cycle_table = CalculateDateTable();
269
270
271function CalculateDateTable() {
272 var month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
273 var four_year_cycle_table = new $Array(1461);
274
275 var cumulative = 0;
276 var position = 0;
277 var leap_position = 0;
278 for (var month = 0; month < 12; month++) {
279 var month_bits = month << kMonthShift;
280 var length = month_lengths[month];
281 for (var day = 1; day <= length; day++) {
282 four_year_cycle_table[leap_position] =
283 month_bits + day;
284 four_year_cycle_table[366 + position] =
285 (1 << kYearShift) + month_bits + day;
286 four_year_cycle_table[731 + position] =
287 (2 << kYearShift) + month_bits + day;
288 four_year_cycle_table[1096 + position] =
289 (3 << kYearShift) + month_bits + day;
290 leap_position++;
291 position++;
292 }
293 if (month == 1) {
294 four_year_cycle_table[leap_position++] = month_bits + 29;
295 }
296 }
297 return four_year_cycle_table;
298}
299
300
301// Constructor for creating objects holding year, month, and date.
302// Introduced to ensure the two return points in FromJulianDay match same map.
303function DayTriplet(year, month, date) {
304 this.year = year;
305 this.month = month;
306 this.date = date;
307}
308
309var julian_day_cache_triplet;
310var julian_day_cache_day = $NaN;
311
312// Compute year, month, and day from modified Julian day.
313// The missing days in 1582 are ignored for JavaScript compatibility.
314function FromJulianDay(julian) {
315 if (julian_day_cache_day == julian) {
316 return julian_day_cache_triplet;
317 }
318 var result;
319 // Avoid floating point and non-Smi maths in common case. This is also a period of
320 // time where leap years are very regular. The range is not too large to avoid overflow
321 // when doing the multiply-to-divide trick.
322 if (julian > kDayZeroInJulianDay &&
323 (julian - kDayZeroInJulianDay) < 40177) { // 1970 - 2080
324 var jsimple = (julian - kDayZeroInJulianDay) + 731; // Day 0 is 1st January 1968
325 var y = 1968;
326 // Divide by 1461 by multiplying with 22967 and shifting down by 25!
327 var after_1968 = (jsimple * 22967) >> 25;
328 y += after_1968 << 2;
329 jsimple -= 1461 * after_1968;
330 var four_year_cycle = four_year_cycle_table[jsimple];
331 result = new DayTriplet(y + (four_year_cycle >> kYearShift),
332 (four_year_cycle & kMonthMask) >> kMonthShift,
333 four_year_cycle & kDayMask);
334 } else {
335 var jalpha = FLOOR((julian - 1867216.25) / 36524.25);
336 var jb = julian + 1 + jalpha - FLOOR(0.25 * jalpha) + 1524;
337 var jc = FLOOR(6680.0 + ((jb-2439870) - 122.1)/365.25);
338 var jd = FLOOR(365 * jc + (0.25 * jc));
339 var je = FLOOR((jb - jd)/30.6001);
340 var m = je - 1;
341 if (m > 12) m -= 13;
342 var y = jc - 4715;
343 if (m > 2) { --y; --m; }
344 var d = jb - jd - FLOOR(30.6001 * je);
345 result = new DayTriplet(y, m, d);
346 }
347 julian_day_cache_day = julian;
348 julian_day_cache_triplet = result;
349 return result;
350}
351
352
353// Compute number of days given a year, month, date.
354// Note that month and date can lie outside the normal range.
355// For example:
356// MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
357// MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
358// MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
359function MakeDay(year, month, date) {
360 if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
361
362 // Conversion to integers.
363 year = TO_INTEGER(year);
364 month = TO_INTEGER(month);
365 date = TO_INTEGER(date);
366
367 // Overflow months into year.
368 year = year + FLOOR(month/12);
369 month = month % 12;
370 if (month < 0) {
371 month += 12;
372 }
373
374 // Return days relative to Jan 1 1970.
375 return ToJulianDay(year, month, date) - kDayZeroInJulianDay;
376}
377
378
379// ECMA 262 - 15.9.1.13
380function MakeDate(day, time) {
381 if (!$isFinite(day)) return $NaN;
382 if (!$isFinite(time)) return $NaN;
383 return day * msPerDay + time;
384}
385
386
387// ECMA 262 - 15.9.1.14
388function TimeClip(time) {
389 if (!$isFinite(time)) return $NaN;
390 if ($abs(time) > 8.64E15) return $NaN;
391 return TO_INTEGER(time);
392}
393
394
395// The Date cache is used to limit the cost of parsing the same Date
396// strings over and over again.
397var Date_cache = {
398 // Cached time value.
399 time: $NaN,
400 // Cached year when interpreting the time as a local time. Only
401 // valid when the time matches cached time.
402 year: $NaN,
403 // String input for which the cached time is valid.
404 string: null
405};
406
407
408%SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) {
409 if (!%_IsConstructCall()) {
410 // ECMA 262 - 15.9.2
411 return (new $Date()).toString();
412 }
413
414 // ECMA 262 - 15.9.3
415 var argc = %_ArgumentsLength();
416 var value;
417 if (argc == 0) {
418 value = %DateCurrentTime();
419
420 } else if (argc == 1) {
421 if (IS_NUMBER(year)) {
422 value = TimeClip(year);
423
424 } else if (IS_STRING(year)) {
425 // Probe the Date cache. If we already have a time value for the
426 // given time, we re-use that instead of parsing the string again.
427 var cache = Date_cache;
428 if (cache.string === year) {
429 value = cache.time;
430 } else {
431 value = DateParse(year);
432 if (!NUMBER_IS_NAN(value)) {
433 cache.time = value;
434 cache.year = YEAR_FROM_TIME(LocalTimeNoCheck(value));
435 cache.string = year;
436 }
437 }
438
439 } else {
440 // According to ECMA 262, no hint should be given for this
441 // conversion. However, ToPrimitive defaults to STRING_HINT for
442 // Date objects which will lose precision when the Date
443 // constructor is called with another Date object as its
444 // argument. We therefore use NUMBER_HINT for the conversion,
445 // which is the default for everything else than Date objects.
446 // This makes us behave like KJS and SpiderMonkey.
447 var time = ToPrimitive(year, NUMBER_HINT);
448 value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
449 }
450
451 } else {
452 year = ToNumber(year);
453 month = ToNumber(month);
454 date = argc > 2 ? ToNumber(date) : 1;
455 hours = argc > 3 ? ToNumber(hours) : 0;
456 minutes = argc > 4 ? ToNumber(minutes) : 0;
457 seconds = argc > 5 ? ToNumber(seconds) : 0;
458 ms = argc > 6 ? ToNumber(ms) : 0;
459 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
460 ? 1900 + TO_INTEGER(year) : year;
461 var day = MakeDay(year, month, date);
462 var time = MakeTime(hours, minutes, seconds, ms);
463 value = TimeClip(UTC(MakeDate(day, time)));
464 }
465 %_SetValueOf(this, value);
466});
467
468
469// Helper functions.
470function GetTimeFrom(aDate) {
471 return DATE_VALUE(aDate);
472}
473
474function GetMillisecondsFrom(aDate) {
475 var t = DATE_VALUE(aDate);
476 if (NUMBER_IS_NAN(t)) return t;
477 return MS_FROM_TIME(LocalTimeNoCheck(t));
478}
479
480
481function GetUTCMillisecondsFrom(aDate) {
482 var t = DATE_VALUE(aDate);
483 if (NUMBER_IS_NAN(t)) return t;
484 return MS_FROM_TIME(t);
485}
486
487
488function GetSecondsFrom(aDate) {
489 var t = DATE_VALUE(aDate);
490 if (NUMBER_IS_NAN(t)) return t;
491 return SEC_FROM_TIME(LocalTimeNoCheck(t));
492}
493
494
495function GetUTCSecondsFrom(aDate) {
496 var t = DATE_VALUE(aDate);
497 if (NUMBER_IS_NAN(t)) return t;
498 return SEC_FROM_TIME(t);
499}
500
501
502function GetMinutesFrom(aDate) {
503 var t = DATE_VALUE(aDate);
504 if (NUMBER_IS_NAN(t)) return t;
505 return MIN_FROM_TIME(LocalTimeNoCheck(t));
506}
507
508
509function GetUTCMinutesFrom(aDate) {
510 var t = DATE_VALUE(aDate);
511 if (NUMBER_IS_NAN(t)) return t;
512 return MIN_FROM_TIME(t);
513}
514
515
516function GetHoursFrom(aDate) {
517 var t = DATE_VALUE(aDate);
518 if (NUMBER_IS_NAN(t)) return t;
519 return HOUR_FROM_TIME(LocalTimeNoCheck(t));
520}
521
522
523function GetUTCHoursFrom(aDate) {
524 var t = DATE_VALUE(aDate);
525 if (NUMBER_IS_NAN(t)) return t;
526 return HOUR_FROM_TIME(t);
527}
528
529
530function GetFullYearFrom(aDate) {
531 var t = DATE_VALUE(aDate);
532 if (NUMBER_IS_NAN(t)) return t;
533 var cache = Date_cache;
534 if (cache.time === t) return cache.year;
535 return YEAR_FROM_TIME(LocalTimeNoCheck(t));
536}
537
538
539function GetUTCFullYearFrom(aDate) {
540 var t = DATE_VALUE(aDate);
541 if (NUMBER_IS_NAN(t)) return t;
542 return YEAR_FROM_TIME(t);
543}
544
545
546function GetMonthFrom(aDate) {
547 var t = DATE_VALUE(aDate);
548 if (NUMBER_IS_NAN(t)) return t;
549 return MONTH_FROM_TIME(LocalTimeNoCheck(t));
550}
551
552
553function GetUTCMonthFrom(aDate) {
554 var t = DATE_VALUE(aDate);
555 if (NUMBER_IS_NAN(t)) return t;
556 return MONTH_FROM_TIME(t);
557}
558
559
560function GetDateFrom(aDate) {
561 var t = DATE_VALUE(aDate);
562 if (NUMBER_IS_NAN(t)) return t;
563 return DATE_FROM_TIME(LocalTimeNoCheck(t));
564}
565
566
567function GetUTCDateFrom(aDate) {
568 var t = DATE_VALUE(aDate);
569 if (NUMBER_IS_NAN(t)) return t;
570 return DATE_FROM_TIME(t);
571}
572
573
574%FunctionSetPrototype($Date, new $Date($NaN));
575
576
577var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
578var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
579
580
581function TwoDigitString(value) {
582 return value < 10 ? "0" + value : "" + value;
583}
584
585
586function DateString(time) {
587 var YMD = FromJulianDay(DAY(time) + kDayZeroInJulianDay);
588 return WeekDays[WeekDay(time)] + ' '
589 + Months[YMD.month] + ' '
590 + TwoDigitString(YMD.date) + ' '
591 + YMD.year;
592}
593
594
595var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
596var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
597
598
599function LongDateString(time) {
600 var YMD = FromJulianDay(DAY(time) + kDayZeroInJulianDay);
601 return LongWeekDays[WeekDay(time)] + ', '
602 + LongMonths[YMD.month] + ' '
603 + TwoDigitString(YMD.date) + ', '
604 + YMD.year;
605}
606
607
608function TimeString(time) {
609 return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
610 + TwoDigitString(MIN_FROM_TIME(time)) + ':'
611 + TwoDigitString(SEC_FROM_TIME(time));
612}
613
614
615function LocalTimezoneString(time) {
616 var timezoneOffset =
617 (local_time_offset + DaylightSavingsOffset(time)) / msPerMinute;
618 var sign = (timezoneOffset >= 0) ? 1 : -1;
619 var hours = FLOOR((sign * timezoneOffset)/60);
620 var min = FLOOR((sign * timezoneOffset)%60);
621 var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
622 TwoDigitString(hours) + TwoDigitString(min);
623 return gmt + ' (' + LocalTimezone(time) + ')';
624}
625
626
627function DatePrintString(time) {
628 return DateString(time) + ' ' + TimeString(time);
629}
630
631// -------------------------------------------------------------------
632
633// Reused output buffer. Used when parsing date strings.
634var parse_buffer = $Array(7);
635
636// ECMA 262 - 15.9.4.2
637function DateParse(string) {
638 var arr = %DateParseString(ToString(string), parse_buffer);
639 if (IS_NULL(arr)) return $NaN;
640
641 var day = MakeDay(arr[0], arr[1], arr[2]);
642 var time = MakeTime(arr[3], arr[4], arr[5], 0);
643 var date = MakeDate(day, time);
644
645 if (IS_NULL(arr[6])) {
646 return TimeClip(UTC(date));
647 } else {
648 return TimeClip(date - arr[6] * 1000);
649 }
650}
651
652
653// ECMA 262 - 15.9.4.3
654function DateUTC(year, month, date, hours, minutes, seconds, ms) {
655 year = ToNumber(year);
656 month = ToNumber(month);
657 var argc = %_ArgumentsLength();
658 date = argc > 2 ? ToNumber(date) : 1;
659 hours = argc > 3 ? ToNumber(hours) : 0;
660 minutes = argc > 4 ? ToNumber(minutes) : 0;
661 seconds = argc > 5 ? ToNumber(seconds) : 0;
662 ms = argc > 6 ? ToNumber(ms) : 0;
663 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
664 ? 1900 + TO_INTEGER(year) : year;
665 var day = MakeDay(year, month, date);
666 var time = MakeTime(hours, minutes, seconds, ms);
667 return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
668}
669
670
671// Mozilla-specific extension. Returns the number of milliseconds
672// elapsed since 1 January 1970 00:00:00 UTC.
673function DateNow() {
674 return %DateCurrentTime();
675}
676
677
678// ECMA 262 - 15.9.5.2
679function DateToString() {
680 var t = DATE_VALUE(this);
681 if (NUMBER_IS_NAN(t)) return kInvalidDate;
682 return DatePrintString(LocalTimeNoCheck(t)) + LocalTimezoneString(t);
683}
684
685
686// ECMA 262 - 15.9.5.3
687function DateToDateString() {
688 var t = DATE_VALUE(this);
689 if (NUMBER_IS_NAN(t)) return kInvalidDate;
690 return DateString(LocalTimeNoCheck(t));
691}
692
693
694// ECMA 262 - 15.9.5.4
695function DateToTimeString() {
696 var t = DATE_VALUE(this);
697 if (NUMBER_IS_NAN(t)) return kInvalidDate;
698 var lt = LocalTimeNoCheck(t);
699 return TimeString(lt) + LocalTimezoneString(lt);
700}
701
702
703// ECMA 262 - 15.9.5.5
704function DateToLocaleString() {
705 return DateToString.call(this);
706}
707
708
709// ECMA 262 - 15.9.5.6
710function DateToLocaleDateString() {
711 var t = DATE_VALUE(this);
712 if (NUMBER_IS_NAN(t)) return kInvalidDate;
713 return LongDateString(LocalTimeNoCheck(t));
714}
715
716
717// ECMA 262 - 15.9.5.7
718function DateToLocaleTimeString() {
719 var t = DATE_VALUE(this);
720 if (NUMBER_IS_NAN(t)) return kInvalidDate;
721 var lt = LocalTimeNoCheck(t);
722 return TimeString(lt);
723}
724
725
726// ECMA 262 - 15.9.5.8
727function DateValueOf() {
728 return DATE_VALUE(this);
729}
730
731
732// ECMA 262 - 15.9.5.9
733function DateGetTime(logMarker) {
734 if (logMarker) %ProfileLogMarker(logMarker);
735 return DATE_VALUE(this);
736}
737
738
739// ECMA 262 - 15.9.5.10
740function DateGetFullYear() {
741 return GetFullYearFrom(this)
742}
743
744
745// ECMA 262 - 15.9.5.11
746function DateGetUTCFullYear() {
747 return GetUTCFullYearFrom(this)
748}
749
750
751// ECMA 262 - 15.9.5.12
752function DateGetMonth() {
753 return GetMonthFrom(this);
754}
755
756
757// ECMA 262 - 15.9.5.13
758function DateGetUTCMonth() {
759 return GetUTCMonthFrom(this);
760}
761
762
763// ECMA 262 - 15.9.5.14
764function DateGetDate() {
765 return GetDateFrom(this);
766}
767
768
769// ECMA 262 - 15.9.5.15
770function DateGetUTCDate() {
771 return GetUTCDateFrom(this);
772}
773
774
775// ECMA 262 - 15.9.5.16
776function DateGetDay() {
777 var t = %_ValueOf(this);
778 if (NUMBER_IS_NAN(t)) return t;
779 return WeekDay(LocalTimeNoCheck(t));
780}
781
782
783// ECMA 262 - 15.9.5.17
784function DateGetUTCDay() {
785 var t = %_ValueOf(this);
786 if (NUMBER_IS_NAN(t)) return t;
787 return WeekDay(t);
788}
789
790
791// ECMA 262 - 15.9.5.18
792function DateGetHours() {
793 return GetHoursFrom(this);
794}
795
796
797// ECMA 262 - 15.9.5.19
798function DateGetUTCHours() {
799 return GetUTCHoursFrom(this);
800}
801
802
803// ECMA 262 - 15.9.5.20
804function DateGetMinutes() {
805 return GetMinutesFrom(this);
806}
807
808
809// ECMA 262 - 15.9.5.21
810function DateGetUTCMinutes() {
811 return GetUTCMinutesFrom(this);
812}
813
814
815// ECMA 262 - 15.9.5.22
816function DateGetSeconds() {
817 return GetSecondsFrom(this);
818}
819
820
821// ECMA 262 - 15.9.5.23
822function DateGetUTCSeconds() {
823 return GetUTCSecondsFrom(this);
824}
825
826
827// ECMA 262 - 15.9.5.24
828function DateGetMilliseconds() {
829 return GetMillisecondsFrom(this);
830}
831
832
833// ECMA 262 - 15.9.5.25
834function DateGetUTCMilliseconds() {
835 return GetUTCMillisecondsFrom(this);
836}
837
838
839// ECMA 262 - 15.9.5.26
840function DateGetTimezoneOffset() {
841 var t = DATE_VALUE(this);
842 if (NUMBER_IS_NAN(t)) return t;
843 return (t - LocalTimeNoCheck(t)) / msPerMinute;
844}
845
846
847// ECMA 262 - 15.9.5.27
848function DateSetTime(ms) {
849 if (!IS_DATE(this)) ThrowDateTypeError();
850 return %_SetValueOf(this, TimeClip(ToNumber(ms)));
851}
852
853
854// ECMA 262 - 15.9.5.28
855function DateSetMilliseconds(ms) {
856 var t = LocalTime(DATE_VALUE(this));
857 ms = ToNumber(ms);
858 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
859 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
860}
861
862
863// ECMA 262 - 15.9.5.29
864function DateSetUTCMilliseconds(ms) {
865 var t = DATE_VALUE(this);
866 ms = ToNumber(ms);
867 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
868 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
869}
870
871
872// ECMA 262 - 15.9.5.30
873function DateSetSeconds(sec, ms) {
874 var t = LocalTime(DATE_VALUE(this));
875 sec = ToNumber(sec);
876 ms = %_ArgumentsLength() < 2 ? GetMillisecondsFrom(this) : ToNumber(ms);
877 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
878 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
879}
880
881
882// ECMA 262 - 15.9.5.31
883function DateSetUTCSeconds(sec, ms) {
884 var t = DATE_VALUE(this);
885 sec = ToNumber(sec);
886 ms = %_ArgumentsLength() < 2 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
887 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
888 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
889}
890
891
892// ECMA 262 - 15.9.5.33
893function DateSetMinutes(min, sec, ms) {
894 var t = LocalTime(DATE_VALUE(this));
895 min = ToNumber(min);
896 var argc = %_ArgumentsLength();
897 sec = argc < 2 ? GetSecondsFrom(this) : ToNumber(sec);
898 ms = argc < 3 ? GetMillisecondsFrom(this) : ToNumber(ms);
899 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
900 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
901}
902
903
904// ECMA 262 - 15.9.5.34
905function DateSetUTCMinutes(min, sec, ms) {
906 var t = DATE_VALUE(this);
907 min = ToNumber(min);
908 var argc = %_ArgumentsLength();
909 sec = argc < 2 ? GetUTCSecondsFrom(this) : ToNumber(sec);
910 ms = argc < 3 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
911 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
912 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
913}
914
915
916// ECMA 262 - 15.9.5.35
917function DateSetHours(hour, min, sec, ms) {
918 var t = LocalTime(DATE_VALUE(this));
919 hour = ToNumber(hour);
920 var argc = %_ArgumentsLength();
921 min = argc < 2 ? GetMinutesFrom(this) : ToNumber(min);
922 sec = argc < 3 ? GetSecondsFrom(this) : ToNumber(sec);
923 ms = argc < 4 ? GetMillisecondsFrom(this) : ToNumber(ms);
924 var time = MakeTime(hour, min, sec, ms);
925 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
926}
927
928
929// ECMA 262 - 15.9.5.34
930function DateSetUTCHours(hour, min, sec, ms) {
931 var t = DATE_VALUE(this);
932 hour = ToNumber(hour);
933 var argc = %_ArgumentsLength();
934 min = argc < 2 ? GetUTCMinutesFrom(this) : ToNumber(min);
935 sec = argc < 3 ? GetUTCSecondsFrom(this) : ToNumber(sec);
936 ms = argc < 4 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
937 var time = MakeTime(hour, min, sec, ms);
938 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
939}
940
941
942// ECMA 262 - 15.9.5.36
943function DateSetDate(date) {
944 var t = LocalTime(DATE_VALUE(this));
945 date = ToNumber(date);
946 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
947 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
948}
949
950
951// ECMA 262 - 15.9.5.37
952function DateSetUTCDate(date) {
953 var t = DATE_VALUE(this);
954 date = ToNumber(date);
955 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
956 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
957}
958
959
960// ECMA 262 - 15.9.5.38
961function DateSetMonth(month, date) {
962 var t = LocalTime(DATE_VALUE(this));
963 month = ToNumber(month);
964 date = %_ArgumentsLength() < 2 ? GetDateFrom(this) : ToNumber(date);
965 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
966 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
967}
968
969
970// ECMA 262 - 15.9.5.39
971function DateSetUTCMonth(month, date) {
972 var t = DATE_VALUE(this);
973 month = ToNumber(month);
974 date = %_ArgumentsLength() < 2 ? GetUTCDateFrom(this) : ToNumber(date);
975 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
976 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
977}
978
979
980// ECMA 262 - 15.9.5.40
981function DateSetFullYear(year, month, date) {
982 var t = DATE_VALUE(this);
983 t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
984 year = ToNumber(year);
985 var argc = %_ArgumentsLength();
986 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
987 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
988 var day = MakeDay(year, month, date);
989 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
990}
991
992
993// ECMA 262 - 15.9.5.41
994function DateSetUTCFullYear(year, month, date) {
995 var t = DATE_VALUE(this);
996 if (NUMBER_IS_NAN(t)) t = 0;
997 var argc = %_ArgumentsLength();
998 year = ToNumber(year);
999 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
1000 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
1001 var day = MakeDay(year, month, date);
1002 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
1003}
1004
1005
1006// ECMA 262 - 15.9.5.42
1007function DateToUTCString() {
1008 var t = DATE_VALUE(this);
1009 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1010 // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
1011 return WeekDays[WeekDay(t)] + ', '
1012 + TwoDigitString(DATE_FROM_TIME(t)) + ' '
1013 + Months[MONTH_FROM_TIME(t)] + ' '
1014 + YEAR_FROM_TIME(t) + ' '
1015 + TimeString(t) + ' GMT';
1016}
1017
1018
1019// ECMA 262 - B.2.4
1020function DateGetYear() {
1021 var t = DATE_VALUE(this);
1022 if (NUMBER_IS_NAN(t)) return $NaN;
1023 return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900;
1024}
1025
1026
1027// ECMA 262 - B.2.5
1028function DateSetYear(year) {
1029 var t = LocalTime(DATE_VALUE(this));
1030 if (NUMBER_IS_NAN(t)) t = 0;
1031 year = ToNumber(year);
1032 if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
1033 year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
1034 ? 1900 + TO_INTEGER(year) : year;
1035 var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
1036 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
1037}
1038
1039
1040// ECMA 262 - B.2.6
1041//
1042// Notice that this does not follow ECMA 262 completely. ECMA 262
1043// says that toGMTString should be the same Function object as
1044// toUTCString. JSC does not do this, so for compatibility we do not
1045// do that either. Instead, we create a new function whose name
1046// property will return toGMTString.
1047function DateToGMTString() {
1048 return DateToUTCString.call(this);
1049}
1050
1051
1052function PadInt(n, digits) {
1053 if (digits == 1) return n;
1054 return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
1055}
1056
1057
1058function DateToISOString() {
1059 var t = DATE_VALUE(this);
1060 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1061 return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) +
1062 '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) +
1063 ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) +
1064 '.' + PadInt(this.getUTCMilliseconds(), 3) +
1065 'Z';
1066}
1067
1068
1069function DateToJSON(key) {
1070 return CheckJSONPrimitive(this.toISOString());
1071}
1072
1073
1074// -------------------------------------------------------------------
1075
1076function SetupDate() {
1077 // Setup non-enumerable properties of the Date object itself.
1078 InstallFunctions($Date, DONT_ENUM, $Array(
1079 "UTC", DateUTC,
1080 "parse", DateParse,
1081 "now", DateNow
1082 ));
1083
1084 // Setup non-enumerable constructor property of the Date prototype object.
1085 %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
1086
1087 // Setup non-enumerable functions of the Date prototype object and
1088 // set their names.
1089 InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array(
1090 "toString", DateToString,
1091 "toDateString", DateToDateString,
1092 "toTimeString", DateToTimeString,
1093 "toLocaleString", DateToLocaleString,
1094 "toLocaleDateString", DateToLocaleDateString,
1095 "toLocaleTimeString", DateToLocaleTimeString,
1096 "valueOf", DateValueOf,
1097 "getTime", DateGetTime,
1098 "getFullYear", DateGetFullYear,
1099 "getUTCFullYear", DateGetUTCFullYear,
1100 "getMonth", DateGetMonth,
1101 "getUTCMonth", DateGetUTCMonth,
1102 "getDate", DateGetDate,
1103 "getUTCDate", DateGetUTCDate,
1104 "getDay", DateGetDay,
1105 "getUTCDay", DateGetUTCDay,
1106 "getHours", DateGetHours,
1107 "getUTCHours", DateGetUTCHours,
1108 "getMinutes", DateGetMinutes,
1109 "getUTCMinutes", DateGetUTCMinutes,
1110 "getSeconds", DateGetSeconds,
1111 "getUTCSeconds", DateGetUTCSeconds,
1112 "getMilliseconds", DateGetMilliseconds,
1113 "getUTCMilliseconds", DateGetUTCMilliseconds,
1114 "getTimezoneOffset", DateGetTimezoneOffset,
1115 "setTime", DateSetTime,
1116 "setMilliseconds", DateSetMilliseconds,
1117 "setUTCMilliseconds", DateSetUTCMilliseconds,
1118 "setSeconds", DateSetSeconds,
1119 "setUTCSeconds", DateSetUTCSeconds,
1120 "setMinutes", DateSetMinutes,
1121 "setUTCMinutes", DateSetUTCMinutes,
1122 "setHours", DateSetHours,
1123 "setUTCHours", DateSetUTCHours,
1124 "setDate", DateSetDate,
1125 "setUTCDate", DateSetUTCDate,
1126 "setMonth", DateSetMonth,
1127 "setUTCMonth", DateSetUTCMonth,
1128 "setFullYear", DateSetFullYear,
1129 "setUTCFullYear", DateSetUTCFullYear,
1130 "toGMTString", DateToGMTString,
1131 "toUTCString", DateToUTCString,
1132 "getYear", DateGetYear,
1133 "setYear", DateSetYear,
1134 "toISOString", DateToISOString,
1135 "toJSON", DateToJSON
1136 ));
1137}
1138
1139SetupDate();