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