blob: 83fca27cd4fb3f263bf52aba628556da6f0c490f [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;
Steve Block6ded16b2010-05-10 14:33:55 +0100116
117 var day = MakeDay(EquivalentYear(YEAR_FROM_TIME(t)),
118 MONTH_FROM_TIME(t),
119 DATE_FROM_TIME(t));
120 return MakeDate(day, TimeWithinDay(t));
Andrei Popescu31002712010-02-23 13:46:05 +0000121}
122
123
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100124// local_time_offset is initialized when the DST_offset_cache is missed.
125// It must not be used until after a call to DaylightSavingsOffset().
126// In this way, only one check, for a DST cache miss, is needed.
127var local_time_offset;
128
129
130// Because computing the DST offset is an expensive operation,
131// we keep a cache of the last computed DST offset along with a time interval
Andrei Popescu31002712010-02-23 13:46:05 +0000132// where we know the cache is valid.
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100133// When the cache is valid, local_time_offset is also valid.
Andrei Popescu31002712010-02-23 13:46:05 +0000134var DST_offset_cache = {
135 // Cached DST offset.
136 offset: 0,
137 // Time interval where the cached offset is valid.
138 start: 0, end: -1,
139 // Size of next interval expansion.
140 increment: 0
141};
142
143
144// NOTE: The implementation relies on the fact that no time zones have
145// more than one daylight savings offset change per month.
146// If this function is called with NaN it returns NaN.
147function DaylightSavingsOffset(t) {
148 // Load the cache object from the builtins object.
149 var cache = DST_offset_cache;
150
151 // Cache the start and the end in local variables for fast access.
152 var start = cache.start;
153 var end = cache.end;
154
155 if (start <= t) {
156 // If the time fits in the cached interval, return the cached offset.
157 if (t <= end) return cache.offset;
158
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100159 // If the cache misses, the local_time_offset may not be initialized.
160 if (IS_UNDEFINED(local_time_offset)) {
161 local_time_offset = %DateLocalTimeOffset();
162 }
163
Andrei Popescu31002712010-02-23 13:46:05 +0000164 // Compute a possible new interval end.
165 var new_end = end + cache.increment;
166
167 if (t <= new_end) {
168 var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end));
169 if (cache.offset == end_offset) {
170 // If the offset at the end of the new interval still matches
171 // the offset in the cache, we grow the cached time interval
172 // and return the offset.
173 cache.end = new_end;
174 cache.increment = msPerMonth;
175 return end_offset;
176 } else {
177 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
178 if (offset == end_offset) {
179 // The offset at the given time is equal to the offset at the
180 // new end of the interval, so that means that we've just skipped
181 // the point in time where the DST offset change occurred. Updated
182 // the interval to reflect this and reset the increment.
183 cache.start = t;
184 cache.end = new_end;
185 cache.increment = msPerMonth;
186 } else {
187 // The interval contains a DST offset change and the given time is
188 // before it. Adjust the increment to avoid a linear search for
189 // the offset change point and change the end of the interval.
190 cache.increment /= 3;
191 cache.end = t;
192 }
193 // Update the offset in the cache and return it.
194 cache.offset = offset;
195 return offset;
196 }
197 }
198 }
199
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100200 // If the cache misses, the local_time_offset may not be initialized.
201 if (IS_UNDEFINED(local_time_offset)) {
202 local_time_offset = %DateLocalTimeOffset();
203 }
Andrei Popescu31002712010-02-23 13:46:05 +0000204 // Compute the DST offset for the time and shrink the cache interval
205 // to only contain the time. This allows fast repeated DST offset
206 // computations for the same time.
207 var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
208 cache.offset = offset;
209 cache.start = cache.end = t;
210 cache.increment = msPerMonth;
211 return offset;
212}
213
214
215var timezone_cache_time = $NaN;
216var timezone_cache_timezone;
217
218function LocalTimezone(t) {
219 if (NUMBER_IS_NAN(t)) return "";
220 if (t == timezone_cache_time) {
221 return timezone_cache_timezone;
222 }
223 var timezone = %DateLocalTimezone(EquivalentTime(t));
224 timezone_cache_time = t;
225 timezone_cache_timezone = timezone;
226 return timezone;
227}
228
229
230function WeekDay(time) {
231 return Modulo(DAY(time) + 4, 7);
232}
233
Andrei Popescu31002712010-02-23 13:46:05 +0000234
235function LocalTime(time) {
236 if (NUMBER_IS_NAN(time)) return time;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100237 // DaylightSavingsOffset called before local_time_offset used.
238 return time + DaylightSavingsOffset(time) + local_time_offset;
Andrei Popescu31002712010-02-23 13:46:05 +0000239}
240
Kristian Monsen25f61362010-05-21 11:50:48 +0100241
242var ltcache = {
243 key: null,
244 val: null
245};
246
Andrei Popescu31002712010-02-23 13:46:05 +0000247function LocalTimeNoCheck(time) {
Kristian Monsen25f61362010-05-21 11:50:48 +0100248 var ltc = ltcache;
249 if (%_ObjectEquals(time, ltc.key)) return ltc.val;
Steve Block6ded16b2010-05-10 14:33:55 +0100250 if (time < -MAX_TIME_MS || time > MAX_TIME_MS) {
251 return $NaN;
252 }
253
Andrei Popescu31002712010-02-23 13:46:05 +0000254 // Inline the DST offset cache checks for speed.
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100255 // The cache is hit, or DaylightSavingsOffset is called,
256 // before local_time_offset is used.
Andrei Popescu31002712010-02-23 13:46:05 +0000257 var cache = DST_offset_cache;
258 if (cache.start <= time && time <= cache.end) {
259 var dst_offset = cache.offset;
260 } else {
261 var dst_offset = DaylightSavingsOffset(time);
262 }
Kristian Monsen25f61362010-05-21 11:50:48 +0100263 ltc.key = time;
264 return (ltc.val = time + local_time_offset + dst_offset);
Andrei Popescu31002712010-02-23 13:46:05 +0000265}
266
267
268function UTC(time) {
269 if (NUMBER_IS_NAN(time)) return time;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100270 // local_time_offset is needed before the call to DaylightSavingsOffset,
271 // so it may be uninitialized.
272 if (IS_UNDEFINED(local_time_offset)) {
273 local_time_offset = %DateLocalTimeOffset();
274 }
Andrei Popescu31002712010-02-23 13:46:05 +0000275 var tmp = time - local_time_offset;
276 return tmp - DaylightSavingsOffset(tmp);
277}
278
279
280// ECMA 262 - 15.9.1.11
281function MakeTime(hour, min, sec, ms) {
282 if (!$isFinite(hour)) return $NaN;
283 if (!$isFinite(min)) return $NaN;
284 if (!$isFinite(sec)) return $NaN;
285 if (!$isFinite(ms)) return $NaN;
286 return TO_INTEGER(hour) * msPerHour
287 + TO_INTEGER(min) * msPerMinute
288 + TO_INTEGER(sec) * msPerSecond
289 + TO_INTEGER(ms);
290}
291
292
293// ECMA 262 - 15.9.1.12
294function TimeInYear(year) {
295 return DaysInYear(year) * msPerDay;
296}
297
298
Steve Block6ded16b2010-05-10 14:33:55 +0100299var ymd_from_time_cache = [$NaN, $NaN, $NaN];
300var ymd_from_time_cached_time = $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000301
Steve Block6ded16b2010-05-10 14:33:55 +0100302function YearFromTime(t) {
303 if (t !== ymd_from_time_cached_time) {
304 if (!$isFinite(t)) {
305 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000306 }
Steve Block6ded16b2010-05-10 14:33:55 +0100307
308 %DateYMDFromTime(t, ymd_from_time_cache);
309 ymd_from_time_cached_time = t
310 }
311
312 return ymd_from_time_cache[0];
313}
314
315function MonthFromTime(t) {
316 if (t !== ymd_from_time_cached_time) {
317 if (!$isFinite(t)) {
318 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000319 }
Steve Block6ded16b2010-05-10 14:33:55 +0100320 %DateYMDFromTime(t, ymd_from_time_cache);
321 ymd_from_time_cached_time = t
Andrei Popescu31002712010-02-23 13:46:05 +0000322 }
Steve Block6ded16b2010-05-10 14:33:55 +0100323
324 return ymd_from_time_cache[1];
Andrei Popescu31002712010-02-23 13:46:05 +0000325}
326
Steve Block6ded16b2010-05-10 14:33:55 +0100327function DateFromTime(t) {
328 if (t !== ymd_from_time_cached_time) {
329 if (!$isFinite(t)) {
330 return $NaN;
331 }
Andrei Popescu31002712010-02-23 13:46:05 +0000332
Steve Block6ded16b2010-05-10 14:33:55 +0100333 %DateYMDFromTime(t, ymd_from_time_cache);
334 ymd_from_time_cached_time = t
Andrei Popescu31002712010-02-23 13:46:05 +0000335 }
Steve Block6ded16b2010-05-10 14:33:55 +0100336
337 return ymd_from_time_cache[2];
Andrei Popescu31002712010-02-23 13:46:05 +0000338}
339
340
341// Compute number of days given a year, month, date.
342// Note that month and date can lie outside the normal range.
343// For example:
344// MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
345// MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
346// MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
347function MakeDay(year, month, date) {
348 if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
349
Steve Block8defd9f2010-07-08 12:39:36 +0100350 // Convert to integer and map -0 to 0.
351 year = TO_INTEGER_MAP_MINUS_ZERO(year);
352 month = TO_INTEGER_MAP_MINUS_ZERO(month);
353 date = TO_INTEGER_MAP_MINUS_ZERO(date);
Andrei Popescu31002712010-02-23 13:46:05 +0000354
Steve Block6ded16b2010-05-10 14:33:55 +0100355 if (year < kMinYear || year > kMaxYear ||
356 month < kMinMonth || month > kMaxMonth ||
357 date < kMinDate || date > kMaxDate) {
358 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000359 }
360
Steve Block6ded16b2010-05-10 14:33:55 +0100361 // Now we rely on year, month and date being SMIs.
362 return %DateMakeDay(year, month, date);
Andrei Popescu31002712010-02-23 13:46:05 +0000363}
364
365
366// ECMA 262 - 15.9.1.13
367function MakeDate(day, time) {
368 if (!$isFinite(day)) return $NaN;
369 if (!$isFinite(time)) return $NaN;
370 return day * msPerDay + time;
371}
372
373
374// ECMA 262 - 15.9.1.14
375function TimeClip(time) {
376 if (!$isFinite(time)) return $NaN;
377 if ($abs(time) > 8.64E15) return $NaN;
378 return TO_INTEGER(time);
379}
380
381
382// The Date cache is used to limit the cost of parsing the same Date
383// strings over and over again.
384var Date_cache = {
385 // Cached time value.
386 time: $NaN,
387 // Cached year when interpreting the time as a local time. Only
388 // valid when the time matches cached time.
389 year: $NaN,
390 // String input for which the cached time is valid.
391 string: null
392};
393
394
395%SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) {
396 if (!%_IsConstructCall()) {
397 // ECMA 262 - 15.9.2
398 return (new $Date()).toString();
399 }
400
401 // ECMA 262 - 15.9.3
402 var argc = %_ArgumentsLength();
403 var value;
404 if (argc == 0) {
405 value = %DateCurrentTime();
406
407 } else if (argc == 1) {
408 if (IS_NUMBER(year)) {
409 value = TimeClip(year);
410
411 } else if (IS_STRING(year)) {
412 // Probe the Date cache. If we already have a time value for the
413 // given time, we re-use that instead of parsing the string again.
414 var cache = Date_cache;
415 if (cache.string === year) {
416 value = cache.time;
417 } else {
418 value = DateParse(year);
419 if (!NUMBER_IS_NAN(value)) {
420 cache.time = value;
421 cache.year = YEAR_FROM_TIME(LocalTimeNoCheck(value));
422 cache.string = year;
423 }
424 }
425
426 } else {
427 // According to ECMA 262, no hint should be given for this
428 // conversion. However, ToPrimitive defaults to STRING_HINT for
429 // Date objects which will lose precision when the Date
430 // constructor is called with another Date object as its
431 // argument. We therefore use NUMBER_HINT for the conversion,
432 // which is the default for everything else than Date objects.
433 // This makes us behave like KJS and SpiderMonkey.
434 var time = ToPrimitive(year, NUMBER_HINT);
435 value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
436 }
437
438 } else {
439 year = ToNumber(year);
440 month = ToNumber(month);
441 date = argc > 2 ? ToNumber(date) : 1;
442 hours = argc > 3 ? ToNumber(hours) : 0;
443 minutes = argc > 4 ? ToNumber(minutes) : 0;
444 seconds = argc > 5 ? ToNumber(seconds) : 0;
445 ms = argc > 6 ? ToNumber(ms) : 0;
446 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
447 ? 1900 + TO_INTEGER(year) : year;
448 var day = MakeDay(year, month, date);
449 var time = MakeTime(hours, minutes, seconds, ms);
450 value = TimeClip(UTC(MakeDate(day, time)));
451 }
452 %_SetValueOf(this, value);
453});
454
455
456// Helper functions.
457function GetTimeFrom(aDate) {
458 return DATE_VALUE(aDate);
459}
460
461function GetMillisecondsFrom(aDate) {
462 var t = DATE_VALUE(aDate);
463 if (NUMBER_IS_NAN(t)) return t;
464 return MS_FROM_TIME(LocalTimeNoCheck(t));
465}
466
467
468function GetUTCMillisecondsFrom(aDate) {
469 var t = DATE_VALUE(aDate);
470 if (NUMBER_IS_NAN(t)) return t;
471 return MS_FROM_TIME(t);
472}
473
474
475function GetSecondsFrom(aDate) {
476 var t = DATE_VALUE(aDate);
477 if (NUMBER_IS_NAN(t)) return t;
478 return SEC_FROM_TIME(LocalTimeNoCheck(t));
479}
480
481
482function GetUTCSecondsFrom(aDate) {
483 var t = DATE_VALUE(aDate);
484 if (NUMBER_IS_NAN(t)) return t;
485 return SEC_FROM_TIME(t);
486}
487
488
489function GetMinutesFrom(aDate) {
490 var t = DATE_VALUE(aDate);
491 if (NUMBER_IS_NAN(t)) return t;
492 return MIN_FROM_TIME(LocalTimeNoCheck(t));
493}
494
495
496function GetUTCMinutesFrom(aDate) {
497 var t = DATE_VALUE(aDate);
498 if (NUMBER_IS_NAN(t)) return t;
499 return MIN_FROM_TIME(t);
500}
501
502
503function GetHoursFrom(aDate) {
504 var t = DATE_VALUE(aDate);
505 if (NUMBER_IS_NAN(t)) return t;
506 return HOUR_FROM_TIME(LocalTimeNoCheck(t));
507}
508
509
510function GetUTCHoursFrom(aDate) {
511 var t = DATE_VALUE(aDate);
512 if (NUMBER_IS_NAN(t)) return t;
513 return HOUR_FROM_TIME(t);
514}
515
516
517function GetFullYearFrom(aDate) {
518 var t = DATE_VALUE(aDate);
519 if (NUMBER_IS_NAN(t)) return t;
520 var cache = Date_cache;
521 if (cache.time === t) return cache.year;
522 return YEAR_FROM_TIME(LocalTimeNoCheck(t));
523}
524
525
526function GetUTCFullYearFrom(aDate) {
527 var t = DATE_VALUE(aDate);
528 if (NUMBER_IS_NAN(t)) return t;
529 return YEAR_FROM_TIME(t);
530}
531
532
533function GetMonthFrom(aDate) {
534 var t = DATE_VALUE(aDate);
535 if (NUMBER_IS_NAN(t)) return t;
536 return MONTH_FROM_TIME(LocalTimeNoCheck(t));
537}
538
539
540function GetUTCMonthFrom(aDate) {
541 var t = DATE_VALUE(aDate);
542 if (NUMBER_IS_NAN(t)) return t;
543 return MONTH_FROM_TIME(t);
544}
545
546
547function GetDateFrom(aDate) {
548 var t = DATE_VALUE(aDate);
549 if (NUMBER_IS_NAN(t)) return t;
550 return DATE_FROM_TIME(LocalTimeNoCheck(t));
551}
552
553
554function GetUTCDateFrom(aDate) {
555 var t = DATE_VALUE(aDate);
556 if (NUMBER_IS_NAN(t)) return t;
557 return DATE_FROM_TIME(t);
558}
559
560
561%FunctionSetPrototype($Date, new $Date($NaN));
562
563
564var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
565var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
566
567
568function TwoDigitString(value) {
569 return value < 10 ? "0" + value : "" + value;
570}
571
572
573function DateString(time) {
Andrei Popescu31002712010-02-23 13:46:05 +0000574 return WeekDays[WeekDay(time)] + ' '
Steve Block6ded16b2010-05-10 14:33:55 +0100575 + Months[MonthFromTime(time)] + ' '
576 + TwoDigitString(DateFromTime(time)) + ' '
577 + YearFromTime(time);
Andrei Popescu31002712010-02-23 13:46:05 +0000578}
579
580
581var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
582var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
583
584
585function LongDateString(time) {
Andrei Popescu31002712010-02-23 13:46:05 +0000586 return LongWeekDays[WeekDay(time)] + ', '
Steve Block6ded16b2010-05-10 14:33:55 +0100587 + LongMonths[MonthFromTime(time)] + ' '
588 + TwoDigitString(DateFromTime(time)) + ', '
589 + YearFromTime(time);
Andrei Popescu31002712010-02-23 13:46:05 +0000590}
591
592
593function TimeString(time) {
594 return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
595 + TwoDigitString(MIN_FROM_TIME(time)) + ':'
596 + TwoDigitString(SEC_FROM_TIME(time));
597}
598
599
600function LocalTimezoneString(time) {
Ben Murdochd69d2e32010-03-30 12:55:27 +0100601 var old_timezone = timezone_cache_timezone;
602 var timezone = LocalTimezone(time);
603 if (old_timezone && timezone != old_timezone) {
604 // If the timezone string has changed from the one that we cached,
605 // the local time offset may now be wrong. So we need to update it
606 // and try again.
607 local_time_offset = %DateLocalTimeOffset();
608 // We also need to invalidate the DST cache as the new timezone may have
609 // different DST times.
610 var dst_cache = DST_offset_cache;
611 dst_cache.start = 0;
612 dst_cache.end = -1;
613 }
614
Andrei Popescu31002712010-02-23 13:46:05 +0000615 var timezoneOffset =
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100616 (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute;
Andrei Popescu31002712010-02-23 13:46:05 +0000617 var sign = (timezoneOffset >= 0) ? 1 : -1;
618 var hours = FLOOR((sign * timezoneOffset)/60);
619 var min = FLOOR((sign * timezoneOffset)%60);
620 var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
621 TwoDigitString(hours) + TwoDigitString(min);
Ben Murdochd69d2e32010-03-30 12:55:27 +0100622 return gmt + ' (' + timezone + ')';
Andrei Popescu31002712010-02-23 13:46:05 +0000623}
624
625
626function DatePrintString(time) {
627 return DateString(time) + ' ' + TimeString(time);
628}
629
630// -------------------------------------------------------------------
631
632// Reused output buffer. Used when parsing date strings.
Steve Block6ded16b2010-05-10 14:33:55 +0100633var parse_buffer = $Array(8);
Andrei Popescu31002712010-02-23 13:46:05 +0000634
635// ECMA 262 - 15.9.4.2
636function DateParse(string) {
637 var arr = %DateParseString(ToString(string), parse_buffer);
638 if (IS_NULL(arr)) return $NaN;
639
640 var day = MakeDay(arr[0], arr[1], arr[2]);
Steve Block6ded16b2010-05-10 14:33:55 +0100641 var time = MakeTime(arr[3], arr[4], arr[5], arr[6]);
Andrei Popescu31002712010-02-23 13:46:05 +0000642 var date = MakeDate(day, time);
643
Steve Block6ded16b2010-05-10 14:33:55 +0100644 if (IS_NULL(arr[7])) {
Andrei Popescu31002712010-02-23 13:46:05 +0000645 return TimeClip(UTC(date));
646 } else {
Steve Block6ded16b2010-05-10 14:33:55 +0100647 return TimeClip(date - arr[7] * 1000);
Andrei Popescu31002712010-02-23 13:46:05 +0000648 }
649}
650
651
652// ECMA 262 - 15.9.4.3
653function DateUTC(year, month, date, hours, minutes, seconds, ms) {
654 year = ToNumber(year);
655 month = ToNumber(month);
656 var argc = %_ArgumentsLength();
657 date = argc > 2 ? ToNumber(date) : 1;
658 hours = argc > 3 ? ToNumber(hours) : 0;
659 minutes = argc > 4 ? ToNumber(minutes) : 0;
660 seconds = argc > 5 ? ToNumber(seconds) : 0;
661 ms = argc > 6 ? ToNumber(ms) : 0;
662 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
663 ? 1900 + TO_INTEGER(year) : year;
664 var day = MakeDay(year, month, date);
665 var time = MakeTime(hours, minutes, seconds, ms);
666 return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
667}
668
669
670// Mozilla-specific extension. Returns the number of milliseconds
671// elapsed since 1 January 1970 00:00:00 UTC.
672function DateNow() {
673 return %DateCurrentTime();
674}
675
676
677// ECMA 262 - 15.9.5.2
678function DateToString() {
679 var t = DATE_VALUE(this);
680 if (NUMBER_IS_NAN(t)) return kInvalidDate;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100681 var time_zone_string = LocalTimezoneString(t); // May update local offset.
682 return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000683}
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;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100698 var time_zone_string = LocalTimezoneString(t); // May update local offset.
699 return TimeString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000700}
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();