blob: b4027c13f2cdd48216754cb87ff49cac1acf9593 [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) {
Ben Murdochd69d2e32010-03-30 12:55:27 +0100616 var old_timezone = timezone_cache_timezone;
617 var timezone = LocalTimezone(time);
618 if (old_timezone && timezone != old_timezone) {
619 // If the timezone string has changed from the one that we cached,
620 // the local time offset may now be wrong. So we need to update it
621 // and try again.
622 local_time_offset = %DateLocalTimeOffset();
623 // We also need to invalidate the DST cache as the new timezone may have
624 // different DST times.
625 var dst_cache = DST_offset_cache;
626 dst_cache.start = 0;
627 dst_cache.end = -1;
628 }
629
Andrei Popescu31002712010-02-23 13:46:05 +0000630 var timezoneOffset =
631 (local_time_offset + DaylightSavingsOffset(time)) / msPerMinute;
632 var sign = (timezoneOffset >= 0) ? 1 : -1;
633 var hours = FLOOR((sign * timezoneOffset)/60);
634 var min = FLOOR((sign * timezoneOffset)%60);
635 var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
636 TwoDigitString(hours) + TwoDigitString(min);
Ben Murdochd69d2e32010-03-30 12:55:27 +0100637 return gmt + ' (' + timezone + ')';
Andrei Popescu31002712010-02-23 13:46:05 +0000638}
639
640
641function DatePrintString(time) {
642 return DateString(time) + ' ' + TimeString(time);
643}
644
645// -------------------------------------------------------------------
646
647// Reused output buffer. Used when parsing date strings.
648var parse_buffer = $Array(7);
649
650// ECMA 262 - 15.9.4.2
651function DateParse(string) {
652 var arr = %DateParseString(ToString(string), parse_buffer);
653 if (IS_NULL(arr)) return $NaN;
654
655 var day = MakeDay(arr[0], arr[1], arr[2]);
656 var time = MakeTime(arr[3], arr[4], arr[5], 0);
657 var date = MakeDate(day, time);
658
659 if (IS_NULL(arr[6])) {
660 return TimeClip(UTC(date));
661 } else {
662 return TimeClip(date - arr[6] * 1000);
663 }
664}
665
666
667// ECMA 262 - 15.9.4.3
668function DateUTC(year, month, date, hours, minutes, seconds, ms) {
669 year = ToNumber(year);
670 month = ToNumber(month);
671 var argc = %_ArgumentsLength();
672 date = argc > 2 ? ToNumber(date) : 1;
673 hours = argc > 3 ? ToNumber(hours) : 0;
674 minutes = argc > 4 ? ToNumber(minutes) : 0;
675 seconds = argc > 5 ? ToNumber(seconds) : 0;
676 ms = argc > 6 ? ToNumber(ms) : 0;
677 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
678 ? 1900 + TO_INTEGER(year) : year;
679 var day = MakeDay(year, month, date);
680 var time = MakeTime(hours, minutes, seconds, ms);
681 return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
682}
683
684
685// Mozilla-specific extension. Returns the number of milliseconds
686// elapsed since 1 January 1970 00:00:00 UTC.
687function DateNow() {
688 return %DateCurrentTime();
689}
690
691
692// ECMA 262 - 15.9.5.2
693function DateToString() {
694 var t = DATE_VALUE(this);
695 if (NUMBER_IS_NAN(t)) return kInvalidDate;
696 return DatePrintString(LocalTimeNoCheck(t)) + LocalTimezoneString(t);
697}
698
699
700// ECMA 262 - 15.9.5.3
701function DateToDateString() {
702 var t = DATE_VALUE(this);
703 if (NUMBER_IS_NAN(t)) return kInvalidDate;
704 return DateString(LocalTimeNoCheck(t));
705}
706
707
708// ECMA 262 - 15.9.5.4
709function DateToTimeString() {
710 var t = DATE_VALUE(this);
711 if (NUMBER_IS_NAN(t)) return kInvalidDate;
712 var lt = LocalTimeNoCheck(t);
713 return TimeString(lt) + LocalTimezoneString(lt);
714}
715
716
717// ECMA 262 - 15.9.5.5
718function DateToLocaleString() {
719 return DateToString.call(this);
720}
721
722
723// ECMA 262 - 15.9.5.6
724function DateToLocaleDateString() {
725 var t = DATE_VALUE(this);
726 if (NUMBER_IS_NAN(t)) return kInvalidDate;
727 return LongDateString(LocalTimeNoCheck(t));
728}
729
730
731// ECMA 262 - 15.9.5.7
732function DateToLocaleTimeString() {
733 var t = DATE_VALUE(this);
734 if (NUMBER_IS_NAN(t)) return kInvalidDate;
735 var lt = LocalTimeNoCheck(t);
736 return TimeString(lt);
737}
738
739
740// ECMA 262 - 15.9.5.8
741function DateValueOf() {
742 return DATE_VALUE(this);
743}
744
745
746// ECMA 262 - 15.9.5.9
Andrei Popescu402d9372010-02-26 13:31:12 +0000747function DateGetTime() {
Andrei Popescu31002712010-02-23 13:46:05 +0000748 return DATE_VALUE(this);
749}
750
751
752// ECMA 262 - 15.9.5.10
753function DateGetFullYear() {
754 return GetFullYearFrom(this)
755}
756
757
758// ECMA 262 - 15.9.5.11
759function DateGetUTCFullYear() {
760 return GetUTCFullYearFrom(this)
761}
762
763
764// ECMA 262 - 15.9.5.12
765function DateGetMonth() {
766 return GetMonthFrom(this);
767}
768
769
770// ECMA 262 - 15.9.5.13
771function DateGetUTCMonth() {
772 return GetUTCMonthFrom(this);
773}
774
775
776// ECMA 262 - 15.9.5.14
777function DateGetDate() {
778 return GetDateFrom(this);
779}
780
781
782// ECMA 262 - 15.9.5.15
783function DateGetUTCDate() {
784 return GetUTCDateFrom(this);
785}
786
787
788// ECMA 262 - 15.9.5.16
789function DateGetDay() {
790 var t = %_ValueOf(this);
791 if (NUMBER_IS_NAN(t)) return t;
792 return WeekDay(LocalTimeNoCheck(t));
793}
794
795
796// ECMA 262 - 15.9.5.17
797function DateGetUTCDay() {
798 var t = %_ValueOf(this);
799 if (NUMBER_IS_NAN(t)) return t;
800 return WeekDay(t);
801}
802
803
804// ECMA 262 - 15.9.5.18
805function DateGetHours() {
806 return GetHoursFrom(this);
807}
808
809
810// ECMA 262 - 15.9.5.19
811function DateGetUTCHours() {
812 return GetUTCHoursFrom(this);
813}
814
815
816// ECMA 262 - 15.9.5.20
817function DateGetMinutes() {
818 return GetMinutesFrom(this);
819}
820
821
822// ECMA 262 - 15.9.5.21
823function DateGetUTCMinutes() {
824 return GetUTCMinutesFrom(this);
825}
826
827
828// ECMA 262 - 15.9.5.22
829function DateGetSeconds() {
830 return GetSecondsFrom(this);
831}
832
833
834// ECMA 262 - 15.9.5.23
835function DateGetUTCSeconds() {
836 return GetUTCSecondsFrom(this);
837}
838
839
840// ECMA 262 - 15.9.5.24
841function DateGetMilliseconds() {
842 return GetMillisecondsFrom(this);
843}
844
845
846// ECMA 262 - 15.9.5.25
847function DateGetUTCMilliseconds() {
848 return GetUTCMillisecondsFrom(this);
849}
850
851
852// ECMA 262 - 15.9.5.26
853function DateGetTimezoneOffset() {
854 var t = DATE_VALUE(this);
855 if (NUMBER_IS_NAN(t)) return t;
856 return (t - LocalTimeNoCheck(t)) / msPerMinute;
857}
858
859
860// ECMA 262 - 15.9.5.27
861function DateSetTime(ms) {
862 if (!IS_DATE(this)) ThrowDateTypeError();
863 return %_SetValueOf(this, TimeClip(ToNumber(ms)));
864}
865
866
867// ECMA 262 - 15.9.5.28
868function DateSetMilliseconds(ms) {
869 var t = LocalTime(DATE_VALUE(this));
870 ms = ToNumber(ms);
871 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
872 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
873}
874
875
876// ECMA 262 - 15.9.5.29
877function DateSetUTCMilliseconds(ms) {
878 var t = DATE_VALUE(this);
879 ms = ToNumber(ms);
880 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
881 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
882}
883
884
885// ECMA 262 - 15.9.5.30
886function DateSetSeconds(sec, ms) {
887 var t = LocalTime(DATE_VALUE(this));
888 sec = ToNumber(sec);
889 ms = %_ArgumentsLength() < 2 ? GetMillisecondsFrom(this) : ToNumber(ms);
890 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
891 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
892}
893
894
895// ECMA 262 - 15.9.5.31
896function DateSetUTCSeconds(sec, ms) {
897 var t = DATE_VALUE(this);
898 sec = ToNumber(sec);
899 ms = %_ArgumentsLength() < 2 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
900 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
901 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
902}
903
904
905// ECMA 262 - 15.9.5.33
906function DateSetMinutes(min, sec, ms) {
907 var t = LocalTime(DATE_VALUE(this));
908 min = ToNumber(min);
909 var argc = %_ArgumentsLength();
910 sec = argc < 2 ? GetSecondsFrom(this) : ToNumber(sec);
911 ms = argc < 3 ? GetMillisecondsFrom(this) : ToNumber(ms);
912 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
913 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
914}
915
916
917// ECMA 262 - 15.9.5.34
918function DateSetUTCMinutes(min, sec, ms) {
919 var t = DATE_VALUE(this);
920 min = ToNumber(min);
921 var argc = %_ArgumentsLength();
922 sec = argc < 2 ? GetUTCSecondsFrom(this) : ToNumber(sec);
923 ms = argc < 3 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
924 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
925 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
926}
927
928
929// ECMA 262 - 15.9.5.35
930function DateSetHours(hour, min, sec, ms) {
931 var t = LocalTime(DATE_VALUE(this));
932 hour = ToNumber(hour);
933 var argc = %_ArgumentsLength();
934 min = argc < 2 ? GetMinutesFrom(this) : ToNumber(min);
935 sec = argc < 3 ? GetSecondsFrom(this) : ToNumber(sec);
936 ms = argc < 4 ? GetMillisecondsFrom(this) : ToNumber(ms);
937 var time = MakeTime(hour, min, sec, ms);
938 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
939}
940
941
942// ECMA 262 - 15.9.5.34
943function DateSetUTCHours(hour, min, sec, ms) {
944 var t = DATE_VALUE(this);
945 hour = ToNumber(hour);
946 var argc = %_ArgumentsLength();
947 min = argc < 2 ? GetUTCMinutesFrom(this) : ToNumber(min);
948 sec = argc < 3 ? GetUTCSecondsFrom(this) : ToNumber(sec);
949 ms = argc < 4 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
950 var time = MakeTime(hour, min, sec, ms);
951 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
952}
953
954
955// ECMA 262 - 15.9.5.36
956function DateSetDate(date) {
957 var t = LocalTime(DATE_VALUE(this));
958 date = ToNumber(date);
959 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
960 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
961}
962
963
964// ECMA 262 - 15.9.5.37
965function DateSetUTCDate(date) {
966 var t = DATE_VALUE(this);
967 date = ToNumber(date);
968 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
969 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
970}
971
972
973// ECMA 262 - 15.9.5.38
974function DateSetMonth(month, date) {
975 var t = LocalTime(DATE_VALUE(this));
976 month = ToNumber(month);
977 date = %_ArgumentsLength() < 2 ? GetDateFrom(this) : ToNumber(date);
978 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
979 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
980}
981
982
983// ECMA 262 - 15.9.5.39
984function DateSetUTCMonth(month, date) {
985 var t = DATE_VALUE(this);
986 month = ToNumber(month);
987 date = %_ArgumentsLength() < 2 ? GetUTCDateFrom(this) : ToNumber(date);
988 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
989 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
990}
991
992
993// ECMA 262 - 15.9.5.40
994function DateSetFullYear(year, month, date) {
995 var t = DATE_VALUE(this);
996 t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
997 year = ToNumber(year);
998 var argc = %_ArgumentsLength();
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(UTC(MakeDate(day, TimeWithinDay(t)))));
1003}
1004
1005
1006// ECMA 262 - 15.9.5.41
1007function DateSetUTCFullYear(year, month, date) {
1008 var t = DATE_VALUE(this);
1009 if (NUMBER_IS_NAN(t)) t = 0;
1010 var argc = %_ArgumentsLength();
1011 year = ToNumber(year);
1012 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
1013 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
1014 var day = MakeDay(year, month, date);
1015 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
1016}
1017
1018
1019// ECMA 262 - 15.9.5.42
1020function DateToUTCString() {
1021 var t = DATE_VALUE(this);
1022 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1023 // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
1024 return WeekDays[WeekDay(t)] + ', '
1025 + TwoDigitString(DATE_FROM_TIME(t)) + ' '
1026 + Months[MONTH_FROM_TIME(t)] + ' '
1027 + YEAR_FROM_TIME(t) + ' '
1028 + TimeString(t) + ' GMT';
1029}
1030
1031
1032// ECMA 262 - B.2.4
1033function DateGetYear() {
1034 var t = DATE_VALUE(this);
1035 if (NUMBER_IS_NAN(t)) return $NaN;
1036 return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900;
1037}
1038
1039
1040// ECMA 262 - B.2.5
1041function DateSetYear(year) {
1042 var t = LocalTime(DATE_VALUE(this));
1043 if (NUMBER_IS_NAN(t)) t = 0;
1044 year = ToNumber(year);
1045 if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
1046 year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
1047 ? 1900 + TO_INTEGER(year) : year;
1048 var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
1049 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
1050}
1051
1052
1053// ECMA 262 - B.2.6
1054//
1055// Notice that this does not follow ECMA 262 completely. ECMA 262
1056// says that toGMTString should be the same Function object as
1057// toUTCString. JSC does not do this, so for compatibility we do not
1058// do that either. Instead, we create a new function whose name
1059// property will return toGMTString.
1060function DateToGMTString() {
1061 return DateToUTCString.call(this);
1062}
1063
1064
1065function PadInt(n, digits) {
1066 if (digits == 1) return n;
1067 return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
1068}
1069
1070
1071function DateToISOString() {
1072 var t = DATE_VALUE(this);
1073 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1074 return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) +
1075 '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) +
1076 ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) +
1077 '.' + PadInt(this.getUTCMilliseconds(), 3) +
1078 'Z';
1079}
1080
1081
1082function DateToJSON(key) {
1083 return CheckJSONPrimitive(this.toISOString());
1084}
1085
1086
1087// -------------------------------------------------------------------
1088
1089function SetupDate() {
1090 // Setup non-enumerable properties of the Date object itself.
1091 InstallFunctions($Date, DONT_ENUM, $Array(
1092 "UTC", DateUTC,
1093 "parse", DateParse,
1094 "now", DateNow
1095 ));
1096
1097 // Setup non-enumerable constructor property of the Date prototype object.
1098 %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
1099
1100 // Setup non-enumerable functions of the Date prototype object and
1101 // set their names.
1102 InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array(
1103 "toString", DateToString,
1104 "toDateString", DateToDateString,
1105 "toTimeString", DateToTimeString,
1106 "toLocaleString", DateToLocaleString,
1107 "toLocaleDateString", DateToLocaleDateString,
1108 "toLocaleTimeString", DateToLocaleTimeString,
1109 "valueOf", DateValueOf,
1110 "getTime", DateGetTime,
1111 "getFullYear", DateGetFullYear,
1112 "getUTCFullYear", DateGetUTCFullYear,
1113 "getMonth", DateGetMonth,
1114 "getUTCMonth", DateGetUTCMonth,
1115 "getDate", DateGetDate,
1116 "getUTCDate", DateGetUTCDate,
1117 "getDay", DateGetDay,
1118 "getUTCDay", DateGetUTCDay,
1119 "getHours", DateGetHours,
1120 "getUTCHours", DateGetUTCHours,
1121 "getMinutes", DateGetMinutes,
1122 "getUTCMinutes", DateGetUTCMinutes,
1123 "getSeconds", DateGetSeconds,
1124 "getUTCSeconds", DateGetUTCSeconds,
1125 "getMilliseconds", DateGetMilliseconds,
1126 "getUTCMilliseconds", DateGetUTCMilliseconds,
1127 "getTimezoneOffset", DateGetTimezoneOffset,
1128 "setTime", DateSetTime,
1129 "setMilliseconds", DateSetMilliseconds,
1130 "setUTCMilliseconds", DateSetUTCMilliseconds,
1131 "setSeconds", DateSetSeconds,
1132 "setUTCSeconds", DateSetUTCSeconds,
1133 "setMinutes", DateSetMinutes,
1134 "setUTCMinutes", DateSetUTCMinutes,
1135 "setHours", DateSetHours,
1136 "setUTCHours", DateSetUTCHours,
1137 "setDate", DateSetDate,
1138 "setUTCDate", DateSetUTCDate,
1139 "setMonth", DateSetMonth,
1140 "setUTCMonth", DateSetUTCMonth,
1141 "setFullYear", DateSetFullYear,
1142 "setUTCFullYear", DateSetUTCFullYear,
1143 "toGMTString", DateToGMTString,
1144 "toUTCString", DateToUTCString,
1145 "getYear", DateGetYear,
1146 "setYear", DateSetYear,
1147 "toISOString", DateToISOString,
1148 "toJSON", DateToJSON
1149 ));
1150}
1151
1152SetupDate();