blob: 6b6ed425d3983002c3f1e1779d755b8b71a63bab [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
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100121// local_time_offset is initialized when the DST_offset_cache is missed.
122// It must not be used until after a call to DaylightSavingsOffset().
123// In this way, only one check, for a DST cache miss, is needed.
124var local_time_offset;
125
126
127// Because computing the DST offset is an expensive operation,
128// we keep a cache of the last computed DST offset along with a time interval
Andrei Popescu31002712010-02-23 13:46:05 +0000129// where we know the cache is valid.
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100130// When the cache is valid, local_time_offset is also valid.
Andrei Popescu31002712010-02-23 13:46:05 +0000131var DST_offset_cache = {
132 // Cached DST offset.
133 offset: 0,
134 // Time interval where the cached offset is valid.
135 start: 0, end: -1,
136 // Size of next interval expansion.
137 increment: 0
138};
139
140
141// NOTE: The implementation relies on the fact that no time zones have
142// more than one daylight savings offset change per month.
143// If this function is called with NaN it returns NaN.
144function DaylightSavingsOffset(t) {
145 // Load the cache object from the builtins object.
146 var cache = DST_offset_cache;
147
148 // Cache the start and the end in local variables for fast access.
149 var start = cache.start;
150 var end = cache.end;
151
152 if (start <= t) {
153 // If the time fits in the cached interval, return the cached offset.
154 if (t <= end) return cache.offset;
155
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100156 // If the cache misses, the local_time_offset may not be initialized.
157 if (IS_UNDEFINED(local_time_offset)) {
158 local_time_offset = %DateLocalTimeOffset();
159 }
160
Andrei Popescu31002712010-02-23 13:46:05 +0000161 // Compute a possible new interval end.
162 var new_end = end + cache.increment;
163
164 if (t <= new_end) {
165 var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end));
166 if (cache.offset == end_offset) {
167 // If the offset at the end of the new interval still matches
168 // the offset in the cache, we grow the cached time interval
169 // and return the offset.
170 cache.end = new_end;
171 cache.increment = msPerMonth;
172 return end_offset;
173 } else {
174 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
175 if (offset == end_offset) {
176 // The offset at the given time is equal to the offset at the
177 // new end of the interval, so that means that we've just skipped
178 // the point in time where the DST offset change occurred. Updated
179 // the interval to reflect this and reset the increment.
180 cache.start = t;
181 cache.end = new_end;
182 cache.increment = msPerMonth;
183 } else {
184 // The interval contains a DST offset change and the given time is
185 // before it. Adjust the increment to avoid a linear search for
186 // the offset change point and change the end of the interval.
187 cache.increment /= 3;
188 cache.end = t;
189 }
190 // Update the offset in the cache and return it.
191 cache.offset = offset;
192 return offset;
193 }
194 }
195 }
196
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100197 // If the cache misses, the local_time_offset may not be initialized.
198 if (IS_UNDEFINED(local_time_offset)) {
199 local_time_offset = %DateLocalTimeOffset();
200 }
Andrei Popescu31002712010-02-23 13:46:05 +0000201 // Compute the DST offset for the time and shrink the cache interval
202 // to only contain the time. This allows fast repeated DST offset
203 // computations for the same time.
204 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
205 cache.offset = offset;
206 cache.start = cache.end = t;
207 cache.increment = msPerMonth;
208 return offset;
209}
210
211
212var timezone_cache_time = $NaN;
213var timezone_cache_timezone;
214
215function LocalTimezone(t) {
216 if (NUMBER_IS_NAN(t)) return "";
217 if (t == timezone_cache_time) {
218 return timezone_cache_timezone;
219 }
220 var timezone = %DateLocalTimezone(EquivalentTime(t));
221 timezone_cache_time = t;
222 timezone_cache_timezone = timezone;
223 return timezone;
224}
225
226
227function WeekDay(time) {
228 return Modulo(DAY(time) + 4, 7);
229}
230
Andrei Popescu31002712010-02-23 13:46:05 +0000231
232function LocalTime(time) {
233 if (NUMBER_IS_NAN(time)) return time;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100234 // DaylightSavingsOffset called before local_time_offset used.
235 return time + DaylightSavingsOffset(time) + local_time_offset;
Andrei Popescu31002712010-02-23 13:46:05 +0000236}
237
238function LocalTimeNoCheck(time) {
239 // Inline the DST offset cache checks for speed.
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100240 // The cache is hit, or DaylightSavingsOffset is called,
241 // before local_time_offset is used.
Andrei Popescu31002712010-02-23 13:46:05 +0000242 var cache = DST_offset_cache;
243 if (cache.start <= time && time <= cache.end) {
244 var dst_offset = cache.offset;
245 } else {
246 var dst_offset = DaylightSavingsOffset(time);
247 }
248 return time + local_time_offset + dst_offset;
249}
250
251
252function UTC(time) {
253 if (NUMBER_IS_NAN(time)) return time;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100254 // local_time_offset is needed before the call to DaylightSavingsOffset,
255 // so it may be uninitialized.
256 if (IS_UNDEFINED(local_time_offset)) {
257 local_time_offset = %DateLocalTimeOffset();
258 }
Andrei Popescu31002712010-02-23 13:46:05 +0000259 var tmp = time - local_time_offset;
260 return tmp - DaylightSavingsOffset(tmp);
261}
262
263
264// ECMA 262 - 15.9.1.11
265function MakeTime(hour, min, sec, ms) {
266 if (!$isFinite(hour)) return $NaN;
267 if (!$isFinite(min)) return $NaN;
268 if (!$isFinite(sec)) return $NaN;
269 if (!$isFinite(ms)) return $NaN;
270 return TO_INTEGER(hour) * msPerHour
271 + TO_INTEGER(min) * msPerMinute
272 + TO_INTEGER(sec) * msPerSecond
273 + TO_INTEGER(ms);
274}
275
276
277// ECMA 262 - 15.9.1.12
278function TimeInYear(year) {
279 return DaysInYear(year) * msPerDay;
280}
281
282
283// Compute modified Julian day from year, month, date.
284function ToJulianDay(year, month, date) {
285 var jy = (month > 1) ? year : year - 1;
286 var jm = (month > 1) ? month + 2 : month + 14;
287 var ja = FLOOR(jy / 100);
288 return FLOOR(FLOOR(365.25*jy) + FLOOR(30.6001*jm) + date + 1720995) + 2 - ja + FLOOR(0.25*ja);
289}
290
291var four_year_cycle_table = CalculateDateTable();
292
293
294function CalculateDateTable() {
295 var month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
296 var four_year_cycle_table = new $Array(1461);
297
298 var cumulative = 0;
299 var position = 0;
300 var leap_position = 0;
301 for (var month = 0; month < 12; month++) {
302 var month_bits = month << kMonthShift;
303 var length = month_lengths[month];
304 for (var day = 1; day <= length; day++) {
305 four_year_cycle_table[leap_position] =
306 month_bits + day;
307 four_year_cycle_table[366 + position] =
308 (1 << kYearShift) + month_bits + day;
309 four_year_cycle_table[731 + position] =
310 (2 << kYearShift) + month_bits + day;
311 four_year_cycle_table[1096 + position] =
312 (3 << kYearShift) + month_bits + day;
313 leap_position++;
314 position++;
315 }
316 if (month == 1) {
317 four_year_cycle_table[leap_position++] = month_bits + 29;
318 }
319 }
320 return four_year_cycle_table;
321}
322
323
324// Constructor for creating objects holding year, month, and date.
325// Introduced to ensure the two return points in FromJulianDay match same map.
326function DayTriplet(year, month, date) {
327 this.year = year;
328 this.month = month;
329 this.date = date;
330}
331
332var julian_day_cache_triplet;
333var julian_day_cache_day = $NaN;
334
335// Compute year, month, and day from modified Julian day.
336// The missing days in 1582 are ignored for JavaScript compatibility.
337function FromJulianDay(julian) {
338 if (julian_day_cache_day == julian) {
339 return julian_day_cache_triplet;
340 }
341 var result;
342 // Avoid floating point and non-Smi maths in common case. This is also a period of
343 // time where leap years are very regular. The range is not too large to avoid overflow
344 // when doing the multiply-to-divide trick.
345 if (julian > kDayZeroInJulianDay &&
346 (julian - kDayZeroInJulianDay) < 40177) { // 1970 - 2080
347 var jsimple = (julian - kDayZeroInJulianDay) + 731; // Day 0 is 1st January 1968
348 var y = 1968;
349 // Divide by 1461 by multiplying with 22967 and shifting down by 25!
350 var after_1968 = (jsimple * 22967) >> 25;
351 y += after_1968 << 2;
352 jsimple -= 1461 * after_1968;
353 var four_year_cycle = four_year_cycle_table[jsimple];
354 result = new DayTriplet(y + (four_year_cycle >> kYearShift),
355 (four_year_cycle & kMonthMask) >> kMonthShift,
356 four_year_cycle & kDayMask);
357 } else {
358 var jalpha = FLOOR((julian - 1867216.25) / 36524.25);
359 var jb = julian + 1 + jalpha - FLOOR(0.25 * jalpha) + 1524;
360 var jc = FLOOR(6680.0 + ((jb-2439870) - 122.1)/365.25);
361 var jd = FLOOR(365 * jc + (0.25 * jc));
362 var je = FLOOR((jb - jd)/30.6001);
363 var m = je - 1;
364 if (m > 12) m -= 13;
365 var y = jc - 4715;
366 if (m > 2) { --y; --m; }
367 var d = jb - jd - FLOOR(30.6001 * je);
368 result = new DayTriplet(y, m, d);
369 }
370 julian_day_cache_day = julian;
371 julian_day_cache_triplet = result;
372 return result;
373}
374
375
376// Compute number of days given a year, month, date.
377// Note that month and date can lie outside the normal range.
378// For example:
379// MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
380// MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
381// MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
382function MakeDay(year, month, date) {
383 if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
384
385 // Conversion to integers.
386 year = TO_INTEGER(year);
387 month = TO_INTEGER(month);
388 date = TO_INTEGER(date);
389
390 // Overflow months into year.
391 year = year + FLOOR(month/12);
392 month = month % 12;
393 if (month < 0) {
394 month += 12;
395 }
396
397 // Return days relative to Jan 1 1970.
398 return ToJulianDay(year, month, date) - kDayZeroInJulianDay;
399}
400
401
402// ECMA 262 - 15.9.1.13
403function MakeDate(day, time) {
404 if (!$isFinite(day)) return $NaN;
405 if (!$isFinite(time)) return $NaN;
406 return day * msPerDay + time;
407}
408
409
410// ECMA 262 - 15.9.1.14
411function TimeClip(time) {
412 if (!$isFinite(time)) return $NaN;
413 if ($abs(time) > 8.64E15) return $NaN;
414 return TO_INTEGER(time);
415}
416
417
418// The Date cache is used to limit the cost of parsing the same Date
419// strings over and over again.
420var Date_cache = {
421 // Cached time value.
422 time: $NaN,
423 // Cached year when interpreting the time as a local time. Only
424 // valid when the time matches cached time.
425 year: $NaN,
426 // String input for which the cached time is valid.
427 string: null
428};
429
430
431%SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) {
432 if (!%_IsConstructCall()) {
433 // ECMA 262 - 15.9.2
434 return (new $Date()).toString();
435 }
436
437 // ECMA 262 - 15.9.3
438 var argc = %_ArgumentsLength();
439 var value;
440 if (argc == 0) {
441 value = %DateCurrentTime();
442
443 } else if (argc == 1) {
444 if (IS_NUMBER(year)) {
445 value = TimeClip(year);
446
447 } else if (IS_STRING(year)) {
448 // Probe the Date cache. If we already have a time value for the
449 // given time, we re-use that instead of parsing the string again.
450 var cache = Date_cache;
451 if (cache.string === year) {
452 value = cache.time;
453 } else {
454 value = DateParse(year);
455 if (!NUMBER_IS_NAN(value)) {
456 cache.time = value;
457 cache.year = YEAR_FROM_TIME(LocalTimeNoCheck(value));
458 cache.string = year;
459 }
460 }
461
462 } else {
463 // According to ECMA 262, no hint should be given for this
464 // conversion. However, ToPrimitive defaults to STRING_HINT for
465 // Date objects which will lose precision when the Date
466 // constructor is called with another Date object as its
467 // argument. We therefore use NUMBER_HINT for the conversion,
468 // which is the default for everything else than Date objects.
469 // This makes us behave like KJS and SpiderMonkey.
470 var time = ToPrimitive(year, NUMBER_HINT);
471 value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
472 }
473
474 } else {
475 year = ToNumber(year);
476 month = ToNumber(month);
477 date = argc > 2 ? ToNumber(date) : 1;
478 hours = argc > 3 ? ToNumber(hours) : 0;
479 minutes = argc > 4 ? ToNumber(minutes) : 0;
480 seconds = argc > 5 ? ToNumber(seconds) : 0;
481 ms = argc > 6 ? ToNumber(ms) : 0;
482 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
483 ? 1900 + TO_INTEGER(year) : year;
484 var day = MakeDay(year, month, date);
485 var time = MakeTime(hours, minutes, seconds, ms);
486 value = TimeClip(UTC(MakeDate(day, time)));
487 }
488 %_SetValueOf(this, value);
489});
490
491
492// Helper functions.
493function GetTimeFrom(aDate) {
494 return DATE_VALUE(aDate);
495}
496
497function GetMillisecondsFrom(aDate) {
498 var t = DATE_VALUE(aDate);
499 if (NUMBER_IS_NAN(t)) return t;
500 return MS_FROM_TIME(LocalTimeNoCheck(t));
501}
502
503
504function GetUTCMillisecondsFrom(aDate) {
505 var t = DATE_VALUE(aDate);
506 if (NUMBER_IS_NAN(t)) return t;
507 return MS_FROM_TIME(t);
508}
509
510
511function GetSecondsFrom(aDate) {
512 var t = DATE_VALUE(aDate);
513 if (NUMBER_IS_NAN(t)) return t;
514 return SEC_FROM_TIME(LocalTimeNoCheck(t));
515}
516
517
518function GetUTCSecondsFrom(aDate) {
519 var t = DATE_VALUE(aDate);
520 if (NUMBER_IS_NAN(t)) return t;
521 return SEC_FROM_TIME(t);
522}
523
524
525function GetMinutesFrom(aDate) {
526 var t = DATE_VALUE(aDate);
527 if (NUMBER_IS_NAN(t)) return t;
528 return MIN_FROM_TIME(LocalTimeNoCheck(t));
529}
530
531
532function GetUTCMinutesFrom(aDate) {
533 var t = DATE_VALUE(aDate);
534 if (NUMBER_IS_NAN(t)) return t;
535 return MIN_FROM_TIME(t);
536}
537
538
539function GetHoursFrom(aDate) {
540 var t = DATE_VALUE(aDate);
541 if (NUMBER_IS_NAN(t)) return t;
542 return HOUR_FROM_TIME(LocalTimeNoCheck(t));
543}
544
545
546function GetUTCHoursFrom(aDate) {
547 var t = DATE_VALUE(aDate);
548 if (NUMBER_IS_NAN(t)) return t;
549 return HOUR_FROM_TIME(t);
550}
551
552
553function GetFullYearFrom(aDate) {
554 var t = DATE_VALUE(aDate);
555 if (NUMBER_IS_NAN(t)) return t;
556 var cache = Date_cache;
557 if (cache.time === t) return cache.year;
558 return YEAR_FROM_TIME(LocalTimeNoCheck(t));
559}
560
561
562function GetUTCFullYearFrom(aDate) {
563 var t = DATE_VALUE(aDate);
564 if (NUMBER_IS_NAN(t)) return t;
565 return YEAR_FROM_TIME(t);
566}
567
568
569function GetMonthFrom(aDate) {
570 var t = DATE_VALUE(aDate);
571 if (NUMBER_IS_NAN(t)) return t;
572 return MONTH_FROM_TIME(LocalTimeNoCheck(t));
573}
574
575
576function GetUTCMonthFrom(aDate) {
577 var t = DATE_VALUE(aDate);
578 if (NUMBER_IS_NAN(t)) return t;
579 return MONTH_FROM_TIME(t);
580}
581
582
583function GetDateFrom(aDate) {
584 var t = DATE_VALUE(aDate);
585 if (NUMBER_IS_NAN(t)) return t;
586 return DATE_FROM_TIME(LocalTimeNoCheck(t));
587}
588
589
590function GetUTCDateFrom(aDate) {
591 var t = DATE_VALUE(aDate);
592 if (NUMBER_IS_NAN(t)) return t;
593 return DATE_FROM_TIME(t);
594}
595
596
597%FunctionSetPrototype($Date, new $Date($NaN));
598
599
600var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
601var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
602
603
604function TwoDigitString(value) {
605 return value < 10 ? "0" + value : "" + value;
606}
607
608
609function DateString(time) {
610 var YMD = FromJulianDay(DAY(time) + kDayZeroInJulianDay);
611 return WeekDays[WeekDay(time)] + ' '
612 + Months[YMD.month] + ' '
613 + TwoDigitString(YMD.date) + ' '
614 + YMD.year;
615}
616
617
618var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
619var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
620
621
622function LongDateString(time) {
623 var YMD = FromJulianDay(DAY(time) + kDayZeroInJulianDay);
624 return LongWeekDays[WeekDay(time)] + ', '
625 + LongMonths[YMD.month] + ' '
626 + TwoDigitString(YMD.date) + ', '
627 + YMD.year;
628}
629
630
631function TimeString(time) {
632 return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
633 + TwoDigitString(MIN_FROM_TIME(time)) + ':'
634 + TwoDigitString(SEC_FROM_TIME(time));
635}
636
637
638function LocalTimezoneString(time) {
Ben Murdochd69d2e32010-03-30 12:55:27 +0100639 var old_timezone = timezone_cache_timezone;
640 var timezone = LocalTimezone(time);
641 if (old_timezone && timezone != old_timezone) {
642 // If the timezone string has changed from the one that we cached,
643 // the local time offset may now be wrong. So we need to update it
644 // and try again.
645 local_time_offset = %DateLocalTimeOffset();
646 // We also need to invalidate the DST cache as the new timezone may have
647 // different DST times.
648 var dst_cache = DST_offset_cache;
649 dst_cache.start = 0;
650 dst_cache.end = -1;
651 }
652
Andrei Popescu31002712010-02-23 13:46:05 +0000653 var timezoneOffset =
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100654 (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute;
Andrei Popescu31002712010-02-23 13:46:05 +0000655 var sign = (timezoneOffset >= 0) ? 1 : -1;
656 var hours = FLOOR((sign * timezoneOffset)/60);
657 var min = FLOOR((sign * timezoneOffset)%60);
658 var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
659 TwoDigitString(hours) + TwoDigitString(min);
Ben Murdochd69d2e32010-03-30 12:55:27 +0100660 return gmt + ' (' + timezone + ')';
Andrei Popescu31002712010-02-23 13:46:05 +0000661}
662
663
664function DatePrintString(time) {
665 return DateString(time) + ' ' + TimeString(time);
666}
667
668// -------------------------------------------------------------------
669
670// Reused output buffer. Used when parsing date strings.
671var parse_buffer = $Array(7);
672
673// ECMA 262 - 15.9.4.2
674function DateParse(string) {
675 var arr = %DateParseString(ToString(string), parse_buffer);
676 if (IS_NULL(arr)) return $NaN;
677
678 var day = MakeDay(arr[0], arr[1], arr[2]);
679 var time = MakeTime(arr[3], arr[4], arr[5], 0);
680 var date = MakeDate(day, time);
681
682 if (IS_NULL(arr[6])) {
683 return TimeClip(UTC(date));
684 } else {
685 return TimeClip(date - arr[6] * 1000);
686 }
687}
688
689
690// ECMA 262 - 15.9.4.3
691function DateUTC(year, month, date, hours, minutes, seconds, ms) {
692 year = ToNumber(year);
693 month = ToNumber(month);
694 var argc = %_ArgumentsLength();
695 date = argc > 2 ? ToNumber(date) : 1;
696 hours = argc > 3 ? ToNumber(hours) : 0;
697 minutes = argc > 4 ? ToNumber(minutes) : 0;
698 seconds = argc > 5 ? ToNumber(seconds) : 0;
699 ms = argc > 6 ? ToNumber(ms) : 0;
700 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
701 ? 1900 + TO_INTEGER(year) : year;
702 var day = MakeDay(year, month, date);
703 var time = MakeTime(hours, minutes, seconds, ms);
704 return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
705}
706
707
708// Mozilla-specific extension. Returns the number of milliseconds
709// elapsed since 1 January 1970 00:00:00 UTC.
710function DateNow() {
711 return %DateCurrentTime();
712}
713
714
715// ECMA 262 - 15.9.5.2
716function DateToString() {
717 var t = DATE_VALUE(this);
718 if (NUMBER_IS_NAN(t)) return kInvalidDate;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100719 var time_zone_string = LocalTimezoneString(t); // May update local offset.
720 return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000721}
722
723
724// ECMA 262 - 15.9.5.3
725function DateToDateString() {
726 var t = DATE_VALUE(this);
727 if (NUMBER_IS_NAN(t)) return kInvalidDate;
728 return DateString(LocalTimeNoCheck(t));
729}
730
731
732// ECMA 262 - 15.9.5.4
733function DateToTimeString() {
734 var t = DATE_VALUE(this);
735 if (NUMBER_IS_NAN(t)) return kInvalidDate;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100736 var time_zone_string = LocalTimezoneString(t); // May update local offset.
737 return TimeString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000738}
739
740
741// ECMA 262 - 15.9.5.5
742function DateToLocaleString() {
743 return DateToString.call(this);
744}
745
746
747// ECMA 262 - 15.9.5.6
748function DateToLocaleDateString() {
749 var t = DATE_VALUE(this);
750 if (NUMBER_IS_NAN(t)) return kInvalidDate;
751 return LongDateString(LocalTimeNoCheck(t));
752}
753
754
755// ECMA 262 - 15.9.5.7
756function DateToLocaleTimeString() {
757 var t = DATE_VALUE(this);
758 if (NUMBER_IS_NAN(t)) return kInvalidDate;
759 var lt = LocalTimeNoCheck(t);
760 return TimeString(lt);
761}
762
763
764// ECMA 262 - 15.9.5.8
765function DateValueOf() {
766 return DATE_VALUE(this);
767}
768
769
770// ECMA 262 - 15.9.5.9
Andrei Popescu402d9372010-02-26 13:31:12 +0000771function DateGetTime() {
Andrei Popescu31002712010-02-23 13:46:05 +0000772 return DATE_VALUE(this);
773}
774
775
776// ECMA 262 - 15.9.5.10
777function DateGetFullYear() {
778 return GetFullYearFrom(this)
779}
780
781
782// ECMA 262 - 15.9.5.11
783function DateGetUTCFullYear() {
784 return GetUTCFullYearFrom(this)
785}
786
787
788// ECMA 262 - 15.9.5.12
789function DateGetMonth() {
790 return GetMonthFrom(this);
791}
792
793
794// ECMA 262 - 15.9.5.13
795function DateGetUTCMonth() {
796 return GetUTCMonthFrom(this);
797}
798
799
800// ECMA 262 - 15.9.5.14
801function DateGetDate() {
802 return GetDateFrom(this);
803}
804
805
806// ECMA 262 - 15.9.5.15
807function DateGetUTCDate() {
808 return GetUTCDateFrom(this);
809}
810
811
812// ECMA 262 - 15.9.5.16
813function DateGetDay() {
814 var t = %_ValueOf(this);
815 if (NUMBER_IS_NAN(t)) return t;
816 return WeekDay(LocalTimeNoCheck(t));
817}
818
819
820// ECMA 262 - 15.9.5.17
821function DateGetUTCDay() {
822 var t = %_ValueOf(this);
823 if (NUMBER_IS_NAN(t)) return t;
824 return WeekDay(t);
825}
826
827
828// ECMA 262 - 15.9.5.18
829function DateGetHours() {
830 return GetHoursFrom(this);
831}
832
833
834// ECMA 262 - 15.9.5.19
835function DateGetUTCHours() {
836 return GetUTCHoursFrom(this);
837}
838
839
840// ECMA 262 - 15.9.5.20
841function DateGetMinutes() {
842 return GetMinutesFrom(this);
843}
844
845
846// ECMA 262 - 15.9.5.21
847function DateGetUTCMinutes() {
848 return GetUTCMinutesFrom(this);
849}
850
851
852// ECMA 262 - 15.9.5.22
853function DateGetSeconds() {
854 return GetSecondsFrom(this);
855}
856
857
858// ECMA 262 - 15.9.5.23
859function DateGetUTCSeconds() {
860 return GetUTCSecondsFrom(this);
861}
862
863
864// ECMA 262 - 15.9.5.24
865function DateGetMilliseconds() {
866 return GetMillisecondsFrom(this);
867}
868
869
870// ECMA 262 - 15.9.5.25
871function DateGetUTCMilliseconds() {
872 return GetUTCMillisecondsFrom(this);
873}
874
875
876// ECMA 262 - 15.9.5.26
877function DateGetTimezoneOffset() {
878 var t = DATE_VALUE(this);
879 if (NUMBER_IS_NAN(t)) return t;
880 return (t - LocalTimeNoCheck(t)) / msPerMinute;
881}
882
883
884// ECMA 262 - 15.9.5.27
885function DateSetTime(ms) {
886 if (!IS_DATE(this)) ThrowDateTypeError();
887 return %_SetValueOf(this, TimeClip(ToNumber(ms)));
888}
889
890
891// ECMA 262 - 15.9.5.28
892function DateSetMilliseconds(ms) {
893 var t = LocalTime(DATE_VALUE(this));
894 ms = ToNumber(ms);
895 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
896 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
897}
898
899
900// ECMA 262 - 15.9.5.29
901function DateSetUTCMilliseconds(ms) {
902 var t = DATE_VALUE(this);
903 ms = ToNumber(ms);
904 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
905 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
906}
907
908
909// ECMA 262 - 15.9.5.30
910function DateSetSeconds(sec, ms) {
911 var t = LocalTime(DATE_VALUE(this));
912 sec = ToNumber(sec);
913 ms = %_ArgumentsLength() < 2 ? GetMillisecondsFrom(this) : ToNumber(ms);
914 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
915 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
916}
917
918
919// ECMA 262 - 15.9.5.31
920function DateSetUTCSeconds(sec, ms) {
921 var t = DATE_VALUE(this);
922 sec = ToNumber(sec);
923 ms = %_ArgumentsLength() < 2 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
924 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
925 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
926}
927
928
929// ECMA 262 - 15.9.5.33
930function DateSetMinutes(min, sec, ms) {
931 var t = LocalTime(DATE_VALUE(this));
932 min = ToNumber(min);
933 var argc = %_ArgumentsLength();
934 sec = argc < 2 ? GetSecondsFrom(this) : ToNumber(sec);
935 ms = argc < 3 ? GetMillisecondsFrom(this) : ToNumber(ms);
936 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
937 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
938}
939
940
941// ECMA 262 - 15.9.5.34
942function DateSetUTCMinutes(min, sec, ms) {
943 var t = DATE_VALUE(this);
944 min = ToNumber(min);
945 var argc = %_ArgumentsLength();
946 sec = argc < 2 ? GetUTCSecondsFrom(this) : ToNumber(sec);
947 ms = argc < 3 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
948 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
949 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
950}
951
952
953// ECMA 262 - 15.9.5.35
954function DateSetHours(hour, min, sec, ms) {
955 var t = LocalTime(DATE_VALUE(this));
956 hour = ToNumber(hour);
957 var argc = %_ArgumentsLength();
958 min = argc < 2 ? GetMinutesFrom(this) : ToNumber(min);
959 sec = argc < 3 ? GetSecondsFrom(this) : ToNumber(sec);
960 ms = argc < 4 ? GetMillisecondsFrom(this) : ToNumber(ms);
961 var time = MakeTime(hour, min, sec, ms);
962 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
963}
964
965
966// ECMA 262 - 15.9.5.34
967function DateSetUTCHours(hour, min, sec, ms) {
968 var t = DATE_VALUE(this);
969 hour = ToNumber(hour);
970 var argc = %_ArgumentsLength();
971 min = argc < 2 ? GetUTCMinutesFrom(this) : ToNumber(min);
972 sec = argc < 3 ? GetUTCSecondsFrom(this) : ToNumber(sec);
973 ms = argc < 4 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
974 var time = MakeTime(hour, min, sec, ms);
975 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
976}
977
978
979// ECMA 262 - 15.9.5.36
980function DateSetDate(date) {
981 var t = LocalTime(DATE_VALUE(this));
982 date = ToNumber(date);
983 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
984 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
985}
986
987
988// ECMA 262 - 15.9.5.37
989function DateSetUTCDate(date) {
990 var t = DATE_VALUE(this);
991 date = ToNumber(date);
992 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
993 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
994}
995
996
997// ECMA 262 - 15.9.5.38
998function DateSetMonth(month, date) {
999 var t = LocalTime(DATE_VALUE(this));
1000 month = ToNumber(month);
1001 date = %_ArgumentsLength() < 2 ? GetDateFrom(this) : ToNumber(date);
1002 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
1003 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
1004}
1005
1006
1007// ECMA 262 - 15.9.5.39
1008function DateSetUTCMonth(month, date) {
1009 var t = DATE_VALUE(this);
1010 month = ToNumber(month);
1011 date = %_ArgumentsLength() < 2 ? GetUTCDateFrom(this) : ToNumber(date);
1012 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
1013 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
1014}
1015
1016
1017// ECMA 262 - 15.9.5.40
1018function DateSetFullYear(year, month, date) {
1019 var t = DATE_VALUE(this);
1020 t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
1021 year = ToNumber(year);
1022 var argc = %_ArgumentsLength();
1023 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
1024 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
1025 var day = MakeDay(year, month, date);
1026 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
1027}
1028
1029
1030// ECMA 262 - 15.9.5.41
1031function DateSetUTCFullYear(year, month, date) {
1032 var t = DATE_VALUE(this);
1033 if (NUMBER_IS_NAN(t)) t = 0;
1034 var argc = %_ArgumentsLength();
1035 year = ToNumber(year);
1036 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
1037 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
1038 var day = MakeDay(year, month, date);
1039 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
1040}
1041
1042
1043// ECMA 262 - 15.9.5.42
1044function DateToUTCString() {
1045 var t = DATE_VALUE(this);
1046 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1047 // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
1048 return WeekDays[WeekDay(t)] + ', '
1049 + TwoDigitString(DATE_FROM_TIME(t)) + ' '
1050 + Months[MONTH_FROM_TIME(t)] + ' '
1051 + YEAR_FROM_TIME(t) + ' '
1052 + TimeString(t) + ' GMT';
1053}
1054
1055
1056// ECMA 262 - B.2.4
1057function DateGetYear() {
1058 var t = DATE_VALUE(this);
1059 if (NUMBER_IS_NAN(t)) return $NaN;
1060 return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900;
1061}
1062
1063
1064// ECMA 262 - B.2.5
1065function DateSetYear(year) {
1066 var t = LocalTime(DATE_VALUE(this));
1067 if (NUMBER_IS_NAN(t)) t = 0;
1068 year = ToNumber(year);
1069 if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
1070 year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
1071 ? 1900 + TO_INTEGER(year) : year;
1072 var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
1073 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
1074}
1075
1076
1077// ECMA 262 - B.2.6
1078//
1079// Notice that this does not follow ECMA 262 completely. ECMA 262
1080// says that toGMTString should be the same Function object as
1081// toUTCString. JSC does not do this, so for compatibility we do not
1082// do that either. Instead, we create a new function whose name
1083// property will return toGMTString.
1084function DateToGMTString() {
1085 return DateToUTCString.call(this);
1086}
1087
1088
1089function PadInt(n, digits) {
1090 if (digits == 1) return n;
1091 return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
1092}
1093
1094
1095function DateToISOString() {
1096 var t = DATE_VALUE(this);
1097 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1098 return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) +
1099 '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) +
1100 ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) +
1101 '.' + PadInt(this.getUTCMilliseconds(), 3) +
1102 'Z';
1103}
1104
1105
1106function DateToJSON(key) {
1107 return CheckJSONPrimitive(this.toISOString());
1108}
1109
1110
1111// -------------------------------------------------------------------
1112
1113function SetupDate() {
1114 // Setup non-enumerable properties of the Date object itself.
1115 InstallFunctions($Date, DONT_ENUM, $Array(
1116 "UTC", DateUTC,
1117 "parse", DateParse,
1118 "now", DateNow
1119 ));
1120
1121 // Setup non-enumerable constructor property of the Date prototype object.
1122 %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
1123
1124 // Setup non-enumerable functions of the Date prototype object and
1125 // set their names.
1126 InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array(
1127 "toString", DateToString,
1128 "toDateString", DateToDateString,
1129 "toTimeString", DateToTimeString,
1130 "toLocaleString", DateToLocaleString,
1131 "toLocaleDateString", DateToLocaleDateString,
1132 "toLocaleTimeString", DateToLocaleTimeString,
1133 "valueOf", DateValueOf,
1134 "getTime", DateGetTime,
1135 "getFullYear", DateGetFullYear,
1136 "getUTCFullYear", DateGetUTCFullYear,
1137 "getMonth", DateGetMonth,
1138 "getUTCMonth", DateGetUTCMonth,
1139 "getDate", DateGetDate,
1140 "getUTCDate", DateGetUTCDate,
1141 "getDay", DateGetDay,
1142 "getUTCDay", DateGetUTCDay,
1143 "getHours", DateGetHours,
1144 "getUTCHours", DateGetUTCHours,
1145 "getMinutes", DateGetMinutes,
1146 "getUTCMinutes", DateGetUTCMinutes,
1147 "getSeconds", DateGetSeconds,
1148 "getUTCSeconds", DateGetUTCSeconds,
1149 "getMilliseconds", DateGetMilliseconds,
1150 "getUTCMilliseconds", DateGetUTCMilliseconds,
1151 "getTimezoneOffset", DateGetTimezoneOffset,
1152 "setTime", DateSetTime,
1153 "setMilliseconds", DateSetMilliseconds,
1154 "setUTCMilliseconds", DateSetUTCMilliseconds,
1155 "setSeconds", DateSetSeconds,
1156 "setUTCSeconds", DateSetUTCSeconds,
1157 "setMinutes", DateSetMinutes,
1158 "setUTCMinutes", DateSetUTCMinutes,
1159 "setHours", DateSetHours,
1160 "setUTCHours", DateSetUTCHours,
1161 "setDate", DateSetDate,
1162 "setUTCDate", DateSetUTCDate,
1163 "setMonth", DateSetMonth,
1164 "setUTCMonth", DateSetUTCMonth,
1165 "setFullYear", DateSetFullYear,
1166 "setUTCFullYear", DateSetUTCFullYear,
1167 "toGMTString", DateToGMTString,
1168 "toUTCString", DateToUTCString,
1169 "getYear", DateGetYear,
1170 "setYear", DateSetYear,
1171 "toISOString", DateToISOString,
1172 "toJSON", DateToJSON
1173 ));
1174}
1175
1176SetupDate();