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. 41const $Date = global.Date; 42 43// Helper function to throw error. 44function ThrowDateTypeError() { 45 throw new $TypeError('this is not a Date object.'); 46} 47 48// ECMA 262 - 5.2 49function Modulo(value, remainder) { 50 var mod = value % remainder; 51 // Guard against returning -0. 52 if (mod == 0) return 0; 53 return mod >= 0 ? mod : mod + remainder; 54} 55 56 57function TimeWithinDay(time) { 58 return Modulo(time, msPerDay); 59} 60 61 62// ECMA 262 - 15.9.1.3 63function DaysInYear(year) { 64 if (year % 4 != 0) return 365; 65 if ((year % 100 == 0) && (year % 400 != 0)) return 365; 66 return 366; 67} 68 69 70function DayFromYear(year) { 71 return 365 * (year-1970) 72 + FLOOR((year-1969)/4) 73 - FLOOR((year-1901)/100) 74 + FLOOR((year-1601)/400); 75} 76 77 78function TimeFromYear(year) { 79 return msPerDay * DayFromYear(year); 80} 81 82 83function InLeapYear(time) { 84 return DaysInYear(YEAR_FROM_TIME(time)) == 366 ? 1 : 0; 85} 86 87 88function DayWithinYear(time) { 89 return DAY(time) - DayFromYear(YEAR_FROM_TIME(time)); 90} 91 92 93// ECMA 262 - 15.9.1.9 94function EquivalentYear(year) { 95 // Returns an equivalent year in the range [2008-2035] matching 96 // - leap year. 97 // - week day of first day. 98 var time = TimeFromYear(year); 99 var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) + 100 (WeekDay(time) * 12) % 28; 101 // Find the year in the range 2008..2037 that is equivalent mod 28. 102 // Add 3*28 to give a positive argument to the modulus operator. 103 return 2008 + (recent_year + 3*28 - 2008) % 28; 104} 105 106 107function EquivalentTime(t) { 108 // The issue here is that some library calls don't work right for dates 109 // that cannot be represented using a non-negative signed 32 bit integer 110 // (measured in whole seconds based on the 1970 epoch). 111 // We solve this by mapping the time to a year with same leap-year-ness 112 // and same starting day for the year. The ECMAscript specification says 113 // we must do this, but for compatibility with other browsers, we use 114 // the actual year if it is in the range 1970..2037 115 if (t >= 0 && t <= 2.1e12) return t; 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. 124var 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. 131var 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. 144function 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 212var timezone_cache_time = $NaN; 213var timezone_cache_timezone; 214 215function 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 227function WeekDay(time) { 228 return Modulo(DAY(time) + 4, 7); 229} 230 231 232function 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 238function 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 252function 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 265function 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 278function TimeInYear(year) { 279 return DaysInYear(year) * msPerDay; 280} 281 282 283// Compute modified Julian day from year, month, date. 284function 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 291var four_year_cycle_table = CalculateDateTable(); 292 293 294function 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. 326function DayTriplet(year, month, date) { 327 this.year = year; 328 this.month = month; 329 this.date = date; 330} 331 332var julian_day_cache_triplet; 333var 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. 337function 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) 382function 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 403function 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 411function 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. 420var 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. 493function GetTimeFrom(aDate) { 494 return DATE_VALUE(aDate); 495} 496 497function 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 504function 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 511function 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 518function 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 525function 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 532function 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 539function 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 546function 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 553function 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 562function 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 569function 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 576function 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 583function 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 590function 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 600var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; 601var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; 602 603 604function TwoDigitString(value) { 605 return value < 10 ? "0" + value : "" + value; 606} 607 608 609function 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 618var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 619var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 620 621 622function 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 631function TimeString(time) { 632 return TwoDigitString(HOUR_FROM_TIME(time)) + ':' 633 + TwoDigitString(MIN_FROM_TIME(time)) + ':' 634 + TwoDigitString(SEC_FROM_TIME(time)); 635} 636 637 638function 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 664function DatePrintString(time) { 665 return DateString(time) + ' ' + TimeString(time); 666} 667 668// ------------------------------------------------------------------- 669 670// Reused output buffer. Used when parsing date strings. 671var parse_buffer = $Array(7); 672 673// ECMA 262 - 15.9.4.2 674function 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 691function 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. 710function DateNow() { 711 return %DateCurrentTime(); 712} 713 714 715// ECMA 262 - 15.9.5.2 716function 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 725function 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 733function 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 742function DateToLocaleString() { 743 return DateToString.call(this); 744} 745 746 747// ECMA 262 - 15.9.5.6 748function 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 756function 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 765function DateValueOf() { 766 return DATE_VALUE(this); 767} 768 769 770// ECMA 262 - 15.9.5.9 771function DateGetTime() { 772 return DATE_VALUE(this); 773} 774 775 776// ECMA 262 - 15.9.5.10 777function DateGetFullYear() { 778 return GetFullYearFrom(this) 779} 780 781 782// ECMA 262 - 15.9.5.11 783function DateGetUTCFullYear() { 784 return GetUTCFullYearFrom(this) 785} 786 787 788// ECMA 262 - 15.9.5.12 789function DateGetMonth() { 790 return GetMonthFrom(this); 791} 792 793 794// ECMA 262 - 15.9.5.13 795function DateGetUTCMonth() { 796 return GetUTCMonthFrom(this); 797} 798 799 800// ECMA 262 - 15.9.5.14 801function DateGetDate() { 802 return GetDateFrom(this); 803} 804 805 806// ECMA 262 - 15.9.5.15 807function DateGetUTCDate() { 808 return GetUTCDateFrom(this); 809} 810 811 812// ECMA 262 - 15.9.5.16 813function 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 821function 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 829function DateGetHours() { 830 return GetHoursFrom(this); 831} 832 833 834// ECMA 262 - 15.9.5.19 835function DateGetUTCHours() { 836 return GetUTCHoursFrom(this); 837} 838 839 840// ECMA 262 - 15.9.5.20 841function DateGetMinutes() { 842 return GetMinutesFrom(this); 843} 844 845 846// ECMA 262 - 15.9.5.21 847function DateGetUTCMinutes() { 848 return GetUTCMinutesFrom(this); 849} 850 851 852// ECMA 262 - 15.9.5.22 853function DateGetSeconds() { 854 return GetSecondsFrom(this); 855} 856 857 858// ECMA 262 - 15.9.5.23 859function DateGetUTCSeconds() { 860 return GetUTCSecondsFrom(this); 861} 862 863 864// ECMA 262 - 15.9.5.24 865function DateGetMilliseconds() { 866 return GetMillisecondsFrom(this); 867} 868 869 870// ECMA 262 - 15.9.5.25 871function DateGetUTCMilliseconds() { 872 return GetUTCMillisecondsFrom(this); 873} 874 875 876// ECMA 262 - 15.9.5.26 877function 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 885function 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 892function 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 901function 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 910function 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 920function 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 930function 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 942function 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 954function 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 967function 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 980function 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 989function 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 998function 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 1008function 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 1018function 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 1031function 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 1044function 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 1057function 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 1065function 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. 1084function DateToGMTString() { 1085 return DateToUTCString.call(this); 1086} 1087 1088 1089function 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 1095function 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 1106function DateToJSON(key) { 1107 return CheckJSONPrimitive(this.toISOString()); 1108} 1109 1110 1111// ------------------------------------------------------------------- 1112 1113function 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 1176SetupDate(); 1177