/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsdate.cpp
C++ | 2301 lines | 1637 code | 332 blank | 332 comment | 426 complexity | 18b9835e785a8d289b95cccd244e81bf MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * vim: set ts=8 sw=4 et tw=78: 3 * 4 * ***** BEGIN LICENSE BLOCK ***** 5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 6 * 7 * The contents of this file are subject to the Mozilla Public License Version 8 * 1.1 (the "License"); you may not use this file except in compliance with 9 * the License. You may obtain a copy of the License at 10 * http://www.mozilla.org/MPL/ 11 * 12 * Software distributed under the License is distributed on an "AS IS" basis, 13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 * for the specific language governing rights and limitations under the 15 * License. 16 * 17 * The Original Code is Mozilla Communicator client code, released 18 * March 31, 1998. 19 * 20 * The Initial Developer of the Original Code is 21 * Netscape Communications Corporation. 22 * Portions created by the Initial Developer are Copyright (C) 1998 23 * the Initial Developer. All Rights Reserved. 24 * 25 * Contributor(s): 26 * 27 * Alternatively, the contents of this file may be used under the terms of 28 * either of the GNU General Public License Version 2 or later (the "GPL"), 29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 30 * in which case the provisions of the GPL or the LGPL are applicable instead 31 * of those above. If you wish to allow use of your version of this file only 32 * under the terms of either the GPL or the LGPL, and not to allow others to 33 * use your version of this file under the terms of the MPL, indicate your 34 * decision by deleting the provisions above and replace them with the notice 35 * and other provisions required by the GPL or the LGPL. If you do not delete 36 * the provisions above, a recipient may use your version of this file under 37 * the terms of any one of the MPL, the GPL or the LGPL. 38 * 39 * ***** END LICENSE BLOCK ***** */ 40 41/* 42 * JS date methods. 43 */ 44 45/* 46 * "For example, OS/360 devotes 26 bytes of the permanently 47 * resident date-turnover routine to the proper handling of 48 * December 31 on leap years (when it is Day 366). That 49 * might have been left to the operator." 50 * 51 * Frederick Brooks, 'The Second-System Effect'. 52 */ 53 54#include "jsstddef.h" 55#include <ctype.h> 56#include <locale.h> 57#include <math.h> 58#include <stdlib.h> 59#include <string.h> 60#include "jstypes.h" 61#include "jsprf.h" 62#include "prmjtime.h" 63#include "jsutil.h" /* Added by JSIFY */ 64#include "jsapi.h" 65#include "jsversion.h" 66#include "jsbuiltins.h" 67#include "jscntxt.h" 68#include "jsdate.h" 69#include "jsinterp.h" 70#include "jsnum.h" 71#include "jsobj.h" 72#include "jsstr.h" 73 74/* 75 * The JS 'Date' object is patterned after the Java 'Date' object. 76 * Here is an script: 77 * 78 * today = new Date(); 79 * 80 * print(today.toLocaleString()); 81 * 82 * weekDay = today.getDay(); 83 * 84 * 85 * These Java (and ECMA-262) methods are supported: 86 * 87 * UTC 88 * getDate (getUTCDate) 89 * getDay (getUTCDay) 90 * getHours (getUTCHours) 91 * getMinutes (getUTCMinutes) 92 * getMonth (getUTCMonth) 93 * getSeconds (getUTCSeconds) 94 * getMilliseconds (getUTCMilliseconds) 95 * getTime 96 * getTimezoneOffset 97 * getYear 98 * getFullYear (getUTCFullYear) 99 * parse 100 * setDate (setUTCDate) 101 * setHours (setUTCHours) 102 * setMinutes (setUTCMinutes) 103 * setMonth (setUTCMonth) 104 * setSeconds (setUTCSeconds) 105 * setMilliseconds (setUTCMilliseconds) 106 * setTime 107 * setYear (setFullYear, setUTCFullYear) 108 * toGMTString (toUTCString) 109 * toLocaleString 110 * toString 111 * 112 * 113 * These Java methods are not supported 114 * 115 * setDay 116 * before 117 * after 118 * equals 119 * hashCode 120 */ 121 122/* 123 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language 124 * definition and reduce dependence on NSPR. NSPR is used to get the current 125 * time in milliseconds, the time zone offset, and the daylight savings time 126 * offset for a given time. NSPR is also used for Date.toLocaleString(), for 127 * locale-specific formatting, and to get a string representing the timezone. 128 * (Which turns out to be platform-dependent.) 129 * 130 * To do: 131 * (I did some performance tests by timing how long it took to run what 132 * I had of the js ECMA conformance tests.) 133 * 134 * - look at saving results across multiple calls to supporting 135 * functions; the toString functions compute some of the same values 136 * multiple times. Although - I took a quick stab at this, and I lost 137 * rather than gained. (Fractionally.) Hard to tell what compilers/processors 138 * are doing these days. 139 * 140 * - look at tweaking function return types to return double instead 141 * of int; this seems to make things run slightly faster sometimes. 142 * (though it could be architecture-dependent.) It'd be good to see 143 * how this does on win32. (Tried it on irix.) Types could use a 144 * general going-over. 145 */ 146 147/* 148 * Supporting functions - ECMA 15.9.1.* 149 */ 150 151#define HalfTimeDomain 8.64e15 152#define HoursPerDay 24.0 153#define MinutesPerDay (HoursPerDay * MinutesPerHour) 154#define MinutesPerHour 60.0 155#define SecondsPerDay (MinutesPerDay * SecondsPerMinute) 156#define SecondsPerHour (MinutesPerHour * SecondsPerMinute) 157#define SecondsPerMinute 60.0 158 159#if defined(XP_WIN) || defined(XP_OS2) 160/* Work around msvc double optimization bug by making these runtime values; if 161 * they're available at compile time, msvc optimizes division by them by 162 * computing the reciprocal and multiplying instead of dividing - this loses 163 * when the reciprocal isn't representable in a double. 164 */ 165static jsdouble msPerSecond = 1000.0; 166static jsdouble msPerDay = SecondsPerDay * 1000.0; 167static jsdouble msPerHour = SecondsPerHour * 1000.0; 168static jsdouble msPerMinute = SecondsPerMinute * 1000.0; 169#else 170#define msPerDay (SecondsPerDay * msPerSecond) 171#define msPerHour (SecondsPerHour * msPerSecond) 172#define msPerMinute (SecondsPerMinute * msPerSecond) 173#define msPerSecond 1000.0 174#endif 175 176#define Day(t) floor((t) / msPerDay) 177 178static jsdouble 179TimeWithinDay(jsdouble t) 180{ 181 jsdouble result; 182 result = fmod(t, msPerDay); 183 if (result < 0) 184 result += msPerDay; 185 return result; 186} 187 188#define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \ 189 ? 366 : 365) 190 191/* math here has to be f.p, because we need 192 * floor((1968 - 1969) / 4) == -1 193 */ 194#define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \ 195 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0)) 196#define TimeFromYear(y) (DayFromYear(y) * msPerDay) 197 198static jsint 199YearFromTime(jsdouble t) 200{ 201 jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970; 202 jsdouble t2 = (jsdouble) TimeFromYear(y); 203 204 if (t2 > t) { 205 y--; 206 } else { 207 if (t2 + msPerDay * DaysInYear(y) <= t) 208 y++; 209 } 210 return y; 211} 212 213#define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366) 214 215#define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year))) 216 217/* 218 * The following array contains the day of year for the first day of 219 * each month, where index 0 is January, and day 0 is January 1. 220 */ 221static jsdouble firstDayOfMonth[2][12] = { 222 {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0}, 223 {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0} 224}; 225 226#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]; 227 228static intN 229MonthFromTime(jsdouble t) 230{ 231 intN d, step; 232 jsint year = YearFromTime(t); 233 d = DayWithinYear(t, year); 234 235 if (d < (step = 31)) 236 return 0; 237 step += (InLeapYear(t) ? 29 : 28); 238 if (d < step) 239 return 1; 240 if (d < (step += 31)) 241 return 2; 242 if (d < (step += 30)) 243 return 3; 244 if (d < (step += 31)) 245 return 4; 246 if (d < (step += 30)) 247 return 5; 248 if (d < (step += 31)) 249 return 6; 250 if (d < (step += 31)) 251 return 7; 252 if (d < (step += 30)) 253 return 8; 254 if (d < (step += 31)) 255 return 9; 256 if (d < (step += 30)) 257 return 10; 258 return 11; 259} 260 261static intN 262DateFromTime(jsdouble t) 263{ 264 intN d, step, next; 265 jsint year = YearFromTime(t); 266 d = DayWithinYear(t, year); 267 268 if (d <= (next = 30)) 269 return d + 1; 270 step = next; 271 next += (InLeapYear(t) ? 29 : 28); 272 if (d <= next) 273 return d - step; 274 step = next; 275 if (d <= (next += 31)) 276 return d - step; 277 step = next; 278 if (d <= (next += 30)) 279 return d - step; 280 step = next; 281 if (d <= (next += 31)) 282 return d - step; 283 step = next; 284 if (d <= (next += 30)) 285 return d - step; 286 step = next; 287 if (d <= (next += 31)) 288 return d - step; 289 step = next; 290 if (d <= (next += 31)) 291 return d - step; 292 step = next; 293 if (d <= (next += 30)) 294 return d - step; 295 step = next; 296 if (d <= (next += 31)) 297 return d - step; 298 step = next; 299 if (d <= (next += 30)) 300 return d - step; 301 step = next; 302 return d - step; 303} 304 305static intN 306WeekDay(jsdouble t) 307{ 308 jsint result; 309 result = (jsint) Day(t) + 4; 310 result = result % 7; 311 if (result < 0) 312 result += 7; 313 return (intN) result; 314} 315 316#define MakeTime(hour, min, sec, ms) \ 317((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms)) 318 319static jsdouble 320MakeDay(jsdouble year, jsdouble month, jsdouble date) 321{ 322 JSBool leap; 323 jsdouble yearday; 324 jsdouble monthday; 325 326 year += floor(month / 12); 327 328 month = fmod(month, 12.0); 329 if (month < 0) 330 month += 12; 331 332 leap = (DaysInYear((jsint) year) == 366); 333 334 yearday = floor(TimeFromYear(year) / msPerDay); 335 monthday = DayFromMonth(month, leap); 336 337 return yearday + monthday + date - 1; 338} 339 340#define MakeDate(day, time) ((day) * msPerDay + (time)) 341 342/* 343 * Years and leap years on which Jan 1 is a Sunday, Monday, etc. 344 * 345 * yearStartingWith[0][i] is an example non-leap year where 346 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. 347 * 348 * yearStartingWith[1][i] is an example leap year where 349 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. 350 */ 351static jsint yearStartingWith[2][7] = { 352 {1978, 1973, 1974, 1975, 1981, 1971, 1977}, 353 {1984, 1996, 1980, 1992, 1976, 1988, 1972} 354}; 355 356/* 357 * Find a year for which any given date will fall on the same weekday. 358 * 359 * This function should be used with caution when used other than 360 * for determining DST; it hasn't been proven not to produce an 361 * incorrect year for times near year boundaries. 362 */ 363static jsint 364EquivalentYearForDST(jsint year) 365{ 366 jsint day; 367 JSBool isLeapYear; 368 369 day = (jsint) DayFromYear(year) + 4; 370 day = day % 7; 371 if (day < 0) 372 day += 7; 373 374 isLeapYear = (DaysInYear(year) == 366); 375 376 return yearStartingWith[isLeapYear][day]; 377} 378 379/* LocalTZA gets set by js_InitDateClass() */ 380static jsdouble LocalTZA; 381 382static jsdouble 383DaylightSavingTA(jsdouble t) 384{ 385 volatile int64 PR_t; 386 int64 ms2us; 387 int64 offset; 388 jsdouble result; 389 390 /* abort if NaN */ 391 if (JSDOUBLE_IS_NaN(t)) 392 return t; 393 394 /* 395 * If earlier than 1970 or after 2038, potentially beyond the ken of 396 * many OSes, map it to an equivalent year before asking. 397 */ 398 if (t < 0.0 || t > 2145916800000.0) { 399 jsint year; 400 jsdouble day; 401 402 year = EquivalentYearForDST(YearFromTime(t)); 403 day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); 404 t = MakeDate(day, TimeWithinDay(t)); 405 } 406 407 /* put our t in an LL, and map it to usec for prtime */ 408 JSLL_D2L(PR_t, t); 409 JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC); 410 JSLL_MUL(PR_t, PR_t, ms2us); 411 412 offset = PRMJ_DSTOffset(PR_t); 413 414 JSLL_DIV(offset, offset, ms2us); 415 JSLL_L2D(result, offset); 416 return result; 417} 418 419 420#define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay) 421 422#define LocalTime(t) ((t) + AdjustTime(t)) 423 424static jsdouble 425UTC(jsdouble t) 426{ 427 return t - AdjustTime(t - LocalTZA); 428} 429 430static intN 431HourFromTime(jsdouble t) 432{ 433 intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay); 434 if (result < 0) 435 result += (intN)HoursPerDay; 436 return result; 437} 438 439static intN 440MinFromTime(jsdouble t) 441{ 442 intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour); 443 if (result < 0) 444 result += (intN)MinutesPerHour; 445 return result; 446} 447 448static intN 449SecFromTime(jsdouble t) 450{ 451 intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute); 452 if (result < 0) 453 result += (intN)SecondsPerMinute; 454 return result; 455} 456 457static intN 458msFromTime(jsdouble t) 459{ 460 intN result = (intN) fmod(t, msPerSecond); 461 if (result < 0) 462 result += (intN)msPerSecond; 463 return result; 464} 465 466#define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \ 467 && !((d < 0 ? -d : d) > HalfTimeDomain)) \ 468 ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN) 469 470/** 471 * end of ECMA 'support' functions 472 */ 473 474/* 475 * Other Support routines and definitions 476 */ 477 478/* 479 * We use the first reseved slot to store UTC time, and the second for caching 480 * the local time. The initial value of the cache entry is NaN. 481 */ 482const uint32 JSSLOT_UTC_TIME = JSSLOT_PRIVATE; 483const uint32 JSSLOT_LOCAL_TIME = JSSLOT_PRIVATE + 1; 484 485const uint32 DATE_RESERVED_SLOTS = 2; 486 487JSClass js_DateClass = { 488 js_Date_str, 489 JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS) | 490 JSCLASS_HAS_CACHED_PROTO(JSProto_Date), 491 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, 492 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, 493 JSCLASS_NO_OPTIONAL_MEMBERS 494}; 495 496/* for use by date_parse */ 497 498static const char* wtb[] = { 499 "am", "pm", 500 "monday", "tuesday", "wednesday", "thursday", "friday", 501 "saturday", "sunday", 502 "january", "february", "march", "april", "may", "june", 503 "july", "august", "september", "october", "november", "december", 504 "gmt", "ut", "utc", 505 "est", "edt", 506 "cst", "cdt", 507 "mst", "mdt", 508 "pst", "pdt" 509 /* time zone table needs to be expanded */ 510}; 511 512static int ttb[] = { 513 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */ 514 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 515 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */ 516 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */ 517 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */ 518 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */ 519 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */ 520}; 521 522/* helper for date_parse */ 523static JSBool 524date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off, 525 int count, int ignoreCase) 526{ 527 JSBool result = JS_FALSE; 528 /* return true if matches, otherwise, false */ 529 530 while (count > 0 && s1[s1off] && s2[s2off]) { 531 if (ignoreCase) { 532 if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) { 533 break; 534 } 535 } else { 536 if ((jschar)s1[s1off] != s2[s2off]) { 537 break; 538 } 539 } 540 s1off++; 541 s2off++; 542 count--; 543 } 544 545 if (count == 0) { 546 result = JS_TRUE; 547 } 548 549 return result; 550} 551 552/* find UTC time from given date... no 1900 correction! */ 553static jsdouble 554date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour, 555 jsdouble min, jsdouble sec, jsdouble msec) 556{ 557 jsdouble day; 558 jsdouble msec_time; 559 jsdouble result; 560 561 day = MakeDay(year, mon, mday); 562 msec_time = MakeTime(hour, min, sec, msec); 563 result = MakeDate(day, msec_time); 564 return result; 565} 566 567/* compute the time in msec (unclipped) from the given args */ 568#define MAXARGS 7 569 570static JSBool 571date_msecFromArgs(JSContext *cx, uintN argc, jsval *argv, jsdouble *rval) 572{ 573 uintN loop; 574 jsdouble array[MAXARGS]; 575 jsdouble d; 576 jsdouble msec_time; 577 578 for (loop = 0; loop < MAXARGS; loop++) { 579 if (loop < argc) { 580 d = js_ValueToNumber(cx, &argv[loop]); 581 if (JSVAL_IS_NULL(argv[loop])) 582 return JS_FALSE; 583 /* return NaN if any arg is not finite */ 584 if (!JSDOUBLE_IS_FINITE(d)) { 585 *rval = *cx->runtime->jsNaN; 586 return JS_TRUE; 587 } 588 array[loop] = js_DoubleToInteger(d); 589 } else { 590 if (loop == 2) { 591 array[loop] = 1; /* Default the date argument to 1. */ 592 } else { 593 array[loop] = 0; 594 } 595 } 596 } 597 598 /* adjust 2-digit years into the 20th century */ 599 if (array[0] >= 0 && array[0] <= 99) 600 array[0] += 1900; 601 602 msec_time = date_msecFromDate(array[0], array[1], array[2], 603 array[3], array[4], array[5], array[6]); 604 *rval = msec_time; 605 return JS_TRUE; 606} 607 608/* 609 * See ECMA 15.9.4.[3-10]; 610 */ 611static JSBool 612date_UTC(JSContext *cx, uintN argc, jsval *vp) 613{ 614 jsdouble msec_time; 615 616 if (!date_msecFromArgs(cx, argc, vp + 2, &msec_time)) 617 return JS_FALSE; 618 619 msec_time = TIMECLIP(msec_time); 620 621 return js_NewNumberInRootedValue(cx, msec_time, vp); 622} 623 624static JSBool 625date_parseString(JSString *str, jsdouble *result) 626{ 627 jsdouble msec; 628 629 const jschar *s; 630 size_t limit; 631 size_t i = 0; 632 int year = -1; 633 int mon = -1; 634 int mday = -1; 635 int hour = -1; 636 int min = -1; 637 int sec = -1; 638 int c = -1; 639 int n = -1; 640 int tzoffset = -1; 641 int prevc = 0; 642 JSBool seenplusminus = JS_FALSE; 643 int temp; 644 JSBool seenmonthname = JS_FALSE; 645 646 JSSTRING_CHARS_AND_LENGTH(str, s, limit); 647 if (limit == 0) 648 goto syntax; 649 while (i < limit) { 650 c = s[i]; 651 i++; 652 if (c <= ' ' || c == ',' || c == '-') { 653 if (c == '-' && '0' <= s[i] && s[i] <= '9') { 654 prevc = c; 655 } 656 continue; 657 } 658 if (c == '(') { /* comments) */ 659 int depth = 1; 660 while (i < limit) { 661 c = s[i]; 662 i++; 663 if (c == '(') depth++; 664 else if (c == ')') 665 if (--depth <= 0) 666 break; 667 } 668 continue; 669 } 670 if ('0' <= c && c <= '9') { 671 n = c - '0'; 672 while (i < limit && '0' <= (c = s[i]) && c <= '9') { 673 n = n * 10 + c - '0'; 674 i++; 675 } 676 677 /* allow TZA before the year, so 678 * 'Wed Nov 05 21:49:11 GMT-0800 1997' 679 * works */ 680 681 /* uses of seenplusminus allow : in TZA, so Java 682 * no-timezone style of GMT+4:30 works 683 */ 684 685 if ((prevc == '+' || prevc == '-')/* && year>=0 */) { 686 /* make ':' case below change tzoffset */ 687 seenplusminus = JS_TRUE; 688 689 /* offset */ 690 if (n < 24) 691 n = n * 60; /* EG. "GMT-3" */ 692 else 693 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ 694 if (prevc == '+') /* plus means east of GMT */ 695 n = -n; 696 if (tzoffset != 0 && tzoffset != -1) 697 goto syntax; 698 tzoffset = n; 699 } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) { 700 if (c <= ' ' || c == ',' || c == '/' || i >= limit) 701 year = n; 702 else 703 goto syntax; 704 } else if (c == ':') { 705 if (hour < 0) 706 hour = /*byte*/ n; 707 else if (min < 0) 708 min = /*byte*/ n; 709 else 710 goto syntax; 711 } else if (c == '/') { 712 /* until it is determined that mon is the actual 713 month, keep it as 1-based rather than 0-based */ 714 if (mon < 0) 715 mon = /*byte*/ n; 716 else if (mday < 0) 717 mday = /*byte*/ n; 718 else 719 goto syntax; 720 } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') { 721 goto syntax; 722 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */ 723 if (tzoffset < 0) 724 tzoffset -= n; 725 else 726 tzoffset += n; 727 } else if (hour >= 0 && min < 0) { 728 min = /*byte*/ n; 729 } else if (prevc == ':' && min >= 0 && sec < 0) { 730 sec = /*byte*/ n; 731 } else if (mon < 0) { 732 mon = /*byte*/n; 733 } else if (mon >= 0 && mday < 0) { 734 mday = /*byte*/ n; 735 } else if (mon >= 0 && mday >= 0 && year < 0) { 736 year = n; 737 } else { 738 goto syntax; 739 } 740 prevc = 0; 741 } else if (c == '/' || c == ':' || c == '+' || c == '-') { 742 prevc = c; 743 } else { 744 size_t st = i - 1; 745 int k; 746 while (i < limit) { 747 c = s[i]; 748 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) 749 break; 750 i++; 751 } 752 if (i <= st + 1) 753 goto syntax; 754 for (k = JS_ARRAY_LENGTH(wtb); --k >= 0;) 755 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) { 756 int action = ttb[k]; 757 if (action != 0) { 758 if (action < 0) { 759 /* 760 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as 761 * 12:30, instead of blindly adding 12 if PM. 762 */ 763 JS_ASSERT(action == -1 || action == -2); 764 if (hour > 12 || hour < 0) { 765 goto syntax; 766 } else { 767 if (action == -1 && hour == 12) { /* am */ 768 hour = 0; 769 } else if (action == -2 && hour != 12) { /* pm */ 770 hour += 12; 771 } 772 } 773 } else if (action <= 13) { /* month! */ 774 /* Adjust mon to be 1-based until the final values 775 for mon, mday and year are adjusted below */ 776 if (seenmonthname) { 777 goto syntax; 778 } 779 seenmonthname = JS_TRUE; 780 temp = /*byte*/ (action - 2) + 1; 781 782 if (mon < 0) { 783 mon = temp; 784 } else if (mday < 0) { 785 mday = mon; 786 mon = temp; 787 } else if (year < 0) { 788 year = mon; 789 mon = temp; 790 } else { 791 goto syntax; 792 } 793 } else { 794 tzoffset = action - 10000; 795 } 796 } 797 break; 798 } 799 if (k < 0) 800 goto syntax; 801 prevc = 0; 802 } 803 } 804 if (year < 0 || mon < 0 || mday < 0) 805 goto syntax; 806 /* 807 Case 1. The input string contains an English month name. 808 The form of the string can be month f l, or f month l, or 809 f l month which each evaluate to the same date. 810 If f and l are both greater than or equal to 70, or 811 both less than 70, the date is invalid. 812 The year is taken to be the greater of the values f, l. 813 If the year is greater than or equal to 70 and less than 100, 814 it is considered to be the number of years after 1900. 815 Case 2. The input string is of the form "f/m/l" where f, m and l are 816 integers, e.g. 7/16/45. 817 Adjust the mon, mday and year values to achieve 100% MSIE 818 compatibility. 819 a. If 0 <= f < 70, f/m/l is interpreted as month/day/year. 820 i. If year < 100, it is the number of years after 1900 821 ii. If year >= 100, it is the number of years after 0. 822 b. If 70 <= f < 100 823 i. If m < 70, f/m/l is interpreted as 824 year/month/day where year is the number of years after 825 1900. 826 ii. If m >= 70, the date is invalid. 827 c. If f >= 100 828 i. If m < 70, f/m/l is interpreted as 829 year/month/day where year is the number of years after 0. 830 ii. If m >= 70, the date is invalid. 831 */ 832 if (seenmonthname) { 833 if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) { 834 goto syntax; 835 } 836 if (mday > year) { 837 temp = year; 838 year = mday; 839 mday = temp; 840 } 841 if (year >= 70 && year < 100) { 842 year += 1900; 843 } 844 } else if (mon < 70) { /* (a) month/day/year */ 845 if (year < 100) { 846 year += 1900; 847 } 848 } else if (mon < 100) { /* (b) year/month/day */ 849 if (mday < 70) { 850 temp = year; 851 year = mon + 1900; 852 mon = mday; 853 mday = temp; 854 } else { 855 goto syntax; 856 } 857 } else { /* (c) year/month/day */ 858 if (mday < 70) { 859 temp = year; 860 year = mon; 861 mon = mday; 862 mday = temp; 863 } else { 864 goto syntax; 865 } 866 } 867 mon -= 1; /* convert month to 0-based */ 868 if (sec < 0) 869 sec = 0; 870 if (min < 0) 871 min = 0; 872 if (hour < 0) 873 hour = 0; 874 if (tzoffset == -1) { /* no time zone specified, have to use local */ 875 jsdouble msec_time; 876 msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); 877 878 *result = UTC(msec_time); 879 return JS_TRUE; 880 } 881 882 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); 883 msec += tzoffset * msPerMinute; 884 *result = msec; 885 return JS_TRUE; 886 887syntax: 888 /* syntax error */ 889 *result = 0; 890 return JS_FALSE; 891} 892 893static JSBool 894date_parse(JSContext *cx, uintN argc, jsval *vp) 895{ 896 JSString *str; 897 jsdouble result; 898 899 if (argc == 0) { 900 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); 901 return JS_TRUE; 902 } 903 str = js_ValueToString(cx, vp[2]); 904 if (!str) 905 return JS_FALSE; 906 vp[2] = STRING_TO_JSVAL(str); 907 if (!date_parseString(str, &result)) { 908 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); 909 return JS_TRUE; 910 } 911 912 result = TIMECLIP(result); 913 return js_NewNumberInRootedValue(cx, result, vp); 914} 915 916static JSBool 917date_now(JSContext *cx, uintN argc, jsval *vp) 918{ 919 return js_NewDoubleInRootedValue(cx, PRMJ_Now() / PRMJ_USEC_PER_MSEC, vp); 920} 921 922#ifdef JS_TRACER 923static jsdouble FASTCALL 924date_now_tn(JSContext*) 925{ 926 return PRMJ_Now() / PRMJ_USEC_PER_MSEC; 927} 928#endif 929 930/* 931 * Get UTC time from the date object. Returns false if the object is not 932 * Date type. 933 */ 934static JSBool 935GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp) 936{ 937 if (!JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL)) 938 return JS_FALSE; 939 *dp = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]); 940 return JS_TRUE; 941} 942 943/* 944 * Set UTC time slot with a pointer pointing to a jsdouble. This function is 945 * used only for setting UTC time to some predefined values, such as NaN. 946 * 947 * It also invalidates cached local time. 948 */ 949static JSBool 950SetUTCTimePtr(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp) 951{ 952 if (vp && !JS_InstanceOf(cx, obj, &js_DateClass, vp + 2)) 953 return JS_FALSE; 954 JS_ASSERT_IF(!vp, STOBJ_GET_CLASS(obj) == &js_DateClass); 955 956 /* Invalidate local time cache. */ 957 obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); 958 obj->fslots[JSSLOT_UTC_TIME] = DOUBLE_TO_JSVAL(dp); 959 return JS_TRUE; 960} 961 962/* 963 * Set UTC time to a given time. 964 */ 965static JSBool 966SetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble t) 967{ 968 jsdouble *dp = js_NewWeaklyRootedDouble(cx, t); 969 if (!dp) 970 return JS_FALSE; 971 return SetUTCTimePtr(cx, obj, vp, dp); 972} 973 974/* 975 * Get the local time, cache it if necessary. If UTC time is not finite 976 * (e.g., NaN), the local time slot is set to the UTC time without conversion. 977 */ 978static JSBool 979GetAndCacheLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp) 980{ 981 jsval v; 982 jsdouble result; 983 jsdouble *cached; 984 985 if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL)) 986 return JS_FALSE; 987 v = obj->fslots[JSSLOT_LOCAL_TIME]; 988 989 result = *JSVAL_TO_DOUBLE(v); 990 991 if (JSDOUBLE_IS_NaN(result)) { 992 if (!GetUTCTime(cx, obj, vp, &result)) 993 return JS_FALSE; 994 995 /* if result is NaN, it couldn't be finite. */ 996 if (JSDOUBLE_IS_FINITE(result)) 997 result = LocalTime(result); 998 999 cached = js_NewWeaklyRootedDouble(cx, result); 1000 if (!cached) 1001 return JS_FALSE; 1002 1003 obj->fslots[JSSLOT_LOCAL_TIME] = DOUBLE_TO_JSVAL(cached); 1004 } 1005 1006 *dp = result; 1007 return JS_TRUE; 1008} 1009 1010/* 1011 * See ECMA 15.9.5.4 thru 15.9.5.23 1012 */ 1013static JSBool 1014date_getTime(JSContext *cx, uintN argc, jsval *vp) 1015{ 1016 jsdouble result; 1017 1018 return GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result) && 1019 js_NewNumberInRootedValue(cx, result, vp); 1020} 1021 1022static JSBool 1023GetYear(JSContext *cx, JSBool fullyear, jsval *vp) 1024{ 1025 jsdouble result; 1026 1027 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1028 return JS_FALSE; 1029 1030 if (JSDOUBLE_IS_FINITE(result)) { 1031 result = YearFromTime(result); 1032 1033 /* Follow ECMA-262 to the letter, contrary to IE JScript. */ 1034 if (!fullyear) 1035 result -= 1900; 1036 } 1037 1038 return js_NewNumberInRootedValue(cx, result, vp); 1039} 1040 1041static JSBool 1042date_getYear(JSContext *cx, uintN argc, jsval *vp) 1043{ 1044 return GetYear(cx, JS_FALSE, vp); 1045} 1046 1047static JSBool 1048date_getFullYear(JSContext *cx, uintN argc, jsval *vp) 1049{ 1050 return GetYear(cx, JS_TRUE, vp); 1051} 1052 1053static JSBool 1054date_getUTCFullYear(JSContext *cx, uintN argc, jsval *vp) 1055{ 1056 jsdouble result; 1057 1058 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1059 return JS_FALSE; 1060 1061 if (JSDOUBLE_IS_FINITE(result)) 1062 result = YearFromTime(result); 1063 1064 return js_NewNumberInRootedValue(cx, result, vp); 1065} 1066 1067static JSBool 1068date_getMonth(JSContext *cx, uintN argc, jsval *vp) 1069{ 1070 jsdouble result; 1071 1072 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1073 return JS_FALSE; 1074 1075 if (JSDOUBLE_IS_FINITE(result)) 1076 result = MonthFromTime(result); 1077 1078 return js_NewNumberInRootedValue(cx, result, vp); 1079} 1080 1081static JSBool 1082date_getUTCMonth(JSContext *cx, uintN argc, jsval *vp) 1083{ 1084 jsdouble result; 1085 1086 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1087 return JS_FALSE; 1088 1089 if (JSDOUBLE_IS_FINITE(result)) 1090 result = MonthFromTime(result); 1091 1092 return js_NewNumberInRootedValue(cx, result, vp); 1093} 1094 1095static JSBool 1096date_getDate(JSContext *cx, uintN argc, jsval *vp) 1097{ 1098 jsdouble result; 1099 1100 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1101 return JS_FALSE; 1102 1103 if (JSDOUBLE_IS_FINITE(result)) 1104 result = DateFromTime(result); 1105 1106 return js_NewNumberInRootedValue(cx, result, vp); 1107} 1108 1109static JSBool 1110date_getUTCDate(JSContext *cx, uintN argc, jsval *vp) 1111{ 1112 jsdouble result; 1113 1114 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1115 return JS_FALSE; 1116 1117 if (JSDOUBLE_IS_FINITE(result)) 1118 result = DateFromTime(result); 1119 1120 return js_NewNumberInRootedValue(cx, result, vp); 1121} 1122 1123static JSBool 1124date_getDay(JSContext *cx, uintN argc, jsval *vp) 1125{ 1126 jsdouble result; 1127 1128 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1129 return JS_FALSE; 1130 1131 if (JSDOUBLE_IS_FINITE(result)) 1132 result = WeekDay(result); 1133 1134 return js_NewNumberInRootedValue(cx, result, vp); 1135} 1136 1137static JSBool 1138date_getUTCDay(JSContext *cx, uintN argc, jsval *vp) 1139{ 1140 jsdouble result; 1141 1142 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1143 return JS_FALSE; 1144 1145 if (JSDOUBLE_IS_FINITE(result)) 1146 result = WeekDay(result); 1147 1148 return js_NewNumberInRootedValue(cx, result, vp); 1149} 1150 1151static JSBool 1152date_getHours(JSContext *cx, uintN argc, jsval *vp) 1153{ 1154 jsdouble result; 1155 1156 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1157 return JS_FALSE; 1158 1159 if (JSDOUBLE_IS_FINITE(result)) 1160 result = HourFromTime(result); 1161 1162 return js_NewNumberInRootedValue(cx, result, vp); 1163} 1164 1165static JSBool 1166date_getUTCHours(JSContext *cx, uintN argc, jsval *vp) 1167{ 1168 jsdouble result; 1169 1170 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1171 return JS_FALSE; 1172 1173 if (JSDOUBLE_IS_FINITE(result)) 1174 result = HourFromTime(result); 1175 1176 return js_NewNumberInRootedValue(cx, result, vp); 1177} 1178 1179static JSBool 1180date_getMinutes(JSContext *cx, uintN argc, jsval *vp) 1181{ 1182 jsdouble result; 1183 1184 if (!GetAndCacheLocalTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1185 return JS_FALSE; 1186 1187 if (JSDOUBLE_IS_FINITE(result)) 1188 result = MinFromTime(result); 1189 1190 return js_NewNumberInRootedValue(cx, result, vp); 1191} 1192 1193static JSBool 1194date_getUTCMinutes(JSContext *cx, uintN argc, jsval *vp) 1195{ 1196 jsdouble result; 1197 1198 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1199 return JS_FALSE; 1200 1201 if (JSDOUBLE_IS_FINITE(result)) 1202 result = MinFromTime(result); 1203 1204 return js_NewNumberInRootedValue(cx, result, vp); 1205} 1206 1207/* Date.getSeconds is mapped to getUTCSeconds */ 1208 1209static JSBool 1210date_getUTCSeconds(JSContext *cx, uintN argc, jsval *vp) 1211{ 1212 jsdouble result; 1213 1214 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1215 return JS_FALSE; 1216 1217 if (JSDOUBLE_IS_FINITE(result)) 1218 result = SecFromTime(result); 1219 1220 return js_NewNumberInRootedValue(cx, result, vp); 1221} 1222 1223/* Date.getMilliseconds is mapped to getUTCMilliseconds */ 1224 1225static JSBool 1226date_getUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp) 1227{ 1228 jsdouble result; 1229 1230 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &result)) 1231 return JS_FALSE; 1232 1233 if (JSDOUBLE_IS_FINITE(result)) 1234 result = msFromTime(result); 1235 1236 return js_NewNumberInRootedValue(cx, result, vp); 1237} 1238 1239static JSBool 1240date_getTimezoneOffset(JSContext *cx, uintN argc, jsval *vp) 1241{ 1242 JSObject *obj; 1243 jsdouble utctime, localtime, result; 1244 1245 obj = JS_THIS_OBJECT(cx, vp); 1246 if (!GetUTCTime(cx, obj, vp, &utctime)) 1247 return JS_FALSE; 1248 if (!GetAndCacheLocalTime(cx, obj, NULL, &localtime)) 1249 return JS_FALSE; 1250 1251 /* 1252 * Return the time zone offset in minutes for the current locale that is 1253 * appropriate for this time. This value would be a constant except for 1254 * daylight savings time. 1255 */ 1256 result = (utctime - localtime) / msPerMinute; 1257 return js_NewNumberInRootedValue(cx, result, vp); 1258} 1259 1260static JSBool 1261SetDateToNaN(JSContext *cx, jsval *vp) 1262{ 1263 JSObject *obj; 1264 1265 obj = JS_THIS_OBJECT(cx, vp); 1266 if (!SetUTCTimePtr(cx, obj, NULL, cx->runtime->jsNaN)) 1267 return JS_FALSE; 1268 *vp = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); 1269 return JS_TRUE; 1270} 1271 1272static JSBool 1273date_setTime(JSContext *cx, uintN argc, jsval *vp) 1274{ 1275 jsdouble result; 1276 1277 if (argc == 0) 1278 return SetDateToNaN(cx, vp); 1279 result = js_ValueToNumber(cx, &vp[2]); 1280 if (JSVAL_IS_NULL(vp[2])) 1281 return JS_FALSE; 1282 1283 result = TIMECLIP(result); 1284 1285 if (!SetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, result)) 1286 return JS_FALSE; 1287 1288 return js_NewNumberInRootedValue(cx, result, vp); 1289} 1290 1291static JSBool 1292date_makeTime(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp) 1293{ 1294 JSObject *obj; 1295 jsval *argv; 1296 uintN i; 1297 jsdouble args[4], *argp, *stop; 1298 jsdouble hour, min, sec, msec; 1299 jsdouble lorutime; /* Local or UTC version of *date */ 1300 1301 jsdouble msec_time; 1302 jsdouble result; 1303 1304 obj = JS_THIS_OBJECT(cx, vp); 1305 if (!GetUTCTime(cx, obj, vp, &result)) 1306 return JS_FALSE; 1307 1308 /* just return NaN if the date is already NaN */ 1309 if (!JSDOUBLE_IS_FINITE(result)) 1310 return js_NewNumberInRootedValue(cx, result, vp); 1311 1312 /* 1313 * Satisfy the ECMA rule that if a function is called with 1314 * fewer arguments than the specified formal arguments, the 1315 * remaining arguments are set to undefined. Seems like all 1316 * the Date.setWhatever functions in ECMA are only varargs 1317 * beyond the first argument; this should be set to undefined 1318 * if it's not given. This means that "d = new Date(); 1319 * d.setMilliseconds()" returns NaN. Blech. 1320 */ 1321 if (argc == 0) 1322 return SetDateToNaN(cx, vp); 1323 if (argc > maxargs) 1324 argc = maxargs; /* clamp argc */ 1325 JS_ASSERT(argc <= 4); 1326 1327 argv = vp + 2; 1328 for (i = 0; i < argc; i++) { 1329 args[i] = js_ValueToNumber(cx, &argv[i]); 1330 if (JSVAL_IS_NULL(argv[i])) 1331 return JS_FALSE; 1332 if (!JSDOUBLE_IS_FINITE(args[i])) 1333 return SetDateToNaN(cx, vp); 1334 args[i] = js_DoubleToInteger(args[i]); 1335 } 1336 1337 if (local) 1338 lorutime = LocalTime(result); 1339 else 1340 lorutime = result; 1341 1342 argp = args; 1343 stop = argp + argc; 1344 if (maxargs >= 4 && argp < stop) 1345 hour = *argp++; 1346 else 1347 hour = HourFromTime(lorutime); 1348 1349 if (maxargs >= 3 && argp < stop) 1350 min = *argp++; 1351 else 1352 min = MinFromTime(lorutime); 1353 1354 if (maxargs >= 2 && argp < stop) 1355 sec = *argp++; 1356 else 1357 sec = SecFromTime(lorutime); 1358 1359 if (maxargs >= 1 && argp < stop) 1360 msec = *argp; 1361 else 1362 msec = msFromTime(lorutime); 1363 1364 msec_time = MakeTime(hour, min, sec, msec); 1365 result = MakeDate(Day(lorutime), msec_time); 1366 1367/* fprintf(stderr, "%f\n", result); */ 1368 1369 if (local) 1370 result = UTC(result); 1371 1372/* fprintf(stderr, "%f\n", result); */ 1373 1374 result = TIMECLIP(result); 1375 if (!SetUTCTime(cx, obj, NULL, result)) 1376 return JS_FALSE; 1377 1378 return js_NewNumberInRootedValue(cx, result, vp); 1379} 1380 1381static JSBool 1382date_setMilliseconds(JSContext *cx, uintN argc, jsval *vp) 1383{ 1384 return date_makeTime(cx, 1, JS_TRUE, argc, vp); 1385} 1386 1387static JSBool 1388date_setUTCMilliseconds(JSContext *cx, uintN argc, jsval *vp) 1389{ 1390 return date_makeTime(cx, 1, JS_FALSE, argc, vp); 1391} 1392 1393static JSBool 1394date_setSeconds(JSContext *cx, uintN argc, jsval *vp) 1395{ 1396 return date_makeTime(cx, 2, JS_TRUE, argc, vp); 1397} 1398 1399static JSBool 1400date_setUTCSeconds(JSContext *cx, uintN argc, jsval *vp) 1401{ 1402 return date_makeTime(cx, 2, JS_FALSE, argc, vp); 1403} 1404 1405static JSBool 1406date_setMinutes(JSContext *cx, uintN argc, jsval *vp) 1407{ 1408 return date_makeTime(cx, 3, JS_TRUE, argc, vp); 1409} 1410 1411static JSBool 1412date_setUTCMinutes(JSContext *cx, uintN argc, jsval *vp) 1413{ 1414 return date_makeTime(cx, 3, JS_FALSE, argc, vp); 1415} 1416 1417static JSBool 1418date_setHours(JSContext *cx, uintN argc, jsval *vp) 1419{ 1420 return date_makeTime(cx, 4, JS_TRUE, argc, vp); 1421} 1422 1423static JSBool 1424date_setUTCHours(JSContext *cx, uintN argc, jsval *vp) 1425{ 1426 return date_makeTime(cx, 4, JS_FALSE, argc, vp); 1427} 1428 1429static JSBool 1430date_makeDate(JSContext *cx, uintN maxargs, JSBool local, uintN argc, jsval *vp) 1431{ 1432 JSObject *obj; 1433 jsval *argv; 1434 uintN i; 1435 jsdouble lorutime; /* local or UTC version of *date */ 1436 jsdouble args[3], *argp, *stop; 1437 jsdouble year, month, day; 1438 jsdouble result; 1439 1440 obj = JS_THIS_OBJECT(cx, vp); 1441 if (!GetUTCTime(cx, obj, vp, &result)) 1442 return JS_FALSE; 1443 1444 /* see complaint about ECMA in date_MakeTime */ 1445 if (argc == 0) 1446 return SetDateToNaN(cx, vp); 1447 if (argc > maxargs) 1448 argc = maxargs; /* clamp argc */ 1449 JS_ASSERT(1 <= argc && argc <= 3); 1450 1451 argv = vp + 2; 1452 for (i = 0; i < argc; i++) { 1453 args[i] = js_ValueToNumber(cx, &argv[i]); 1454 if (JSVAL_IS_NULL(argv[i])) 1455 return JS_FALSE; 1456 if (!JSDOUBLE_IS_FINITE(args[i])) 1457 return SetDateToNaN(cx, vp); 1458 args[i] = js_DoubleToInteger(args[i]); 1459 } 1460 1461 /* return NaN if date is NaN and we're not setting the year, 1462 * If we are, use 0 as the time. */ 1463 if (!(JSDOUBLE_IS_FINITE(result))) { 1464 if (maxargs < 3) 1465 return js_NewNumberInRootedValue(cx, result, vp); 1466 lorutime = +0.; 1467 } else { 1468 lorutime = local ? LocalTime(result) : result; 1469 } 1470 1471 argp = args; 1472 stop = argp + argc; 1473 if (maxargs >= 3 && argp < stop) 1474 year = *argp++; 1475 else 1476 year = YearFromTime(lorutime); 1477 1478 if (maxargs >= 2 && argp < stop) 1479 month = *argp++; 1480 else 1481 month = MonthFromTime(lorutime); 1482 1483 if (maxargs >= 1 && argp < stop) 1484 day = *argp++; 1485 else 1486 day = DateFromTime(lorutime); 1487 1488 day = MakeDay(year, month, day); /* day within year */ 1489 result = MakeDate(day, TimeWithinDay(lorutime)); 1490 1491 if (local) 1492 result = UTC(result); 1493 1494 result = TIMECLIP(result); 1495 if (!SetUTCTime(cx, obj, NULL, result)) 1496 return JS_FALSE; 1497 1498 return js_NewNumberInRootedValue(cx, result, vp); 1499} 1500 1501static JSBool 1502date_setDate(JSContext *cx, uintN argc, jsval *vp) 1503{ 1504 return date_makeDate(cx, 1, JS_TRUE, argc, vp); 1505} 1506 1507static JSBool 1508date_setUTCDate(JSContext *cx, uintN argc, jsval *vp) 1509{ 1510 return date_makeDate(cx, 1, JS_FALSE, argc, vp); 1511} 1512 1513static JSBool 1514date_setMonth(JSContext *cx, uintN argc, jsval *vp) 1515{ 1516 return date_makeDate(cx, 2, JS_TRUE, argc, vp); 1517} 1518 1519static JSBool 1520date_setUTCMonth(JSContext *cx, uintN argc, jsval *vp) 1521{ 1522 return date_makeDate(cx, 2, JS_FALSE, argc, vp); 1523} 1524 1525static JSBool 1526date_setFullYear(JSContext *cx, uintN argc, jsval *vp) 1527{ 1528 return date_makeDate(cx, 3, JS_TRUE, argc, vp); 1529} 1530 1531static JSBool 1532date_setUTCFullYear(JSContext *cx, uintN argc, jsval *vp) 1533{ 1534 return date_makeDate(cx, 3, JS_FALSE, argc, vp); 1535} 1536 1537static JSBool 1538date_setYear(JSContext *cx, uintN argc, jsval *vp) 1539{ 1540 JSObject *obj; 1541 jsdouble t; 1542 jsdouble year; 1543 jsdouble day; 1544 jsdouble result; 1545 1546 obj = JS_THIS_OBJECT(cx, vp); 1547 if (!GetUTCTime(cx, obj, vp, &result)) 1548 return JS_FALSE; 1549 1550 if (argc == 0) 1551 return SetDateToNaN(cx, vp); 1552 year = js_ValueToNumber(cx, &vp[2]); 1553 if (JSVAL_IS_NULL(vp[2])) 1554 return JS_FALSE; 1555 if (!JSDOUBLE_IS_FINITE(year)) 1556 return SetDateToNaN(cx, vp); 1557 1558 year = js_DoubleToInteger(year); 1559 1560 if (!JSDOUBLE_IS_FINITE(result)) { 1561 t = +0.0; 1562 } else { 1563 t = LocalTime(result); 1564 } 1565 1566 if (year >= 0 && year <= 99) 1567 year += 1900; 1568 1569 day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); 1570 result = MakeDate(day, TimeWithinDay(t)); 1571 result = UTC(result); 1572 1573 result = TIMECLIP(result); 1574 if (!SetUTCTime(cx, obj, NULL, result)) 1575 return JS_FALSE; 1576 1577 return js_NewNumberInRootedValue(cx, result, vp); 1578} 1579 1580/* constants for toString, toUTCString */ 1581static char js_NaN_date_str[] = "Invalid Date"; 1582static const char* days[] = 1583{ 1584 "Sun","Mon","Tue","Wed","Thu","Fri","Sat" 1585}; 1586static const char* months[] = 1587{ 1588 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 1589}; 1590 1591 1592// Avoid dependence on PRMJ_FormatTimeUSEnglish, because it 1593// requires a PRMJTime... which only has 16-bit years. Sub-ECMA. 1594static void 1595print_gmt_string(char* buf, size_t size, jsdouble utctime) 1596{ 1597 JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", 1598 days[WeekDay(utctime)], 1599 DateFromTime(utctime), 1600 months[MonthFromTime(utctime)], 1601 YearFromTime(utctime), 1602 HourFromTime(utctime), 1603 MinFromTime(utctime), 1604 SecFromTime(utctime)); 1605} 1606 1607static void 1608print_iso_string(char* buf, size_t size, jsdouble utctime) 1609{ 1610 JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ", 1611 YearFromTime(utctime), 1612 MonthFromTime(utctime) + 1, 1613 DateFromTime(utctime), 1614 HourFromTime(utctime), 1615 MinFromTime(utctime), 1616 SecFromTime(utctime), 1617 msFromTime(utctime)); 1618} 1619 1620static JSBool 1621date_utc_format(JSContext *cx, jsval *vp, 1622 void (*printFunc)(char*, size_t, jsdouble)) 1623{ 1624 char buf[100]; 1625 JSString *str; 1626 jsdouble utctime; 1627 1628 if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime)) 1629 return JS_FALSE; 1630 1631 if (!JSDOUBLE_IS_FINITE(utctime)) { 1632 JS_snprintf(buf, sizeof buf, js_NaN_date_str); 1633 } else { 1634 (*printFunc)(buf, sizeof buf, utctime); 1635 } 1636 str = JS_NewStringCopyZ(cx, buf); 1637 if (!str) 1638 return JS_FALSE; 1639 *vp = STRING_TO_JSVAL(str); 1640 return JS_TRUE; 1641} 1642 1643static JSBool 1644date_toGMTString(JSContext *cx, uintN argc, jsval *vp) 1645{ 1646 return date_utc_format(cx, vp, print_gmt_string); 1647} 1648 1649static JSBool 1650date_toISOString(JSContext *cx, uintN argc, jsval *vp) 1651{ 1652 return date_utc_format(cx, vp, print_iso_string); 1653} 1654 1655/* for Date.toLocaleString; interface to PRMJTime date struct. 1656 */ 1657static void 1658new_explode(jsdouble timeval, PRMJTime *split) 1659{ 1660 jsint year = YearFromTime(timeval); 1661 1662 split->tm_usec = (int32) msFromTime(timeval) * 1000; 1663 split->tm_sec = (int8) SecFromTime(timeval); 1664 split->tm_min = (int8) MinFromTime(timeval); 1665 split->tm_hour = (int8) HourFromTime(timeval); 1666 split->tm_mday = (int8) DateFromTime(timeval); 1667 split->tm_mon = (int8) MonthFromTime(timeval); 1668 split->tm_wday = (int8) WeekDay(timeval); 1669 split->tm_year = year; 1670 split->tm_yday = (int16) DayWithinYear(timeval, year); 1671 1672 /* not sure how this affects things, but it doesn't seem 1673 to matter. */ 1674 split->tm_isdst = (DaylightSavingTA(timeval) != 0); 1675} 1676 1677typedef enum formatspec { 1678 FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME 1679} formatspec; 1680 1681/* helper function */ 1682static JSBool 1683date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval) 1684{ 1685 char buf[100]; 1686 JSString *str; 1687 char tzbuf[100]; 1688 JSBool usetz; 1689 size_t i, tzlen; 1690 PRMJTime split; 1691 1692 if (!JSDOUBLE_IS_FINITE(date)) { 1693 JS_snprintf(buf, sizeof buf, js_NaN_date_str); 1694 } else { 1695 jsdouble local = LocalTime(date); 1696 1697 /* offset from GMT in minutes. The offset includes daylight savings, 1698 if it applies. */ 1699 jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute); 1700 1701 /* map 510 minutes to 0830 hours */ 1702 intN offset = (minutes / 60) * 100 + minutes % 60; 1703 1704 /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is 1705 * printed as 'GMT-0800' rather than as 'PST' to avoid 1706 * operating-system dependence on strftime (which 1707 * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints 1708 * PST as 'Pacific Standard Time.' This way we always know 1709 * what we're getting, and can parse it if we produce it. 1710 * The OS TZA string is included as a comment. 1711 */ 1712 1713 /* get a timezone string from the OS to include as a 1714 comment. */ 1715 new_explode(date, &split); 1716 if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) { 1717 1718 /* Decide whether to use the resulting timezone string. 1719 * 1720 * Reject it if it contains any non-ASCII, non-alphanumeric 1721 * characters. It's then likely in some other character 1722 * encoding, and we probably won't display it correctly. 1723 */ 1724 usetz = JS_TRUE; 1725 tzlen = strlen(tzbuf); 1726 if (tzlen > 100) { 1727 usetz = JS_FALSE; 1728 } else { 1729 for (i = 0; i < tzlen; i++) { 1730 jschar c = tzbuf[i]; 1731 if (c > 127 || 1732 !(isalpha(c) || isdigit(c) || 1733 c == ' ' || c == '(' || c == ')')) { 1734 usetz = JS_FALSE; 1735 } 1736 } 1737 } 1738 1739 /* Also reject it if it's not parenthesized or if it's '()'. */ 1740 if (tzbuf[0] != '(' || tzbuf[1] == ')') 1741 usetz = JS_FALSE; 1742 } else 1743 usetz = JS_FALSE; 1744 1745 switch (format) { 1746 case FORMATSPEC_FULL: 1747 /* 1748 * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it 1749 * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. 1750 */ 1751 /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */ 1752 JS_snprintf(buf, sizeof buf, 1753 "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s", 1754 days[WeekDay(local)], 1755 months[MonthFromTime(local)], 1756 DateFromTime(local), 1757 YearFromTime(local), 1758 HourFromTime(local), 1759 MinFromTime(local), 1760 SecFromTime(local), 1761 offset, 1762 usetz ? " " : "", 1763 usetz ? tzbuf : ""); 1764 break; 1765 case FORMATSPEC_DATE: 1766 /* Tue Oct 31 2000 */ 1767 JS_snprintf(buf, sizeof buf, 1768 "%s %s %.2d %.4d", 1769 days[WeekDay(local)], 1770 months[MonthFromTime(local)], 1771 DateFromTime(local), 1772 YearFromTime(local)); 1773 break; 1774 case FORMATSPEC_TIME: 1775 /* 09:41:40 GMT-0800 (PST) */ 1776 JS_snprintf(buf, sizeof buf, 1777 "%.2d:%.2d:%.2d GMT%+.4d%s%s", 1778 HourFromTime(local), 1779 MinFromTime(local), 1780 SecFromTime(local), 1781 offset, 1782 usetz ? " " : "", 1783 usetz ? tzbuf : ""); 1784 break; 1785 } 1786 } 1787 1788 str = JS_NewStringCopyZ(cx, buf); 1789 if (!str) 1790 return JS_FALSE; 1791 *rval = STRING_TO_JSVAL(str); 1792 return JS_TRUE; 1793} 1794 1795static JSBool 1796date_toLocaleHelper(JSContext *cx, const char *format, jsval *vp…
Large files files are truncated, but you can click here to view the full file