blob: 7d8f45888fd3cd83a2268b860a170dcdaaaefd1a [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
Andrei Popescu402d9372010-02-26 13:31:12 +0000733function DateGetTime() {
Andrei Popescu31002712010-02-23 13:46:05 +0000734 return DATE_VALUE(this);
735}
736
737
738// ECMA 262 - 15.9.5.10
739function DateGetFullYear() {
740 return GetFullYearFrom(this)
741}
742
743
744// ECMA 262 - 15.9.5.11
745function DateGetUTCFullYear() {
746 return GetUTCFullYearFrom(this)
747}
748
749
750// ECMA 262 - 15.9.5.12
751function DateGetMonth() {
752 return GetMonthFrom(this);
753}
754
755
756// ECMA 262 - 15.9.5.13
757function DateGetUTCMonth() {
758 return GetUTCMonthFrom(this);
759}
760
761
762// ECMA 262 - 15.9.5.14
763function DateGetDate() {
764 return GetDateFrom(this);
765}
766
767
768// ECMA 262 - 15.9.5.15
769function DateGetUTCDate() {
770 return GetUTCDateFrom(this);
771}
772
773
774// ECMA 262 - 15.9.5.16
775function DateGetDay() {
776 var t = %_ValueOf(this);
777 if (NUMBER_IS_NAN(t)) return t;
778 return WeekDay(LocalTimeNoCheck(t));
779}
780
781
782// ECMA 262 - 15.9.5.17
783function DateGetUTCDay() {
784 var t = %_ValueOf(this);
785 if (NUMBER_IS_NAN(t)) return t;
786 return WeekDay(t);
787}
788
789
790// ECMA 262 - 15.9.5.18
791function DateGetHours() {
792 return GetHoursFrom(this);
793}
794
795
796// ECMA 262 - 15.9.5.19
797function DateGetUTCHours() {
798 return GetUTCHoursFrom(this);
799}
800
801
802// ECMA 262 - 15.9.5.20
803function DateGetMinutes() {
804 return GetMinutesFrom(this);
805}
806
807
808// ECMA 262 - 15.9.5.21
809function DateGetUTCMinutes() {
810 return GetUTCMinutesFrom(this);
811}
812
813
814// ECMA 262 - 15.9.5.22
815function DateGetSeconds() {
816 return GetSecondsFrom(this);
817}
818
819
820// ECMA 262 - 15.9.5.23
821function DateGetUTCSeconds() {
822 return GetUTCSecondsFrom(this);
823}
824
825
826// ECMA 262 - 15.9.5.24
827function DateGetMilliseconds() {
828 return GetMillisecondsFrom(this);
829}
830
831
832// ECMA 262 - 15.9.5.25
833function DateGetUTCMilliseconds() {
834 return GetUTCMillisecondsFrom(this);
835}
836
837
838// ECMA 262 - 15.9.5.26
839function DateGetTimezoneOffset() {
840 var t = DATE_VALUE(this);
841 if (NUMBER_IS_NAN(t)) return t;
842 return (t - LocalTimeNoCheck(t)) / msPerMinute;
843}
844
845
846// ECMA 262 - 15.9.5.27
847function DateSetTime(ms) {
848 if (!IS_DATE(this)) ThrowDateTypeError();
849 return %_SetValueOf(this, TimeClip(ToNumber(ms)));
850}
851
852
853// ECMA 262 - 15.9.5.28
854function DateSetMilliseconds(ms) {
855 var t = LocalTime(DATE_VALUE(this));
856 ms = ToNumber(ms);
857 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
858 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
859}
860
861
862// ECMA 262 - 15.9.5.29
863function DateSetUTCMilliseconds(ms) {
864 var t = DATE_VALUE(this);
865 ms = ToNumber(ms);
866 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
867 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
868}
869
870
871// ECMA 262 - 15.9.5.30
872function DateSetSeconds(sec, ms) {
873 var t = LocalTime(DATE_VALUE(this));
874 sec = ToNumber(sec);
875 ms = %_ArgumentsLength() < 2 ? GetMillisecondsFrom(this) : ToNumber(ms);
876 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
877 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
878}
879
880
881// ECMA 262 - 15.9.5.31
882function DateSetUTCSeconds(sec, ms) {
883 var t = DATE_VALUE(this);
884 sec = ToNumber(sec);
885 ms = %_ArgumentsLength() < 2 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
886 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
887 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
888}
889
890
891// ECMA 262 - 15.9.5.33
892function DateSetMinutes(min, sec, ms) {
893 var t = LocalTime(DATE_VALUE(this));
894 min = ToNumber(min);
895 var argc = %_ArgumentsLength();
896 sec = argc < 2 ? GetSecondsFrom(this) : ToNumber(sec);
897 ms = argc < 3 ? GetMillisecondsFrom(this) : ToNumber(ms);
898 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
899 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
900}
901
902
903// ECMA 262 - 15.9.5.34
904function DateSetUTCMinutes(min, sec, ms) {
905 var t = DATE_VALUE(this);
906 min = ToNumber(min);
907 var argc = %_ArgumentsLength();
908 sec = argc < 2 ? GetUTCSecondsFrom(this) : ToNumber(sec);
909 ms = argc < 3 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
910 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
911 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
912}
913
914
915// ECMA 262 - 15.9.5.35
916function DateSetHours(hour, min, sec, ms) {
917 var t = LocalTime(DATE_VALUE(this));
918 hour = ToNumber(hour);
919 var argc = %_ArgumentsLength();
920 min = argc < 2 ? GetMinutesFrom(this) : ToNumber(min);
921 sec = argc < 3 ? GetSecondsFrom(this) : ToNumber(sec);
922 ms = argc < 4 ? GetMillisecondsFrom(this) : ToNumber(ms);
923 var time = MakeTime(hour, min, sec, ms);
924 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
925}
926
927
928// ECMA 262 - 15.9.5.34
929function DateSetUTCHours(hour, min, sec, ms) {
930 var t = DATE_VALUE(this);
931 hour = ToNumber(hour);
932 var argc = %_ArgumentsLength();
933 min = argc < 2 ? GetUTCMinutesFrom(this) : ToNumber(min);
934 sec = argc < 3 ? GetUTCSecondsFrom(this) : ToNumber(sec);
935 ms = argc < 4 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
936 var time = MakeTime(hour, min, sec, ms);
937 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
938}
939
940
941// ECMA 262 - 15.9.5.36
942function DateSetDate(date) {
943 var t = LocalTime(DATE_VALUE(this));
944 date = ToNumber(date);
945 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
946 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
947}
948
949
950// ECMA 262 - 15.9.5.37
951function DateSetUTCDate(date) {
952 var t = DATE_VALUE(this);
953 date = ToNumber(date);
954 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
955 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
956}
957
958
959// ECMA 262 - 15.9.5.38
960function DateSetMonth(month, date) {
961 var t = LocalTime(DATE_VALUE(this));
962 month = ToNumber(month);
963 date = %_ArgumentsLength() < 2 ? GetDateFrom(this) : ToNumber(date);
964 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
965 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
966}
967
968
969// ECMA 262 - 15.9.5.39
970function DateSetUTCMonth(month, date) {
971 var t = DATE_VALUE(this);
972 month = ToNumber(month);
973 date = %_ArgumentsLength() < 2 ? GetUTCDateFrom(this) : ToNumber(date);
974 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
975 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
976}
977
978
979// ECMA 262 - 15.9.5.40
980function DateSetFullYear(year, month, date) {
981 var t = DATE_VALUE(this);
982 t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
983 year = ToNumber(year);
984 var argc = %_ArgumentsLength();
985 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
986 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
987 var day = MakeDay(year, month, date);
988 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
989}
990
991
992// ECMA 262 - 15.9.5.41
993function DateSetUTCFullYear(year, month, date) {
994 var t = DATE_VALUE(this);
995 if (NUMBER_IS_NAN(t)) t = 0;
996 var argc = %_ArgumentsLength();
997 year = ToNumber(year);
998 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
999 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
1000 var day = MakeDay(year, month, date);
1001 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
1002}
1003
1004
1005// ECMA 262 - 15.9.5.42
1006function DateToUTCString() {
1007 var t = DATE_VALUE(this);
1008 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1009 // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
1010 return WeekDays[WeekDay(t)] + ', '
1011 + TwoDigitString(DATE_FROM_TIME(t)) + ' '
1012 + Months[MONTH_FROM_TIME(t)] + ' '
1013 + YEAR_FROM_TIME(t) + ' '
1014 + TimeString(t) + ' GMT';
1015}
1016
1017
1018// ECMA 262 - B.2.4
1019function DateGetYear() {
1020 var t = DATE_VALUE(this);
1021 if (NUMBER_IS_NAN(t)) return $NaN;
1022 return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900;
1023}
1024
1025
1026// ECMA 262 - B.2.5
1027function DateSetYear(year) {
1028 var t = LocalTime(DATE_VALUE(this));
1029 if (NUMBER_IS_NAN(t)) t = 0;
1030 year = ToNumber(year);
1031 if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
1032 year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
1033 ? 1900 + TO_INTEGER(year) : year;
1034 var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
1035 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
1036}
1037
1038
1039// ECMA 262 - B.2.6
1040//
1041// Notice that this does not follow ECMA 262 completely. ECMA 262
1042// says that toGMTString should be the same Function object as
1043// toUTCString. JSC does not do this, so for compatibility we do not
1044// do that either. Instead, we create a new function whose name
1045// property will return toGMTString.
1046function DateToGMTString() {
1047 return DateToUTCString.call(this);
1048}
1049
1050
1051function PadInt(n, digits) {
1052 if (digits == 1) return n;
1053 return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
1054}
1055
1056
1057function DateToISOString() {
1058 var t = DATE_VALUE(this);
1059 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1060 return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) +
1061 '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) +
1062 ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) +
1063 '.' + PadInt(this.getUTCMilliseconds(), 3) +
1064 'Z';
1065}
1066
1067
1068function DateToJSON(key) {
1069 return CheckJSONPrimitive(this.toISOString());
1070}
1071
1072
1073// -------------------------------------------------------------------
1074
1075function SetupDate() {
1076 // Setup non-enumerable properties of the Date object itself.
1077 InstallFunctions($Date, DONT_ENUM, $Array(
1078 "UTC", DateUTC,
1079 "parse", DateParse,
1080 "now", DateNow
1081 ));
1082
1083 // Setup non-enumerable constructor property of the Date prototype object.
1084 %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
1085
1086 // Setup non-enumerable functions of the Date prototype object and
1087 // set their names.
1088 InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array(
1089 "toString", DateToString,
1090 "toDateString", DateToDateString,
1091 "toTimeString", DateToTimeString,
1092 "toLocaleString", DateToLocaleString,
1093 "toLocaleDateString", DateToLocaleDateString,
1094 "toLocaleTimeString", DateToLocaleTimeString,
1095 "valueOf", DateValueOf,
1096 "getTime", DateGetTime,
1097 "getFullYear", DateGetFullYear,
1098 "getUTCFullYear", DateGetUTCFullYear,
1099 "getMonth", DateGetMonth,
1100 "getUTCMonth", DateGetUTCMonth,
1101 "getDate", DateGetDate,
1102 "getUTCDate", DateGetUTCDate,
1103 "getDay", DateGetDay,
1104 "getUTCDay", DateGetUTCDay,
1105 "getHours", DateGetHours,
1106 "getUTCHours", DateGetUTCHours,
1107 "getMinutes", DateGetMinutes,
1108 "getUTCMinutes", DateGetUTCMinutes,
1109 "getSeconds", DateGetSeconds,
1110 "getUTCSeconds", DateGetUTCSeconds,
1111 "getMilliseconds", DateGetMilliseconds,
1112 "getUTCMilliseconds", DateGetUTCMilliseconds,
1113 "getTimezoneOffset", DateGetTimezoneOffset,
1114 "setTime", DateSetTime,
1115 "setMilliseconds", DateSetMilliseconds,
1116 "setUTCMilliseconds", DateSetUTCMilliseconds,
1117 "setSeconds", DateSetSeconds,
1118 "setUTCSeconds", DateSetUTCSeconds,
1119 "setMinutes", DateSetMinutes,
1120 "setUTCMinutes", DateSetUTCMinutes,
1121 "setHours", DateSetHours,
1122 "setUTCHours", DateSetUTCHours,
1123 "setDate", DateSetDate,
1124 "setUTCDate", DateSetUTCDate,
1125 "setMonth", DateSetMonth,
1126 "setUTCMonth", DateSetUTCMonth,
1127 "setFullYear", DateSetFullYear,
1128 "setUTCFullYear", DateSetUTCFullYear,
1129 "toGMTString", DateToGMTString,
1130 "toUTCString", DateToUTCString,
1131 "getYear", DateGetYear,
1132 "setYear", DateSetYear,
1133 "toISOString", DateToISOString,
1134 "toJSON", DateToJSON
1135 ));
1136}
1137
1138SetupDate();