Home | History | Annotate | Download | only in src
      1 // 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.
     41 const $Date = global.Date;
     42 
     43 // Helper function to throw error.
     44 function ThrowDateTypeError() {
     45   throw new $TypeError('this is not a Date object.');
     46 }
     47 
     48 // ECMA 262 - 5.2
     49 function 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 
     57 function TimeWithinDay(time) {
     58   return Modulo(time, msPerDay);
     59 }
     60 
     61 
     62 // ECMA 262 - 15.9.1.3
     63 function 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 
     70 function 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 
     78 function TimeFromYear(year) {
     79   return msPerDay * DayFromYear(year);
     80 }
     81 
     82 
     83 function InLeapYear(time) {
     84   return DaysInYear(YearFromTime(time)) - 365;  // Returns 1 or 0.
     85 }
     86 
     87 
     88 // ECMA 262 - 15.9.1.9
     89 function EquivalentYear(year) {
     90   // Returns an equivalent year in the range [2008-2035] matching
     91   // - leap year.
     92   // - week day of first day.
     93   var time = TimeFromYear(year);
     94   var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) +
     95       (WeekDay(time) * 12) % 28;
     96   // Find the year in the range 2008..2037 that is equivalent mod 28.
     97   // Add 3*28 to give a positive argument to the modulus operator.
     98   return 2008 + (recent_year + 3*28 - 2008) % 28;
     99 }
    100 
    101 
    102 function EquivalentTime(t) {
    103   // The issue here is that some library calls don't work right for dates
    104   // that cannot be represented using a non-negative signed 32 bit integer
    105   // (measured in whole seconds based on the 1970 epoch).
    106   // We solve this by mapping the time to a year with same leap-year-ness
    107   // and same starting day for the year.  The ECMAscript specification says
    108   // we must do this, but for compatibility with other browsers, we use
    109   // the actual year if it is in the range 1970..2037
    110   if (t >= 0 && t <= 2.1e12) return t;
    111 
    112   var day = MakeDay(EquivalentYear(YearFromTime(t)),
    113                     MonthFromTime(t),
    114                     DateFromTime(t));
    115   return MakeDate(day, TimeWithinDay(t));
    116 }
    117 
    118 
    119 // local_time_offset is initialized when the DST_offset_cache is missed.
    120 // It must not be used until after a call to DaylightSavingsOffset().
    121 // In this way, only one check, for a DST cache miss, is needed.
    122 var local_time_offset;
    123 
    124 
    125 // Because computing the DST offset is an expensive operation,
    126 // we keep a cache of the last computed DST offset along with a time interval
    127 // where we know the cache is valid.
    128 // When the cache is valid, local_time_offset is also valid.
    129 var DST_offset_cache = {
    130   // Cached DST offset.
    131   offset: 0,
    132   // Time interval where the cached offset is valid.
    133   start: 0, end: -1,
    134   // Size of next interval expansion.
    135   increment: 0,
    136   initial_increment: 19 * msPerDay
    137 };
    138 
    139 
    140 // NOTE: The implementation relies on the fact that no time zones have
    141 // more than one daylight savings offset change per 19 days.
    142 //
    143 // In Egypt in 2010 they decided to suspend DST during Ramadan. This
    144 // led to a short interval where DST is in effect from September 10 to
    145 // September 30.
    146 //
    147 // If this function is called with NaN it returns NaN.
    148 function DaylightSavingsOffset(t) {
    149   // Load the cache object from the builtins object.
    150   var cache = DST_offset_cache;
    151 
    152   // Cache the start and the end in local variables for fast access.
    153   var start = cache.start;
    154   var end = cache.end;
    155 
    156   if (start <= t) {
    157     // If the time fits in the cached interval, return the cached offset.
    158     if (t <= end) return cache.offset;
    159 
    160     // If the cache misses, the local_time_offset may not be initialized.
    161     if (IS_UNDEFINED(local_time_offset)) {
    162       local_time_offset = %DateLocalTimeOffset();
    163     }
    164 
    165     // Compute a possible new interval end.
    166     var new_end = end + cache.increment;
    167 
    168     if (t <= new_end) {
    169       var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end));
    170       if (cache.offset == end_offset) {
    171         // If the offset at the end of the new interval still matches
    172         // the offset in the cache, we grow the cached time interval
    173         // and return the offset.
    174         cache.end = new_end;
    175         cache.increment = cache.initial_increment;
    176         return end_offset;
    177       } else {
    178         var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
    179         if (offset == end_offset) {
    180           // The offset at the given time is equal to the offset at the
    181           // new end of the interval, so that means that we've just skipped
    182           // the point in time where the DST offset change occurred. Updated
    183           // the interval to reflect this and reset the increment.
    184           cache.start = t;
    185           cache.end = new_end;
    186           cache.increment = cache.initial_increment;
    187         } else {
    188           // The interval contains a DST offset change and the given time is
    189           // before it. Adjust the increment to avoid a linear search for
    190           // the offset change point and change the end of the interval.
    191           cache.increment /= 3;
    192           cache.end = t;
    193         }
    194         // Update the offset in the cache and return it.
    195         cache.offset = offset;
    196         return offset;
    197       }
    198     }
    199   }
    200 
    201   // If the cache misses, the local_time_offset may not be initialized.
    202   if (IS_UNDEFINED(local_time_offset)) {
    203     local_time_offset = %DateLocalTimeOffset();
    204   }
    205   // Compute the DST offset for the time and shrink the cache interval
    206   // to only contain the time. This allows fast repeated DST offset
    207   // computations for the same time.
    208   var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
    209   cache.offset = offset;
    210   cache.start = cache.end = t;
    211   cache.increment = cache.initial_increment;
    212   return offset;
    213 }
    214 
    215 
    216 var timezone_cache_time = $NaN;
    217 var timezone_cache_timezone;
    218 
    219 function LocalTimezone(t) {
    220   if (NUMBER_IS_NAN(t)) return "";
    221   if (t == timezone_cache_time) {
    222     return timezone_cache_timezone;
    223   }
    224   var timezone = %DateLocalTimezone(EquivalentTime(t));
    225   timezone_cache_time = t;
    226   timezone_cache_timezone = timezone;
    227   return timezone;
    228 }
    229 
    230 
    231 function WeekDay(time) {
    232   return Modulo(DAY(time) + 4, 7);
    233 }
    234 
    235 
    236 function LocalTime(time) {
    237   if (NUMBER_IS_NAN(time)) return time;
    238   // DaylightSavingsOffset called before local_time_offset used.
    239   return time + DaylightSavingsOffset(time) + local_time_offset;
    240 }
    241 
    242 
    243 var ltcache = {
    244   key: null,
    245   val: null
    246 };
    247 
    248 function LocalTimeNoCheck(time) {
    249   var ltc = ltcache;
    250   if (%_ObjectEquals(time, ltc.key)) return ltc.val;
    251 
    252   // Inline the DST offset cache checks for speed.
    253   // The cache is hit, or DaylightSavingsOffset is called,
    254   // before local_time_offset is used.
    255   var cache = DST_offset_cache;
    256   if (cache.start <= time && time <= cache.end) {
    257     var dst_offset = cache.offset;
    258   } else {
    259     var dst_offset = DaylightSavingsOffset(time);
    260   }
    261   ltc.key = time;
    262   return (ltc.val = time + local_time_offset + dst_offset);
    263 }
    264 
    265 
    266 function UTC(time) {
    267   if (NUMBER_IS_NAN(time)) return time;
    268   // local_time_offset is needed before the call to DaylightSavingsOffset,
    269   // so it may be uninitialized.
    270   if (IS_UNDEFINED(local_time_offset)) {
    271     local_time_offset = %DateLocalTimeOffset();
    272   }
    273   var tmp = time - local_time_offset;
    274   return tmp - DaylightSavingsOffset(tmp);
    275 }
    276 
    277 
    278 // ECMA 262 - 15.9.1.11
    279 function MakeTime(hour, min, sec, ms) {
    280   if (!$isFinite(hour)) return $NaN;
    281   if (!$isFinite(min)) return $NaN;
    282   if (!$isFinite(sec)) return $NaN;
    283   if (!$isFinite(ms)) return $NaN;
    284   return TO_INTEGER(hour) * msPerHour
    285       + TO_INTEGER(min) * msPerMinute
    286       + TO_INTEGER(sec) * msPerSecond
    287       + TO_INTEGER(ms);
    288 }
    289 
    290 
    291 // ECMA 262 - 15.9.1.12
    292 function TimeInYear(year) {
    293   return DaysInYear(year) * msPerDay;
    294 }
    295 
    296 
    297 var ymd_from_time_cache = [$NaN, $NaN, $NaN];
    298 var ymd_from_time_cached_time = $NaN;
    299 
    300 function YearFromTime(t) {
    301   if (t !== ymd_from_time_cached_time) {
    302     if (!$isFinite(t)) {
    303       return $NaN;
    304     }
    305 
    306     %DateYMDFromTime(t, ymd_from_time_cache);
    307     ymd_from_time_cached_time = t
    308   }
    309 
    310   return ymd_from_time_cache[0];
    311 }
    312 
    313 function MonthFromTime(t) {
    314   if (t !== ymd_from_time_cached_time) {
    315     if (!$isFinite(t)) {
    316       return $NaN;
    317     }
    318     %DateYMDFromTime(t, ymd_from_time_cache);
    319     ymd_from_time_cached_time = t
    320   }
    321 
    322   return ymd_from_time_cache[1];
    323 }
    324 
    325 function DateFromTime(t) {
    326   if (t !== ymd_from_time_cached_time) {
    327     if (!$isFinite(t)) {
    328       return $NaN;
    329     }
    330 
    331     %DateYMDFromTime(t, ymd_from_time_cache);
    332     ymd_from_time_cached_time = t
    333   }
    334 
    335   return ymd_from_time_cache[2];
    336 }
    337 
    338 
    339 // Compute number of days given a year, month, date.
    340 // Note that month and date can lie outside the normal range.
    341 //   For example:
    342 //     MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
    343 //     MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
    344 //     MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
    345 function MakeDay(year, month, date) {
    346   if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
    347 
    348   // Convert to integer and map -0 to 0.
    349   year = TO_INTEGER_MAP_MINUS_ZERO(year);
    350   month = TO_INTEGER_MAP_MINUS_ZERO(month);
    351   date = TO_INTEGER_MAP_MINUS_ZERO(date);
    352 
    353   if (year < kMinYear || year > kMaxYear ||
    354       month < kMinMonth || month > kMaxMonth ||
    355       date < kMinDate || date > kMaxDate) {
    356     return $NaN;
    357   }
    358 
    359   // Now we rely on year, month and date being SMIs.
    360   return %DateMakeDay(year, month, date);
    361 }
    362 
    363 
    364 // ECMA 262 - 15.9.1.13
    365 function MakeDate(day, time) {
    366   var time = day * msPerDay + time;
    367   // Some of our runtime funtions for computing UTC(time) rely on
    368   // times not being significantly larger than MAX_TIME_MS. If there
    369   // is no way that the time can be within range even after UTC
    370   // conversion we return NaN immediately instead of relying on
    371   // TimeClip to do it.
    372   if ($abs(time) > MAX_TIME_BEFORE_UTC) return $NaN;
    373   return time;
    374 }
    375 
    376 
    377 // ECMA 262 - 15.9.1.14
    378 function TimeClip(time) {
    379   if (!$isFinite(time)) return $NaN;
    380   if ($abs(time) > MAX_TIME_MS) return $NaN;
    381   return TO_INTEGER(time);
    382 }
    383 
    384 
    385 // The Date cache is used to limit the cost of parsing the same Date
    386 // strings over and over again.
    387 var Date_cache = {
    388   // Cached time value.
    389   time: $NaN,
    390   // Cached year when interpreting the time as a local time. Only
    391   // valid when the time matches cached time.
    392   year: $NaN,
    393   // String input for which the cached time is valid.
    394   string: null
    395 };
    396 
    397 
    398 %SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) {
    399   if (!%_IsConstructCall()) {
    400     // ECMA 262 - 15.9.2
    401     return (new $Date()).toString();
    402   }
    403 
    404   // ECMA 262 - 15.9.3
    405   var argc = %_ArgumentsLength();
    406   var value;
    407   if (argc == 0) {
    408     value = %DateCurrentTime();
    409 
    410   } else if (argc == 1) {
    411     if (IS_NUMBER(year)) {
    412       value = TimeClip(year);
    413 
    414     } else if (IS_STRING(year)) {
    415       // Probe the Date cache. If we already have a time value for the
    416       // given time, we re-use that instead of parsing the string again.
    417       var cache = Date_cache;
    418       if (cache.string === year) {
    419         value = cache.time;
    420       } else {
    421         value = DateParse(year);
    422         if (!NUMBER_IS_NAN(value)) {
    423           cache.time = value;
    424           cache.year = YearFromTime(LocalTimeNoCheck(value));
    425           cache.string = year;
    426         }
    427       }
    428 
    429     } else {
    430       // According to ECMA 262, no hint should be given for this
    431       // conversion. However, ToPrimitive defaults to STRING_HINT for
    432       // Date objects which will lose precision when the Date
    433       // constructor is called with another Date object as its
    434       // argument. We therefore use NUMBER_HINT for the conversion,
    435       // which is the default for everything else than Date objects.
    436       // This makes us behave like KJS and SpiderMonkey.
    437       var time = ToPrimitive(year, NUMBER_HINT);
    438       value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
    439     }
    440 
    441   } else {
    442     year = ToNumber(year);
    443     month = ToNumber(month);
    444     date = argc > 2 ? ToNumber(date) : 1;
    445     hours = argc > 3 ? ToNumber(hours) : 0;
    446     minutes = argc > 4 ? ToNumber(minutes) : 0;
    447     seconds = argc > 5 ? ToNumber(seconds) : 0;
    448     ms = argc > 6 ? ToNumber(ms) : 0;
    449     year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
    450         ? 1900 + TO_INTEGER(year) : year;
    451     var day = MakeDay(year, month, date);
    452     var time = MakeTime(hours, minutes, seconds, ms);
    453     value = TimeClip(UTC(MakeDate(day, time)));
    454   }
    455   %_SetValueOf(this, value);
    456 });
    457 
    458 
    459 %FunctionSetPrototype($Date, new $Date($NaN));
    460 
    461 
    462 var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    463 var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    464 
    465 
    466 function TwoDigitString(value) {
    467   return value < 10 ? "0" + value : "" + value;
    468 }
    469 
    470 
    471 function DateString(time) {
    472   return WeekDays[WeekDay(time)] + ' '
    473       + Months[MonthFromTime(time)] + ' '
    474       + TwoDigitString(DateFromTime(time)) + ' '
    475       + YearFromTime(time);
    476 }
    477 
    478 
    479 var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
    480 var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
    481 
    482 
    483 function LongDateString(time) {
    484   return LongWeekDays[WeekDay(time)] + ', '
    485       + LongMonths[MonthFromTime(time)] + ' '
    486       + TwoDigitString(DateFromTime(time)) + ', '
    487       + YearFromTime(time);
    488 }
    489 
    490 
    491 function TimeString(time) {
    492   return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
    493       + TwoDigitString(MIN_FROM_TIME(time)) + ':'
    494       + TwoDigitString(SEC_FROM_TIME(time));
    495 }
    496 
    497 
    498 function LocalTimezoneString(time) {
    499   var old_timezone = timezone_cache_timezone;
    500   var timezone = LocalTimezone(time);
    501   if (old_timezone && timezone != old_timezone) {
    502     // If the timezone string has changed from the one that we cached,
    503     // the local time offset may now be wrong. So we need to update it
    504     // and try again.
    505     local_time_offset = %DateLocalTimeOffset();
    506     // We also need to invalidate the DST cache as the new timezone may have
    507     // different DST times.
    508     var dst_cache = DST_offset_cache;
    509     dst_cache.start = 0;
    510     dst_cache.end = -1;
    511   }
    512 
    513   var timezoneOffset =
    514       (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute;
    515   var sign = (timezoneOffset >= 0) ? 1 : -1;
    516   var hours = FLOOR((sign * timezoneOffset)/60);
    517   var min   = FLOOR((sign * timezoneOffset)%60);
    518   var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
    519       TwoDigitString(hours) + TwoDigitString(min);
    520   return gmt + ' (' +  timezone + ')';
    521 }
    522 
    523 
    524 function DatePrintString(time) {
    525   return DateString(time) + ' ' + TimeString(time);
    526 }
    527 
    528 // -------------------------------------------------------------------
    529 
    530 // Reused output buffer. Used when parsing date strings.
    531 var parse_buffer = $Array(8);
    532 
    533 // ECMA 262 - 15.9.4.2
    534 function DateParse(string) {
    535   var arr = %DateParseString(ToString(string), parse_buffer);
    536   if (IS_NULL(arr)) return $NaN;
    537 
    538   var day = MakeDay(arr[0], arr[1], arr[2]);
    539   var time = MakeTime(arr[3], arr[4], arr[5], arr[6]);
    540   var date = MakeDate(day, time);
    541 
    542   if (IS_NULL(arr[7])) {
    543     return TimeClip(UTC(date));
    544   } else {
    545     return TimeClip(date - arr[7] * 1000);
    546   }
    547 }
    548 
    549 
    550 // ECMA 262 - 15.9.4.3
    551 function DateUTC(year, month, date, hours, minutes, seconds, ms) {
    552   year = ToNumber(year);
    553   month = ToNumber(month);
    554   var argc = %_ArgumentsLength();
    555   date = argc > 2 ? ToNumber(date) : 1;
    556   hours = argc > 3 ? ToNumber(hours) : 0;
    557   minutes = argc > 4 ? ToNumber(minutes) : 0;
    558   seconds = argc > 5 ? ToNumber(seconds) : 0;
    559   ms = argc > 6 ? ToNumber(ms) : 0;
    560   year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
    561       ? 1900 + TO_INTEGER(year) : year;
    562   var day = MakeDay(year, month, date);
    563   var time = MakeTime(hours, minutes, seconds, ms);
    564   return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
    565 }
    566 
    567 
    568 // Mozilla-specific extension. Returns the number of milliseconds
    569 // elapsed since 1 January 1970 00:00:00 UTC.
    570 function DateNow() {
    571   return %DateCurrentTime();
    572 }
    573 
    574 
    575 // ECMA 262 - 15.9.5.2
    576 function DateToString() {
    577   var t = DATE_VALUE(this);
    578   if (NUMBER_IS_NAN(t)) return kInvalidDate;
    579   var time_zone_string = LocalTimezoneString(t);  // May update local offset.
    580   return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string;
    581 }
    582 
    583 
    584 // ECMA 262 - 15.9.5.3
    585 function DateToDateString() {
    586   var t = DATE_VALUE(this);
    587   if (NUMBER_IS_NAN(t)) return kInvalidDate;
    588   return DateString(LocalTimeNoCheck(t));
    589 }
    590 
    591 
    592 // ECMA 262 - 15.9.5.4
    593 function DateToTimeString() {
    594   var t = DATE_VALUE(this);
    595   if (NUMBER_IS_NAN(t)) return kInvalidDate;
    596   var time_zone_string = LocalTimezoneString(t);  // May update local offset.
    597   return TimeString(LocalTimeNoCheck(t)) + time_zone_string;
    598 }
    599 
    600 
    601 // ECMA 262 - 15.9.5.5
    602 function DateToLocaleString() {
    603   return %_CallFunction(this, DateToString);
    604 }
    605 
    606 
    607 // ECMA 262 - 15.9.5.6
    608 function DateToLocaleDateString() {
    609   var t = DATE_VALUE(this);
    610   if (NUMBER_IS_NAN(t)) return kInvalidDate;
    611   return LongDateString(LocalTimeNoCheck(t));
    612 }
    613 
    614 
    615 // ECMA 262 - 15.9.5.7
    616 function DateToLocaleTimeString() {
    617   var t = DATE_VALUE(this);
    618   if (NUMBER_IS_NAN(t)) return kInvalidDate;
    619   var lt = LocalTimeNoCheck(t);
    620   return TimeString(lt);
    621 }
    622 
    623 
    624 // ECMA 262 - 15.9.5.8
    625 function DateValueOf() {
    626   return DATE_VALUE(this);
    627 }
    628 
    629 
    630 // ECMA 262 - 15.9.5.9
    631 function DateGetTime() {
    632   return DATE_VALUE(this);
    633 }
    634 
    635 
    636 // ECMA 262 - 15.9.5.10
    637 function DateGetFullYear() {
    638   var t = DATE_VALUE(this);
    639   if (NUMBER_IS_NAN(t)) return t;
    640   var cache = Date_cache;
    641   if (cache.time === t) return cache.year;
    642   return YearFromTime(LocalTimeNoCheck(t));
    643 }
    644 
    645 
    646 // ECMA 262 - 15.9.5.11
    647 function DateGetUTCFullYear() {
    648   var t = DATE_VALUE(this);
    649   if (NUMBER_IS_NAN(t)) return t;
    650   return YearFromTime(t);
    651 }
    652 
    653 
    654 // ECMA 262 - 15.9.5.12
    655 function DateGetMonth() {
    656   var t = DATE_VALUE(this);
    657   if (NUMBER_IS_NAN(t)) return t;
    658   return MonthFromTime(LocalTimeNoCheck(t));
    659 }
    660 
    661 
    662 // ECMA 262 - 15.9.5.13
    663 function DateGetUTCMonth() {
    664   var t = DATE_VALUE(this);
    665   if (NUMBER_IS_NAN(t)) return t;
    666   return MonthFromTime(t);
    667 }
    668 
    669 
    670 // ECMA 262 - 15.9.5.14
    671 function DateGetDate() {
    672   var t = DATE_VALUE(this);
    673   if (NUMBER_IS_NAN(t)) return t;
    674   return DateFromTime(LocalTimeNoCheck(t));
    675 }
    676 
    677 
    678 // ECMA 262 - 15.9.5.15
    679 function DateGetUTCDate() {
    680   var t = DATE_VALUE(this);
    681   return NAN_OR_DATE_FROM_TIME(t);
    682 }
    683 
    684 
    685 // ECMA 262 - 15.9.5.16
    686 function DateGetDay() {
    687   var t = %_ValueOf(this);
    688   if (NUMBER_IS_NAN(t)) return t;
    689   return WeekDay(LocalTimeNoCheck(t));
    690 }
    691 
    692 
    693 // ECMA 262 - 15.9.5.17
    694 function DateGetUTCDay() {
    695   var t = %_ValueOf(this);
    696   if (NUMBER_IS_NAN(t)) return t;
    697   return WeekDay(t);
    698 }
    699 
    700 
    701 // ECMA 262 - 15.9.5.18
    702 function DateGetHours() {
    703   var t = DATE_VALUE(this);
    704   if (NUMBER_IS_NAN(t)) return t;
    705   return HOUR_FROM_TIME(LocalTimeNoCheck(t));
    706 }
    707 
    708 
    709 // ECMA 262 - 15.9.5.19
    710 function DateGetUTCHours() {
    711   var t = DATE_VALUE(this);
    712   if (NUMBER_IS_NAN(t)) return t;
    713   return HOUR_FROM_TIME(t);
    714 }
    715 
    716 
    717 // ECMA 262 - 15.9.5.20
    718 function DateGetMinutes() {
    719   var t = DATE_VALUE(this);
    720   if (NUMBER_IS_NAN(t)) return t;
    721   return MIN_FROM_TIME(LocalTimeNoCheck(t));
    722 }
    723 
    724 
    725 // ECMA 262 - 15.9.5.21
    726 function DateGetUTCMinutes() {
    727   var t = DATE_VALUE(this);
    728   return NAN_OR_MIN_FROM_TIME(t);
    729 }
    730 
    731 
    732 // ECMA 262 - 15.9.5.22
    733 function DateGetSeconds() {
    734   var t = DATE_VALUE(this);
    735   if (NUMBER_IS_NAN(t)) return t;
    736   return SEC_FROM_TIME(LocalTimeNoCheck(t));
    737 }
    738 
    739 
    740 // ECMA 262 - 15.9.5.23
    741 function DateGetUTCSeconds() {
    742   var t = DATE_VALUE(this);
    743   return NAN_OR_SEC_FROM_TIME(t);
    744 }
    745 
    746 
    747 // ECMA 262 - 15.9.5.24
    748 function DateGetMilliseconds() {
    749   var t = DATE_VALUE(this);
    750   if (NUMBER_IS_NAN(t)) return t;
    751   return MS_FROM_TIME(LocalTimeNoCheck(t));
    752 }
    753 
    754 
    755 // ECMA 262 - 15.9.5.25
    756 function DateGetUTCMilliseconds() {
    757   var t = DATE_VALUE(this);
    758   return NAN_OR_MS_FROM_TIME(t);
    759 }
    760 
    761 
    762 // ECMA 262 - 15.9.5.26
    763 function DateGetTimezoneOffset() {
    764   var t = DATE_VALUE(this);
    765   if (NUMBER_IS_NAN(t)) return t;
    766   return (t - LocalTimeNoCheck(t)) / msPerMinute;
    767 }
    768 
    769 
    770 // ECMA 262 - 15.9.5.27
    771 function DateSetTime(ms) {
    772   if (!IS_DATE(this)) ThrowDateTypeError();
    773   return %_SetValueOf(this, TimeClip(ToNumber(ms)));
    774 }
    775 
    776 
    777 // ECMA 262 - 15.9.5.28
    778 function DateSetMilliseconds(ms) {
    779   var t = LocalTime(DATE_VALUE(this));
    780   ms = ToNumber(ms);
    781   var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
    782   return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
    783 }
    784 
    785 
    786 // ECMA 262 - 15.9.5.29
    787 function DateSetUTCMilliseconds(ms) {
    788   var t = DATE_VALUE(this);
    789   ms = ToNumber(ms);
    790   var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
    791   return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
    792 }
    793 
    794 
    795 // ECMA 262 - 15.9.5.30
    796 function DateSetSeconds(sec, ms) {
    797   var t = LocalTime(DATE_VALUE(this));
    798   sec = ToNumber(sec);
    799   ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
    800   var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
    801   return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
    802 }
    803 
    804 
    805 // ECMA 262 - 15.9.5.31
    806 function DateSetUTCSeconds(sec, ms) {
    807   var t = DATE_VALUE(this);
    808   sec = ToNumber(sec);
    809   ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
    810   var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
    811   return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
    812 }
    813 
    814 
    815 // ECMA 262 - 15.9.5.33
    816 function DateSetMinutes(min, sec, ms) {
    817   var t = LocalTime(DATE_VALUE(this));
    818   min = ToNumber(min);
    819   var argc = %_ArgumentsLength();
    820   sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
    821   ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
    822   var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
    823   return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
    824 }
    825 
    826 
    827 // ECMA 262 - 15.9.5.34
    828 function DateSetUTCMinutes(min, sec, ms) {
    829   var t = DATE_VALUE(this);
    830   min = ToNumber(min);
    831   var argc = %_ArgumentsLength();
    832   sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
    833   ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
    834   var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
    835   return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
    836 }
    837 
    838 
    839 // ECMA 262 - 15.9.5.35
    840 function DateSetHours(hour, min, sec, ms) {
    841   var t = LocalTime(DATE_VALUE(this));
    842   hour = ToNumber(hour);
    843   var argc = %_ArgumentsLength();
    844   min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
    845   sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
    846   ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
    847   var time = MakeTime(hour, min, sec, ms);
    848   return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
    849 }
    850 
    851 
    852 // ECMA 262 - 15.9.5.34
    853 function DateSetUTCHours(hour, min, sec, ms) {
    854   var t = DATE_VALUE(this);
    855   hour = ToNumber(hour);
    856   var argc = %_ArgumentsLength();
    857   min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
    858   sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
    859   ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
    860   var time = MakeTime(hour, min, sec, ms);
    861   return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
    862 }
    863 
    864 
    865 // ECMA 262 - 15.9.5.36
    866 function DateSetDate(date) {
    867   var t = LocalTime(DATE_VALUE(this));
    868   date = ToNumber(date);
    869   var day = MakeDay(YearFromTime(t), MonthFromTime(t), date);
    870   return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
    871 }
    872 
    873 
    874 // ECMA 262 - 15.9.5.37
    875 function DateSetUTCDate(date) {
    876   var t = DATE_VALUE(this);
    877   date = ToNumber(date);
    878   var day = MakeDay(YearFromTime(t), MonthFromTime(t), date);
    879   return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
    880 }
    881 
    882 
    883 // ECMA 262 - 15.9.5.38
    884 function DateSetMonth(month, date) {
    885   var t = LocalTime(DATE_VALUE(this));
    886   month = ToNumber(month);
    887   date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
    888   var day = MakeDay(YearFromTime(t), month, date);
    889   return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
    890 }
    891 
    892 
    893 // ECMA 262 - 15.9.5.39
    894 function DateSetUTCMonth(month, date) {
    895   var t = DATE_VALUE(this);
    896   month = ToNumber(month);
    897   date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
    898   var day = MakeDay(YearFromTime(t), month, date);
    899   return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
    900 }
    901 
    902 
    903 // ECMA 262 - 15.9.5.40
    904 function DateSetFullYear(year, month, date) {
    905   var t = DATE_VALUE(this);
    906   t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
    907   year = ToNumber(year);
    908   var argc = %_ArgumentsLength();
    909   month = argc < 2 ? MonthFromTime(t) : ToNumber(month);
    910   date = argc < 3 ? DateFromTime(t) : ToNumber(date);
    911   var day = MakeDay(year, month, date);
    912   return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
    913 }
    914 
    915 
    916 // ECMA 262 - 15.9.5.41
    917 function DateSetUTCFullYear(year, month, date) {
    918   var t = DATE_VALUE(this);
    919   if (NUMBER_IS_NAN(t)) t = 0;
    920   var argc = %_ArgumentsLength();
    921   year = ToNumber(year);
    922   month = argc < 2 ? MonthFromTime(t) : ToNumber(month);
    923   date = argc < 3 ? DateFromTime(t) : ToNumber(date);
    924   var day = MakeDay(year, month, date);
    925   return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
    926 }
    927 
    928 
    929 // ECMA 262 - 15.9.5.42
    930 function DateToUTCString() {
    931   var t = DATE_VALUE(this);
    932   if (NUMBER_IS_NAN(t)) return kInvalidDate;
    933   // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
    934   return WeekDays[WeekDay(t)] + ', '
    935       + TwoDigitString(DateFromTime(t)) + ' '
    936       + Months[MonthFromTime(t)] + ' '
    937       + YearFromTime(t) + ' '
    938       + TimeString(t) + ' GMT';
    939 }
    940 
    941 
    942 // ECMA 262 - B.2.4
    943 function DateGetYear() {
    944   var t = DATE_VALUE(this);
    945   if (NUMBER_IS_NAN(t)) return $NaN;
    946   return YearFromTime(LocalTimeNoCheck(t)) - 1900;
    947 }
    948 
    949 
    950 // ECMA 262 - B.2.5
    951 function DateSetYear(year) {
    952   var t = LocalTime(DATE_VALUE(this));
    953   if (NUMBER_IS_NAN(t)) t = 0;
    954   year = ToNumber(year);
    955   if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
    956   year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
    957       ? 1900 + TO_INTEGER(year) : year;
    958   var day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
    959   return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
    960 }
    961 
    962 
    963 // ECMA 262 - B.2.6
    964 //
    965 // Notice that this does not follow ECMA 262 completely.  ECMA 262
    966 // says that toGMTString should be the same Function object as
    967 // toUTCString.  JSC does not do this, so for compatibility we do not
    968 // do that either.  Instead, we create a new function whose name
    969 // property will return toGMTString.
    970 function DateToGMTString() {
    971   return %_CallFunction(this, DateToUTCString);
    972 }
    973 
    974 
    975 function PadInt(n, digits) {
    976   if (digits == 1) return n;
    977   return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
    978 }
    979 
    980 
    981 function DateToISOString() {
    982   var t = DATE_VALUE(this);
    983   if (NUMBER_IS_NAN(t)) return kInvalidDate;
    984   return this.getUTCFullYear() + 
    985       '-' + PadInt(this.getUTCMonth() + 1, 2) +
    986       '-' + PadInt(this.getUTCDate(), 2) + 
    987       'T' + PadInt(this.getUTCHours(), 2) +
    988       ':' + PadInt(this.getUTCMinutes(), 2) + 
    989       ':' + PadInt(this.getUTCSeconds(), 2) +
    990       '.' + PadInt(this.getUTCMilliseconds(), 3) +
    991       'Z';
    992 }
    993 
    994 
    995 function DateToJSON(key) {
    996   var o = ToObject(this);
    997   var tv = DefaultNumber(o);
    998   if (IS_NUMBER(tv) && !NUMBER_IS_FINITE(tv)) { 
    999     return null; 
   1000   }
   1001   return o.toISOString();
   1002 }
   1003 
   1004 
   1005 function ResetDateCache() {
   1006 
   1007   // Reset the local_time_offset:
   1008   local_time_offset = %DateLocalTimeOffset();
   1009 
   1010   // Reset the DST offset cache:
   1011   var cache = DST_offset_cache;
   1012   cache.offset = 0;
   1013   cache.start = 0;
   1014   cache.end = -1;
   1015   cache.increment = 0;
   1016   cache.initial_increment = 19 * msPerDay;
   1017 
   1018   // Reset the timezone cache:
   1019   timezone_cache_time = $NaN;
   1020   timezone_cache_timezone = undefined;
   1021 
   1022   // Reset the ltcache:
   1023   ltcache.key = null;
   1024   ltcache.val = null;
   1025 
   1026   // Reset the ymd_from_time_cache:
   1027   ymd_from_time_cache = [$NaN, $NaN, $NaN];
   1028   ymd_from_time_cached_time = $NaN;
   1029 
   1030   // Reset the date cache:
   1031   cache = Date_cache;
   1032   cache.time = $NaN;
   1033   cache.year = $NaN;
   1034   cache.string = null;
   1035 }
   1036 
   1037 
   1038 // -------------------------------------------------------------------
   1039 
   1040 function SetupDate() {
   1041   // Setup non-enumerable properties of the Date object itself.
   1042   InstallFunctions($Date, DONT_ENUM, $Array(
   1043     "UTC", DateUTC,
   1044     "parse", DateParse,
   1045     "now", DateNow
   1046   ));
   1047 
   1048   // Setup non-enumerable constructor property of the Date prototype object.
   1049   %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
   1050 
   1051   // Setup non-enumerable functions of the Date prototype object and
   1052   // set their names.
   1053   InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array(
   1054     "toString", DateToString,
   1055     "toDateString", DateToDateString,
   1056     "toTimeString", DateToTimeString,
   1057     "toLocaleString", DateToLocaleString,
   1058     "toLocaleDateString", DateToLocaleDateString,
   1059     "toLocaleTimeString", DateToLocaleTimeString,
   1060     "valueOf", DateValueOf,
   1061     "getTime", DateGetTime,
   1062     "getFullYear", DateGetFullYear,
   1063     "getUTCFullYear", DateGetUTCFullYear,
   1064     "getMonth", DateGetMonth,
   1065     "getUTCMonth", DateGetUTCMonth,
   1066     "getDate", DateGetDate,
   1067     "getUTCDate", DateGetUTCDate,
   1068     "getDay", DateGetDay,
   1069     "getUTCDay", DateGetUTCDay,
   1070     "getHours", DateGetHours,
   1071     "getUTCHours", DateGetUTCHours,
   1072     "getMinutes", DateGetMinutes,
   1073     "getUTCMinutes", DateGetUTCMinutes,
   1074     "getSeconds", DateGetSeconds,
   1075     "getUTCSeconds", DateGetUTCSeconds,
   1076     "getMilliseconds", DateGetMilliseconds,
   1077     "getUTCMilliseconds", DateGetUTCMilliseconds,
   1078     "getTimezoneOffset", DateGetTimezoneOffset,
   1079     "setTime", DateSetTime,
   1080     "setMilliseconds", DateSetMilliseconds,
   1081     "setUTCMilliseconds", DateSetUTCMilliseconds,
   1082     "setSeconds", DateSetSeconds,
   1083     "setUTCSeconds", DateSetUTCSeconds,
   1084     "setMinutes", DateSetMinutes,
   1085     "setUTCMinutes", DateSetUTCMinutes,
   1086     "setHours", DateSetHours,
   1087     "setUTCHours", DateSetUTCHours,
   1088     "setDate", DateSetDate,
   1089     "setUTCDate", DateSetUTCDate,
   1090     "setMonth", DateSetMonth,
   1091     "setUTCMonth", DateSetUTCMonth,
   1092     "setFullYear", DateSetFullYear,
   1093     "setUTCFullYear", DateSetUTCFullYear,
   1094     "toGMTString", DateToGMTString,
   1095     "toUTCString", DateToUTCString,
   1096     "getYear", DateGetYear,
   1097     "setYear", DateSetYear,
   1098     "toISOString", DateToISOString,
   1099     "toJSON", DateToJSON
   1100   ));
   1101 }
   1102 
   1103 SetupDate();
   1104