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