blob: e780cb86a5435ed7247edc86d1aa7cdbf035aaa8 [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
Andrei Popescu31002712010-02-23 13:46:05 +0000350 year = TO_INTEGER(year);
351 month = TO_INTEGER(month);
352 date = TO_INTEGER(date);
353
Steve Block6ded16b2010-05-10 14:33:55 +0100354 if (year < kMinYear || year > kMaxYear ||
355 month < kMinMonth || month > kMaxMonth ||
356 date < kMinDate || date > kMaxDate) {
357 return $NaN;
Andrei Popescu31002712010-02-23 13:46:05 +0000358 }
359
Steve Block6ded16b2010-05-10 14:33:55 +0100360 // Now we rely on year, month and date being SMIs.
361 return %DateMakeDay(year, month, date);
Andrei Popescu31002712010-02-23 13:46:05 +0000362}
363
364
365// ECMA 262 - 15.9.1.13
366function MakeDate(day, time) {
367 if (!$isFinite(day)) return $NaN;
368 if (!$isFinite(time)) return $NaN;
369 return day * msPerDay + time;
370}
371
372
373// ECMA 262 - 15.9.1.14
374function TimeClip(time) {
375 if (!$isFinite(time)) return $NaN;
376 if ($abs(time) > 8.64E15) return $NaN;
377 return TO_INTEGER(time);
378}
379
380
381// The Date cache is used to limit the cost of parsing the same Date
382// strings over and over again.
383var Date_cache = {
384 // Cached time value.
385 time: $NaN,
386 // Cached year when interpreting the time as a local time. Only
387 // valid when the time matches cached time.
388 year: $NaN,
389 // String input for which the cached time is valid.
390 string: null
391};
392
393
394%SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) {
395 if (!%_IsConstructCall()) {
396 // ECMA 262 - 15.9.2
397 return (new $Date()).toString();
398 }
399
400 // ECMA 262 - 15.9.3
401 var argc = %_ArgumentsLength();
402 var value;
403 if (argc == 0) {
404 value = %DateCurrentTime();
405
406 } else if (argc == 1) {
407 if (IS_NUMBER(year)) {
408 value = TimeClip(year);
409
410 } else if (IS_STRING(year)) {
411 // Probe the Date cache. If we already have a time value for the
412 // given time, we re-use that instead of parsing the string again.
413 var cache = Date_cache;
414 if (cache.string === year) {
415 value = cache.time;
416 } else {
417 value = DateParse(year);
418 if (!NUMBER_IS_NAN(value)) {
419 cache.time = value;
420 cache.year = YEAR_FROM_TIME(LocalTimeNoCheck(value));
421 cache.string = year;
422 }
423 }
424
425 } else {
426 // According to ECMA 262, no hint should be given for this
427 // conversion. However, ToPrimitive defaults to STRING_HINT for
428 // Date objects which will lose precision when the Date
429 // constructor is called with another Date object as its
430 // argument. We therefore use NUMBER_HINT for the conversion,
431 // which is the default for everything else than Date objects.
432 // This makes us behave like KJS and SpiderMonkey.
433 var time = ToPrimitive(year, NUMBER_HINT);
434 value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
435 }
436
437 } else {
438 year = ToNumber(year);
439 month = ToNumber(month);
440 date = argc > 2 ? ToNumber(date) : 1;
441 hours = argc > 3 ? ToNumber(hours) : 0;
442 minutes = argc > 4 ? ToNumber(minutes) : 0;
443 seconds = argc > 5 ? ToNumber(seconds) : 0;
444 ms = argc > 6 ? ToNumber(ms) : 0;
445 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
446 ? 1900 + TO_INTEGER(year) : year;
447 var day = MakeDay(year, month, date);
448 var time = MakeTime(hours, minutes, seconds, ms);
449 value = TimeClip(UTC(MakeDate(day, time)));
450 }
451 %_SetValueOf(this, value);
452});
453
454
455// Helper functions.
456function GetTimeFrom(aDate) {
457 return DATE_VALUE(aDate);
458}
459
460function GetMillisecondsFrom(aDate) {
461 var t = DATE_VALUE(aDate);
462 if (NUMBER_IS_NAN(t)) return t;
463 return MS_FROM_TIME(LocalTimeNoCheck(t));
464}
465
466
467function GetUTCMillisecondsFrom(aDate) {
468 var t = DATE_VALUE(aDate);
469 if (NUMBER_IS_NAN(t)) return t;
470 return MS_FROM_TIME(t);
471}
472
473
474function GetSecondsFrom(aDate) {
475 var t = DATE_VALUE(aDate);
476 if (NUMBER_IS_NAN(t)) return t;
477 return SEC_FROM_TIME(LocalTimeNoCheck(t));
478}
479
480
481function GetUTCSecondsFrom(aDate) {
482 var t = DATE_VALUE(aDate);
483 if (NUMBER_IS_NAN(t)) return t;
484 return SEC_FROM_TIME(t);
485}
486
487
488function GetMinutesFrom(aDate) {
489 var t = DATE_VALUE(aDate);
490 if (NUMBER_IS_NAN(t)) return t;
491 return MIN_FROM_TIME(LocalTimeNoCheck(t));
492}
493
494
495function GetUTCMinutesFrom(aDate) {
496 var t = DATE_VALUE(aDate);
497 if (NUMBER_IS_NAN(t)) return t;
498 return MIN_FROM_TIME(t);
499}
500
501
502function GetHoursFrom(aDate) {
503 var t = DATE_VALUE(aDate);
504 if (NUMBER_IS_NAN(t)) return t;
505 return HOUR_FROM_TIME(LocalTimeNoCheck(t));
506}
507
508
509function GetUTCHoursFrom(aDate) {
510 var t = DATE_VALUE(aDate);
511 if (NUMBER_IS_NAN(t)) return t;
512 return HOUR_FROM_TIME(t);
513}
514
515
516function GetFullYearFrom(aDate) {
517 var t = DATE_VALUE(aDate);
518 if (NUMBER_IS_NAN(t)) return t;
519 var cache = Date_cache;
520 if (cache.time === t) return cache.year;
521 return YEAR_FROM_TIME(LocalTimeNoCheck(t));
522}
523
524
525function GetUTCFullYearFrom(aDate) {
526 var t = DATE_VALUE(aDate);
527 if (NUMBER_IS_NAN(t)) return t;
528 return YEAR_FROM_TIME(t);
529}
530
531
532function GetMonthFrom(aDate) {
533 var t = DATE_VALUE(aDate);
534 if (NUMBER_IS_NAN(t)) return t;
535 return MONTH_FROM_TIME(LocalTimeNoCheck(t));
536}
537
538
539function GetUTCMonthFrom(aDate) {
540 var t = DATE_VALUE(aDate);
541 if (NUMBER_IS_NAN(t)) return t;
542 return MONTH_FROM_TIME(t);
543}
544
545
546function GetDateFrom(aDate) {
547 var t = DATE_VALUE(aDate);
548 if (NUMBER_IS_NAN(t)) return t;
549 return DATE_FROM_TIME(LocalTimeNoCheck(t));
550}
551
552
553function GetUTCDateFrom(aDate) {
554 var t = DATE_VALUE(aDate);
555 if (NUMBER_IS_NAN(t)) return t;
556 return DATE_FROM_TIME(t);
557}
558
559
560%FunctionSetPrototype($Date, new $Date($NaN));
561
562
563var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
564var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
565
566
567function TwoDigitString(value) {
568 return value < 10 ? "0" + value : "" + value;
569}
570
571
572function DateString(time) {
Andrei Popescu31002712010-02-23 13:46:05 +0000573 return WeekDays[WeekDay(time)] + ' '
Steve Block6ded16b2010-05-10 14:33:55 +0100574 + Months[MonthFromTime(time)] + ' '
575 + TwoDigitString(DateFromTime(time)) + ' '
576 + YearFromTime(time);
Andrei Popescu31002712010-02-23 13:46:05 +0000577}
578
579
580var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
581var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
582
583
584function LongDateString(time) {
Andrei Popescu31002712010-02-23 13:46:05 +0000585 return LongWeekDays[WeekDay(time)] + ', '
Steve Block6ded16b2010-05-10 14:33:55 +0100586 + LongMonths[MonthFromTime(time)] + ' '
587 + TwoDigitString(DateFromTime(time)) + ', '
588 + YearFromTime(time);
Andrei Popescu31002712010-02-23 13:46:05 +0000589}
590
591
592function TimeString(time) {
593 return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
594 + TwoDigitString(MIN_FROM_TIME(time)) + ':'
595 + TwoDigitString(SEC_FROM_TIME(time));
596}
597
598
599function LocalTimezoneString(time) {
Ben Murdochd69d2e32010-03-30 12:55:27 +0100600 var old_timezone = timezone_cache_timezone;
601 var timezone = LocalTimezone(time);
602 if (old_timezone && timezone != old_timezone) {
603 // If the timezone string has changed from the one that we cached,
604 // the local time offset may now be wrong. So we need to update it
605 // and try again.
606 local_time_offset = %DateLocalTimeOffset();
607 // We also need to invalidate the DST cache as the new timezone may have
608 // different DST times.
609 var dst_cache = DST_offset_cache;
610 dst_cache.start = 0;
611 dst_cache.end = -1;
612 }
613
Andrei Popescu31002712010-02-23 13:46:05 +0000614 var timezoneOffset =
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100615 (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute;
Andrei Popescu31002712010-02-23 13:46:05 +0000616 var sign = (timezoneOffset >= 0) ? 1 : -1;
617 var hours = FLOOR((sign * timezoneOffset)/60);
618 var min = FLOOR((sign * timezoneOffset)%60);
619 var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
620 TwoDigitString(hours) + TwoDigitString(min);
Ben Murdochd69d2e32010-03-30 12:55:27 +0100621 return gmt + ' (' + timezone + ')';
Andrei Popescu31002712010-02-23 13:46:05 +0000622}
623
624
625function DatePrintString(time) {
626 return DateString(time) + ' ' + TimeString(time);
627}
628
629// -------------------------------------------------------------------
630
631// Reused output buffer. Used when parsing date strings.
Steve Block6ded16b2010-05-10 14:33:55 +0100632var parse_buffer = $Array(8);
Andrei Popescu31002712010-02-23 13:46:05 +0000633
634// ECMA 262 - 15.9.4.2
635function DateParse(string) {
636 var arr = %DateParseString(ToString(string), parse_buffer);
637 if (IS_NULL(arr)) return $NaN;
638
639 var day = MakeDay(arr[0], arr[1], arr[2]);
Steve Block6ded16b2010-05-10 14:33:55 +0100640 var time = MakeTime(arr[3], arr[4], arr[5], arr[6]);
Andrei Popescu31002712010-02-23 13:46:05 +0000641 var date = MakeDate(day, time);
642
Steve Block6ded16b2010-05-10 14:33:55 +0100643 if (IS_NULL(arr[7])) {
Andrei Popescu31002712010-02-23 13:46:05 +0000644 return TimeClip(UTC(date));
645 } else {
Steve Block6ded16b2010-05-10 14:33:55 +0100646 return TimeClip(date - arr[7] * 1000);
Andrei Popescu31002712010-02-23 13:46:05 +0000647 }
648}
649
650
651// ECMA 262 - 15.9.4.3
652function DateUTC(year, month, date, hours, minutes, seconds, ms) {
653 year = ToNumber(year);
654 month = ToNumber(month);
655 var argc = %_ArgumentsLength();
656 date = argc > 2 ? ToNumber(date) : 1;
657 hours = argc > 3 ? ToNumber(hours) : 0;
658 minutes = argc > 4 ? ToNumber(minutes) : 0;
659 seconds = argc > 5 ? ToNumber(seconds) : 0;
660 ms = argc > 6 ? ToNumber(ms) : 0;
661 year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
662 ? 1900 + TO_INTEGER(year) : year;
663 var day = MakeDay(year, month, date);
664 var time = MakeTime(hours, minutes, seconds, ms);
665 return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
666}
667
668
669// Mozilla-specific extension. Returns the number of milliseconds
670// elapsed since 1 January 1970 00:00:00 UTC.
671function DateNow() {
672 return %DateCurrentTime();
673}
674
675
676// ECMA 262 - 15.9.5.2
677function DateToString() {
678 var t = DATE_VALUE(this);
679 if (NUMBER_IS_NAN(t)) return kInvalidDate;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100680 var time_zone_string = LocalTimezoneString(t); // May update local offset.
681 return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000682}
683
684
685// ECMA 262 - 15.9.5.3
686function DateToDateString() {
687 var t = DATE_VALUE(this);
688 if (NUMBER_IS_NAN(t)) return kInvalidDate;
689 return DateString(LocalTimeNoCheck(t));
690}
691
692
693// ECMA 262 - 15.9.5.4
694function DateToTimeString() {
695 var t = DATE_VALUE(this);
696 if (NUMBER_IS_NAN(t)) return kInvalidDate;
Andrei Popescu6599b9d2010-04-28 13:01:47 +0100697 var time_zone_string = LocalTimezoneString(t); // May update local offset.
698 return TimeString(LocalTimeNoCheck(t)) + time_zone_string;
Andrei Popescu31002712010-02-23 13:46:05 +0000699}
700
701
702// ECMA 262 - 15.9.5.5
703function DateToLocaleString() {
704 return DateToString.call(this);
705}
706
707
708// ECMA 262 - 15.9.5.6
709function DateToLocaleDateString() {
710 var t = DATE_VALUE(this);
711 if (NUMBER_IS_NAN(t)) return kInvalidDate;
712 return LongDateString(LocalTimeNoCheck(t));
713}
714
715
716// ECMA 262 - 15.9.5.7
717function DateToLocaleTimeString() {
718 var t = DATE_VALUE(this);
719 if (NUMBER_IS_NAN(t)) return kInvalidDate;
720 var lt = LocalTimeNoCheck(t);
721 return TimeString(lt);
722}
723
724
725// ECMA 262 - 15.9.5.8
726function DateValueOf() {
727 return DATE_VALUE(this);
728}
729
730
731// ECMA 262 - 15.9.5.9
Andrei Popescu402d9372010-02-26 13:31:12 +0000732function DateGetTime() {
Andrei Popescu31002712010-02-23 13:46:05 +0000733 return DATE_VALUE(this);
734}
735
736
737// ECMA 262 - 15.9.5.10
738function DateGetFullYear() {
739 return GetFullYearFrom(this)
740}
741
742
743// ECMA 262 - 15.9.5.11
744function DateGetUTCFullYear() {
745 return GetUTCFullYearFrom(this)
746}
747
748
749// ECMA 262 - 15.9.5.12
750function DateGetMonth() {
751 return GetMonthFrom(this);
752}
753
754
755// ECMA 262 - 15.9.5.13
756function DateGetUTCMonth() {
757 return GetUTCMonthFrom(this);
758}
759
760
761// ECMA 262 - 15.9.5.14
762function DateGetDate() {
763 return GetDateFrom(this);
764}
765
766
767// ECMA 262 - 15.9.5.15
768function DateGetUTCDate() {
769 return GetUTCDateFrom(this);
770}
771
772
773// ECMA 262 - 15.9.5.16
774function DateGetDay() {
775 var t = %_ValueOf(this);
776 if (NUMBER_IS_NAN(t)) return t;
777 return WeekDay(LocalTimeNoCheck(t));
778}
779
780
781// ECMA 262 - 15.9.5.17
782function DateGetUTCDay() {
783 var t = %_ValueOf(this);
784 if (NUMBER_IS_NAN(t)) return t;
785 return WeekDay(t);
786}
787
788
789// ECMA 262 - 15.9.5.18
790function DateGetHours() {
791 return GetHoursFrom(this);
792}
793
794
795// ECMA 262 - 15.9.5.19
796function DateGetUTCHours() {
797 return GetUTCHoursFrom(this);
798}
799
800
801// ECMA 262 - 15.9.5.20
802function DateGetMinutes() {
803 return GetMinutesFrom(this);
804}
805
806
807// ECMA 262 - 15.9.5.21
808function DateGetUTCMinutes() {
809 return GetUTCMinutesFrom(this);
810}
811
812
813// ECMA 262 - 15.9.5.22
814function DateGetSeconds() {
815 return GetSecondsFrom(this);
816}
817
818
819// ECMA 262 - 15.9.5.23
820function DateGetUTCSeconds() {
821 return GetUTCSecondsFrom(this);
822}
823
824
825// ECMA 262 - 15.9.5.24
826function DateGetMilliseconds() {
827 return GetMillisecondsFrom(this);
828}
829
830
831// ECMA 262 - 15.9.5.25
832function DateGetUTCMilliseconds() {
833 return GetUTCMillisecondsFrom(this);
834}
835
836
837// ECMA 262 - 15.9.5.26
838function DateGetTimezoneOffset() {
839 var t = DATE_VALUE(this);
840 if (NUMBER_IS_NAN(t)) return t;
841 return (t - LocalTimeNoCheck(t)) / msPerMinute;
842}
843
844
845// ECMA 262 - 15.9.5.27
846function DateSetTime(ms) {
847 if (!IS_DATE(this)) ThrowDateTypeError();
848 return %_SetValueOf(this, TimeClip(ToNumber(ms)));
849}
850
851
852// ECMA 262 - 15.9.5.28
853function DateSetMilliseconds(ms) {
854 var t = LocalTime(DATE_VALUE(this));
855 ms = ToNumber(ms);
856 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
857 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
858}
859
860
861// ECMA 262 - 15.9.5.29
862function DateSetUTCMilliseconds(ms) {
863 var t = DATE_VALUE(this);
864 ms = ToNumber(ms);
865 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
866 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
867}
868
869
870// ECMA 262 - 15.9.5.30
871function DateSetSeconds(sec, ms) {
872 var t = LocalTime(DATE_VALUE(this));
873 sec = ToNumber(sec);
874 ms = %_ArgumentsLength() < 2 ? GetMillisecondsFrom(this) : ToNumber(ms);
875 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
876 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
877}
878
879
880// ECMA 262 - 15.9.5.31
881function DateSetUTCSeconds(sec, ms) {
882 var t = DATE_VALUE(this);
883 sec = ToNumber(sec);
884 ms = %_ArgumentsLength() < 2 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
885 var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
886 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
887}
888
889
890// ECMA 262 - 15.9.5.33
891function DateSetMinutes(min, sec, ms) {
892 var t = LocalTime(DATE_VALUE(this));
893 min = ToNumber(min);
894 var argc = %_ArgumentsLength();
895 sec = argc < 2 ? GetSecondsFrom(this) : ToNumber(sec);
896 ms = argc < 3 ? GetMillisecondsFrom(this) : ToNumber(ms);
897 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
898 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
899}
900
901
902// ECMA 262 - 15.9.5.34
903function DateSetUTCMinutes(min, sec, ms) {
904 var t = DATE_VALUE(this);
905 min = ToNumber(min);
906 var argc = %_ArgumentsLength();
907 sec = argc < 2 ? GetUTCSecondsFrom(this) : ToNumber(sec);
908 ms = argc < 3 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
909 var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
910 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
911}
912
913
914// ECMA 262 - 15.9.5.35
915function DateSetHours(hour, min, sec, ms) {
916 var t = LocalTime(DATE_VALUE(this));
917 hour = ToNumber(hour);
918 var argc = %_ArgumentsLength();
919 min = argc < 2 ? GetMinutesFrom(this) : ToNumber(min);
920 sec = argc < 3 ? GetSecondsFrom(this) : ToNumber(sec);
921 ms = argc < 4 ? GetMillisecondsFrom(this) : ToNumber(ms);
922 var time = MakeTime(hour, min, sec, ms);
923 return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
924}
925
926
927// ECMA 262 - 15.9.5.34
928function DateSetUTCHours(hour, min, sec, ms) {
929 var t = DATE_VALUE(this);
930 hour = ToNumber(hour);
931 var argc = %_ArgumentsLength();
932 min = argc < 2 ? GetUTCMinutesFrom(this) : ToNumber(min);
933 sec = argc < 3 ? GetUTCSecondsFrom(this) : ToNumber(sec);
934 ms = argc < 4 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
935 var time = MakeTime(hour, min, sec, ms);
936 return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
937}
938
939
940// ECMA 262 - 15.9.5.36
941function DateSetDate(date) {
942 var t = LocalTime(DATE_VALUE(this));
943 date = ToNumber(date);
944 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
945 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
946}
947
948
949// ECMA 262 - 15.9.5.37
950function DateSetUTCDate(date) {
951 var t = DATE_VALUE(this);
952 date = ToNumber(date);
953 var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
954 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
955}
956
957
958// ECMA 262 - 15.9.5.38
959function DateSetMonth(month, date) {
960 var t = LocalTime(DATE_VALUE(this));
961 month = ToNumber(month);
962 date = %_ArgumentsLength() < 2 ? GetDateFrom(this) : ToNumber(date);
963 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
964 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
965}
966
967
968// ECMA 262 - 15.9.5.39
969function DateSetUTCMonth(month, date) {
970 var t = DATE_VALUE(this);
971 month = ToNumber(month);
972 date = %_ArgumentsLength() < 2 ? GetUTCDateFrom(this) : ToNumber(date);
973 var day = MakeDay(YEAR_FROM_TIME(t), month, date);
974 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
975}
976
977
978// ECMA 262 - 15.9.5.40
979function DateSetFullYear(year, month, date) {
980 var t = DATE_VALUE(this);
981 t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
982 year = ToNumber(year);
983 var argc = %_ArgumentsLength();
984 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
985 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
986 var day = MakeDay(year, month, date);
987 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
988}
989
990
991// ECMA 262 - 15.9.5.41
992function DateSetUTCFullYear(year, month, date) {
993 var t = DATE_VALUE(this);
994 if (NUMBER_IS_NAN(t)) t = 0;
995 var argc = %_ArgumentsLength();
996 year = ToNumber(year);
997 month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
998 date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
999 var day = MakeDay(year, month, date);
1000 return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
1001}
1002
1003
1004// ECMA 262 - 15.9.5.42
1005function DateToUTCString() {
1006 var t = DATE_VALUE(this);
1007 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1008 // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
1009 return WeekDays[WeekDay(t)] + ', '
1010 + TwoDigitString(DATE_FROM_TIME(t)) + ' '
1011 + Months[MONTH_FROM_TIME(t)] + ' '
1012 + YEAR_FROM_TIME(t) + ' '
1013 + TimeString(t) + ' GMT';
1014}
1015
1016
1017// ECMA 262 - B.2.4
1018function DateGetYear() {
1019 var t = DATE_VALUE(this);
1020 if (NUMBER_IS_NAN(t)) return $NaN;
1021 return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900;
1022}
1023
1024
1025// ECMA 262 - B.2.5
1026function DateSetYear(year) {
1027 var t = LocalTime(DATE_VALUE(this));
1028 if (NUMBER_IS_NAN(t)) t = 0;
1029 year = ToNumber(year);
1030 if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
1031 year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
1032 ? 1900 + TO_INTEGER(year) : year;
1033 var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
1034 return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
1035}
1036
1037
1038// ECMA 262 - B.2.6
1039//
1040// Notice that this does not follow ECMA 262 completely. ECMA 262
1041// says that toGMTString should be the same Function object as
1042// toUTCString. JSC does not do this, so for compatibility we do not
1043// do that either. Instead, we create a new function whose name
1044// property will return toGMTString.
1045function DateToGMTString() {
1046 return DateToUTCString.call(this);
1047}
1048
1049
1050function PadInt(n, digits) {
1051 if (digits == 1) return n;
1052 return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
1053}
1054
1055
1056function DateToISOString() {
1057 var t = DATE_VALUE(this);
1058 if (NUMBER_IS_NAN(t)) return kInvalidDate;
1059 return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) +
1060 '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) +
1061 ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) +
1062 '.' + PadInt(this.getUTCMilliseconds(), 3) +
1063 'Z';
1064}
1065
1066
1067function DateToJSON(key) {
1068 return CheckJSONPrimitive(this.toISOString());
1069}
1070
1071
1072// -------------------------------------------------------------------
1073
1074function SetupDate() {
1075 // Setup non-enumerable properties of the Date object itself.
1076 InstallFunctions($Date, DONT_ENUM, $Array(
1077 "UTC", DateUTC,
1078 "parse", DateParse,
1079 "now", DateNow
1080 ));
1081
1082 // Setup non-enumerable constructor property of the Date prototype object.
1083 %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
1084
1085 // Setup non-enumerable functions of the Date prototype object and
1086 // set their names.
1087 InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array(
1088 "toString", DateToString,
1089 "toDateString", DateToDateString,
1090 "toTimeString", DateToTimeString,
1091 "toLocaleString", DateToLocaleString,
1092 "toLocaleDateString", DateToLocaleDateString,
1093 "toLocaleTimeString", DateToLocaleTimeString,
1094 "valueOf", DateValueOf,
1095 "getTime", DateGetTime,
1096 "getFullYear", DateGetFullYear,
1097 "getUTCFullYear", DateGetUTCFullYear,
1098 "getMonth", DateGetMonth,
1099 "getUTCMonth", DateGetUTCMonth,
1100 "getDate", DateGetDate,
1101 "getUTCDate", DateGetUTCDate,
1102 "getDay", DateGetDay,
1103 "getUTCDay", DateGetUTCDay,
1104 "getHours", DateGetHours,
1105 "getUTCHours", DateGetUTCHours,
1106 "getMinutes", DateGetMinutes,
1107 "getUTCMinutes", DateGetUTCMinutes,
1108 "getSeconds", DateGetSeconds,
1109 "getUTCSeconds", DateGetUTCSeconds,
1110 "getMilliseconds", DateGetMilliseconds,
1111 "getUTCMilliseconds", DateGetUTCMilliseconds,
1112 "getTimezoneOffset", DateGetTimezoneOffset,
1113 "setTime", DateSetTime,
1114 "setMilliseconds", DateSetMilliseconds,
1115 "setUTCMilliseconds", DateSetUTCMilliseconds,
1116 "setSeconds", DateSetSeconds,
1117 "setUTCSeconds", DateSetUTCSeconds,
1118 "setMinutes", DateSetMinutes,
1119 "setUTCMinutes", DateSetUTCMinutes,
1120 "setHours", DateSetHours,
1121 "setUTCHours", DateSetUTCHours,
1122 "setDate", DateSetDate,
1123 "setUTCDate", DateSetUTCDate,
1124 "setMonth", DateSetMonth,
1125 "setUTCMonth", DateSetUTCMonth,
1126 "setFullYear", DateSetFullYear,
1127 "setUTCFullYear", DateSetUTCFullYear,
1128 "toGMTString", DateToGMTString,
1129 "toUTCString", DateToUTCString,
1130 "getYear", DateGetYear,
1131 "setYear", DateSetYear,
1132 "toISOString", DateToISOString,
1133 "toJSON", DateToJSON
1134 ));
1135}
1136
1137SetupDate();