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