PageRenderTime 133ms CodeModel.GetById 27ms app.highlight 92ms RepoModel.GetById 1ms app.codeStats 1ms

/js/src/jsdate.cpp

http://github.com/zpao/v8monkey
C++ | 2820 lines | 2011 code | 393 blank | 416 comment | 507 complexity | d888822aeeaa26b4c5976840971fb528 MD5 | raw file

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 <ctype.h>
  55#include <locale.h>
  56#include <math.h>
  57#include <stdlib.h>
  58#include <string.h>
  59
  60#include "mozilla/Util.h"
  61
  62#include "jstypes.h"
  63#include "jsprf.h"
  64#include "prmjtime.h"
  65#include "jsutil.h"
  66#include "jsapi.h"
  67#include "jsversion.h"
  68#include "jscntxt.h"
  69#include "jsdate.h"
  70#include "jsinterp.h"
  71#include "jsnum.h"
  72#include "jsobj.h"
  73#include "jsstr.h"
  74#include "jslibmath.h"
  75
  76#include "vm/GlobalObject.h"
  77
  78#include "jsinferinlines.h"
  79#include "jsobjinlines.h"
  80#include "jsstrinlines.h"
  81
  82#include "vm/Stack-inl.h"
  83
  84using namespace mozilla;
  85using namespace js;
  86using namespace js::types;
  87
  88/*
  89 * The JS 'Date' object is patterned after the Java 'Date' object.
  90 * Here is an script:
  91 *
  92 *    today = new Date();
  93 *
  94 *    print(today.toLocaleString());
  95 *
  96 *    weekDay = today.getDay();
  97 *
  98 *
  99 * These Java (and ECMA-262) methods are supported:
 100 *
 101 *     UTC
 102 *     getDate (getUTCDate)
 103 *     getDay (getUTCDay)
 104 *     getHours (getUTCHours)
 105 *     getMinutes (getUTCMinutes)
 106 *     getMonth (getUTCMonth)
 107 *     getSeconds (getUTCSeconds)
 108 *     getMilliseconds (getUTCMilliseconds)
 109 *     getTime
 110 *     getTimezoneOffset
 111 *     getYear
 112 *     getFullYear (getUTCFullYear)
 113 *     parse
 114 *     setDate (setUTCDate)
 115 *     setHours (setUTCHours)
 116 *     setMinutes (setUTCMinutes)
 117 *     setMonth (setUTCMonth)
 118 *     setSeconds (setUTCSeconds)
 119 *     setMilliseconds (setUTCMilliseconds)
 120 *     setTime
 121 *     setYear (setFullYear, setUTCFullYear)
 122 *     toGMTString (toUTCString)
 123 *     toLocaleString
 124 *     toString
 125 *
 126 *
 127 * These Java methods are not supported
 128 *
 129 *     setDay
 130 *     before
 131 *     after
 132 *     equals
 133 *     hashCode
 134 */
 135
 136/*
 137 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language
 138 * definition and reduce dependence on NSPR.  NSPR is used to get the current
 139 * time in milliseconds, the time zone offset, and the daylight savings time
 140 * offset for a given time.  NSPR is also used for Date.toLocaleString(), for
 141 * locale-specific formatting, and to get a string representing the timezone.
 142 * (Which turns out to be platform-dependent.)
 143 *
 144 * To do:
 145 * (I did some performance tests by timing how long it took to run what
 146 *  I had of the js ECMA conformance tests.)
 147 *
 148 * - look at saving results across multiple calls to supporting
 149 * functions; the toString functions compute some of the same values
 150 * multiple times.  Although - I took a quick stab at this, and I lost
 151 * rather than gained.  (Fractionally.)  Hard to tell what compilers/processors
 152 * are doing these days.
 153 *
 154 * - look at tweaking function return types to return double instead
 155 * of int; this seems to make things run slightly faster sometimes.
 156 * (though it could be architecture-dependent.)  It'd be good to see
 157 * how this does on win32.  (Tried it on irix.)  Types could use a
 158 * general going-over.
 159 */
 160
 161/*
 162 * Supporting functions - ECMA 15.9.1.*
 163 */
 164
 165#define HoursPerDay     24.0
 166#define MinutesPerDay   (HoursPerDay * MinutesPerHour)
 167#define MinutesPerHour  60.0
 168#define SecondsPerDay   (MinutesPerDay * SecondsPerMinute)
 169#define SecondsPerHour  (MinutesPerHour * SecondsPerMinute)
 170#define SecondsPerMinute 60.0
 171
 172#if defined(XP_WIN) || defined(XP_OS2)
 173/* Work around msvc double optimization bug by making these runtime values; if
 174 * they're available at compile time, msvc optimizes division by them by
 175 * computing the reciprocal and multiplying instead of dividing - this loses
 176 * when the reciprocal isn't representable in a double.
 177 */
 178static jsdouble msPerSecond = 1000.0;
 179static jsdouble msPerDay = SecondsPerDay * 1000.0;
 180static jsdouble msPerHour = SecondsPerHour * 1000.0;
 181static jsdouble msPerMinute = SecondsPerMinute * 1000.0;
 182#else
 183#define msPerDay        (SecondsPerDay * msPerSecond)
 184#define msPerHour       (SecondsPerHour * msPerSecond)
 185#define msPerMinute     (SecondsPerMinute * msPerSecond)
 186#define msPerSecond     1000.0
 187#endif
 188
 189#define Day(t)          floor((t) / msPerDay)
 190
 191static jsdouble
 192TimeWithinDay(jsdouble t)
 193{
 194    jsdouble result;
 195    result = fmod(t, msPerDay);
 196    if (result < 0)
 197        result += msPerDay;
 198    return result;
 199}
 200
 201static inline bool
 202IsLeapYear(jsint year)
 203{
 204    return year % 4 == 0 && (year % 100 || (year % 400 == 0));
 205}
 206
 207static inline jsint
 208DaysInYear(jsint year)
 209{
 210    return IsLeapYear(year) ? 366 : 365;
 211}
 212
 213static inline jsint
 214DaysInFebruary(jsint year)
 215{
 216    return IsLeapYear(year) ? 29 : 28;
 217}
 218
 219/* math here has to be f.p, because we need
 220 *  floor((1968 - 1969) / 4) == -1
 221 */
 222#define DayFromYear(y)  (365 * ((y)-1970) + floor(((y)-1969)/4.0)            \
 223                         - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))
 224#define TimeFromYear(y) (DayFromYear(y) * msPerDay)
 225
 226static jsint
 227YearFromTime(jsdouble t)
 228{
 229    jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970;
 230    jsdouble t2 = (jsdouble) TimeFromYear(y);
 231
 232    /*
 233     * Adjust the year if the approximation was wrong.  Since the year was
 234     * computed using the average number of ms per year, it will usually
 235     * be wrong for dates within several hours of a year transition.
 236     */
 237    if (t2 > t) {
 238        y--;
 239    } else {
 240        if (t2 + msPerDay * DaysInYear(y) <= t)
 241            y++;
 242    }
 243    return y;
 244}
 245
 246#define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))
 247
 248/*
 249 * The following array contains the day of year for the first day of
 250 * each month, where index 0 is January, and day 0 is January 1.
 251 */
 252static jsdouble firstDayOfMonth[2][13] = {
 253    {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0, 365.0},
 254    {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0, 366.0}
 255};
 256
 257#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]
 258
 259static intN
 260DaysInMonth(jsint year, jsint month)
 261{
 262    JSBool leap = IsLeapYear(year);
 263    intN result = intN(DayFromMonth(month, leap) - DayFromMonth(month-1, leap));
 264    return result;
 265}
 266
 267static intN
 268MonthFromTime(jsdouble t)
 269{
 270    intN d, step;
 271    jsint year = YearFromTime(t);
 272    d = DayWithinYear(t, year);
 273
 274    if (d < (step = 31))
 275        return 0;
 276    if (d < (step += DaysInFebruary(year)))
 277        return 1;
 278    if (d < (step += 31))
 279        return 2;
 280    if (d < (step += 30))
 281        return 3;
 282    if (d < (step += 31))
 283        return 4;
 284    if (d < (step += 30))
 285        return 5;
 286    if (d < (step += 31))
 287        return 6;
 288    if (d < (step += 31))
 289        return 7;
 290    if (d < (step += 30))
 291        return 8;
 292    if (d < (step += 31))
 293        return 9;
 294    if (d < (step += 30))
 295        return 10;
 296    return 11;
 297}
 298
 299static intN
 300DateFromTime(jsdouble t)
 301{
 302    intN d, step, next;
 303    jsint year = YearFromTime(t);
 304    d = DayWithinYear(t, year);
 305
 306    if (d <= (next = 30))
 307        return d + 1;
 308    step = next;
 309    if (d <= (next += DaysInFebruary(year)))
 310        return d - step;
 311    step = next;
 312    if (d <= (next += 31))
 313        return d - step;
 314    step = next;
 315    if (d <= (next += 30))
 316        return d - step;
 317    step = next;
 318    if (d <= (next += 31))
 319        return d - step;
 320    step = next;
 321    if (d <= (next += 30))
 322        return d - step;
 323    step = next;
 324    if (d <= (next += 31))
 325        return d - step;
 326    step = next;
 327    if (d <= (next += 31))
 328        return d - step;
 329    step = next;
 330    if (d <= (next += 30))
 331        return d - step;
 332    step = next;
 333    if (d <= (next += 31))
 334        return d - step;
 335    step = next;
 336    if (d <= (next += 30))
 337        return d - step;
 338    step = next;
 339    return d - step;
 340}
 341
 342static intN
 343WeekDay(jsdouble t)
 344{
 345    jsint result;
 346    result = (jsint) Day(t) + 4;
 347    result = result % 7;
 348    if (result < 0)
 349        result += 7;
 350    return (intN) result;
 351}
 352
 353#define MakeTime(hour, min, sec, ms) \
 354((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms))
 355
 356static jsdouble
 357MakeDay(jsdouble year, jsdouble month, jsdouble date)
 358{
 359    JSBool leap;
 360    jsdouble yearday;
 361    jsdouble monthday;
 362
 363    year += floor(month / 12);
 364
 365    month = fmod(month, 12.0);
 366    if (month < 0)
 367        month += 12;
 368
 369    leap = IsLeapYear((jsint) year);
 370
 371    yearday = floor(TimeFromYear(year) / msPerDay);
 372    monthday = DayFromMonth(month, leap);
 373
 374    return yearday + monthday + date - 1;
 375}
 376
 377#define MakeDate(day, time) ((day) * msPerDay + (time))
 378
 379/*
 380 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
 381 *
 382 * yearStartingWith[0][i] is an example non-leap year where
 383 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
 384 *
 385 * yearStartingWith[1][i] is an example leap year where
 386 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
 387 */
 388static jsint yearStartingWith[2][7] = {
 389    {1978, 1973, 1974, 1975, 1981, 1971, 1977},
 390    {1984, 1996, 1980, 1992, 1976, 1988, 1972}
 391};
 392
 393/*
 394 * Find a year for which any given date will fall on the same weekday.
 395 *
 396 * This function should be used with caution when used other than
 397 * for determining DST; it hasn't been proven not to produce an
 398 * incorrect year for times near year boundaries.
 399 */
 400static jsint
 401EquivalentYearForDST(jsint year)
 402{
 403    jsint day;
 404
 405    day = (jsint) DayFromYear(year) + 4;
 406    day = day % 7;
 407    if (day < 0)
 408        day += 7;
 409
 410    return yearStartingWith[IsLeapYear(year)][day];
 411}
 412
 413/* LocalTZA gets set by js_InitDateClass() */
 414static jsdouble LocalTZA;
 415
 416static jsdouble
 417DaylightSavingTA(jsdouble t, JSContext *cx)
 418{
 419    /* abort if NaN */
 420    if (JSDOUBLE_IS_NaN(t))
 421        return t;
 422
 423    /*
 424     * If earlier than 1970 or after 2038, potentially beyond the ken of
 425     * many OSes, map it to an equivalent year before asking.
 426     */
 427    if (t < 0.0 || t > 2145916800000.0) {
 428        jsint year = EquivalentYearForDST(YearFromTime(t));
 429        jsdouble day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
 430        t = MakeDate(day, TimeWithinDay(t));
 431    }
 432
 433    int64_t timeMilliseconds = static_cast<int64_t>(t);
 434    int64_t offsetMilliseconds = cx->dstOffsetCache.getDSTOffsetMilliseconds(timeMilliseconds, cx);
 435    return static_cast<jsdouble>(offsetMilliseconds);
 436}
 437
 438static jsdouble
 439AdjustTime(jsdouble date, JSContext *cx)
 440{
 441    jsdouble t = DaylightSavingTA(date, cx) + LocalTZA;
 442    t = (LocalTZA >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
 443    return t;
 444}
 445
 446static jsdouble
 447LocalTime(jsdouble t, JSContext *cx)
 448{
 449    return t + AdjustTime(t, cx);
 450}
 451
 452static jsdouble
 453UTC(jsdouble t, JSContext *cx)
 454{
 455    return t - AdjustTime(t - LocalTZA, cx);
 456}
 457
 458static intN
 459HourFromTime(jsdouble t)
 460{
 461    intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);
 462    if (result < 0)
 463        result += (intN)HoursPerDay;
 464    return result;
 465}
 466
 467static intN
 468MinFromTime(jsdouble t)
 469{
 470    intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);
 471    if (result < 0)
 472        result += (intN)MinutesPerHour;
 473    return result;
 474}
 475
 476static intN
 477SecFromTime(jsdouble t)
 478{
 479    intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);
 480    if (result < 0)
 481        result += (intN)SecondsPerMinute;
 482    return result;
 483}
 484
 485static intN
 486msFromTime(jsdouble t)
 487{
 488    intN result = (intN) fmod(t, msPerSecond);
 489    if (result < 0)
 490        result += (intN)msPerSecond;
 491    return result;
 492}
 493
 494/**
 495 * end of ECMA 'support' functions
 496 */
 497
 498static JSBool
 499date_convert(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
 500{
 501    JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
 502    JS_ASSERT(obj->isDate());
 503
 504    return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp);
 505}
 506
 507/*
 508 * Other Support routines and definitions
 509 */
 510
 511Class js::DateClass = {
 512    js_Date_str,
 513    JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_CLASS_RESERVED_SLOTS) |
 514    JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
 515    JS_PropertyStub,         /* addProperty */
 516    JS_PropertyStub,         /* delProperty */
 517    JS_PropertyStub,         /* getProperty */
 518    JS_StrictPropertyStub,   /* setProperty */
 519    JS_EnumerateStub,
 520    JS_ResolveStub,
 521    date_convert
 522};
 523
 524/* for use by date_parse */
 525
 526static const char* wtb[] = {
 527    "am", "pm",
 528    "monday", "tuesday", "wednesday", "thursday", "friday",
 529    "saturday", "sunday",
 530    "january", "february", "march", "april", "may", "june",
 531    "july", "august", "september", "october", "november", "december",
 532    "gmt", "ut", "utc",
 533    "est", "edt",
 534    "cst", "cdt",
 535    "mst", "mdt",
 536    "pst", "pdt"
 537    /* time zone table needs to be expanded */
 538};
 539
 540static int ttb[] = {
 541    -1, -2, 0, 0, 0, 0, 0, 0, 0,       /* AM/PM */
 542    2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
 543    10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */
 544    10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */
 545    10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */
 546    10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */
 547    10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */
 548};
 549
 550/* helper for date_parse */
 551static JSBool
 552date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
 553                   int count, int ignoreCase)
 554{
 555    JSBool result = JS_FALSE;
 556    /* return true if matches, otherwise, false */
 557
 558    while (count > 0 && s1[s1off] && s2[s2off]) {
 559        if (ignoreCase) {
 560            if (unicode::ToLowerCase(s1[s1off]) != unicode::ToLowerCase(s2[s2off]))
 561                break;
 562        } else {
 563            if ((jschar)s1[s1off] != s2[s2off]) {
 564                break;
 565            }
 566        }
 567        s1off++;
 568        s2off++;
 569        count--;
 570    }
 571
 572    if (count == 0) {
 573        result = JS_TRUE;
 574    }
 575
 576    return result;
 577}
 578
 579/* find UTC time from given date... no 1900 correction! */
 580static jsdouble
 581date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,
 582                  jsdouble min, jsdouble sec, jsdouble msec)
 583{
 584    jsdouble day;
 585    jsdouble msec_time;
 586    jsdouble result;
 587
 588    day = MakeDay(year, mon, mday);
 589    msec_time = MakeTime(hour, min, sec, msec);
 590    result = MakeDate(day, msec_time);
 591    return result;
 592}
 593
 594/* compute the time in msec (unclipped) from the given args */
 595#define MAXARGS        7
 596
 597static JSBool
 598date_msecFromArgs(JSContext *cx, CallArgs args, double *rval)
 599{
 600    uintN loop;
 601    jsdouble array[MAXARGS];
 602    jsdouble msec_time;
 603
 604    for (loop = 0; loop < MAXARGS; loop++) {
 605        if (loop < args.length()) {
 606            jsdouble d;
 607            if (!ToNumber(cx, args[loop], &d))
 608                return JS_FALSE;
 609            /* return NaN if any arg is not finite */
 610            if (!JSDOUBLE_IS_FINITE(d)) {
 611                *rval = js_NaN;
 612                return JS_TRUE;
 613            }
 614            array[loop] = js_DoubleToInteger(d);
 615        } else {
 616            if (loop == 2) {
 617                array[loop] = 1; /* Default the date argument to 1. */
 618            } else {
 619                array[loop] = 0;
 620            }
 621        }
 622    }
 623
 624    /* adjust 2-digit years into the 20th century */
 625    if (array[0] >= 0 && array[0] <= 99)
 626        array[0] += 1900;
 627
 628    msec_time = date_msecFromDate(array[0], array[1], array[2],
 629                                  array[3], array[4], array[5], array[6]);
 630    *rval = msec_time;
 631    return JS_TRUE;
 632}
 633
 634/*
 635 * See ECMA 15.9.4.[3-10];
 636 */
 637static JSBool
 638date_UTC(JSContext *cx, uintN argc, Value *vp)
 639{
 640    CallArgs args = CallArgsFromVp(argc, vp);
 641
 642    jsdouble msec_time;
 643    if (!date_msecFromArgs(cx, args, &msec_time))
 644        return JS_FALSE;
 645
 646    msec_time = TIMECLIP(msec_time);
 647
 648    args.rval().setNumber(msec_time);
 649    return JS_TRUE;
 650}
 651
 652/*
 653 * Read and convert decimal digits from s[*i] into *result
 654 * while *i < limit.
 655 *
 656 * Succeed if any digits are converted. Advance *i only
 657 * as digits are consumed.
 658 */
 659static JSBool
 660digits(size_t *result, const jschar *s, size_t *i, size_t limit)
 661{
 662    size_t init = *i;
 663    *result = 0;
 664    while (*i < limit &&
 665           ('0' <= s[*i] && s[*i] <= '9')) {
 666        *result *= 10;
 667        *result += (s[*i] - '0');
 668        ++(*i);
 669    }
 670    return (*i != init);
 671}
 672
 673/*
 674 * Read and convert decimal digits to the right of a decimal point,
 675 * representing a fractional integer, from s[*i] into *result
 676 * while *i < limit.
 677 *
 678 * Succeed if any digits are converted. Advance *i only
 679 * as digits are consumed.
 680 */
 681static JSBool
 682fractional(jsdouble *result, const jschar *s, size_t *i, size_t limit)
 683{
 684    jsdouble factor = 0.1;
 685    size_t init = *i;
 686    *result = 0.0;
 687    while (*i < limit &&
 688           ('0' <= s[*i] && s[*i] <= '9')) {
 689        *result += (s[*i] - '0') * factor;
 690        factor *= 0.1;
 691        ++(*i);
 692    }
 693    return (*i != init);
 694}
 695
 696/*
 697 * Read and convert exactly n decimal digits from s[*i]
 698 * to s[min(*i+n,limit)] into *result.
 699 *
 700 * Succeed if exactly n digits are converted. Advance *i only
 701 * on success.
 702 */
 703static JSBool
 704ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
 705{
 706    size_t init = *i;
 707
 708    if (digits(result, s, i, JS_MIN(limit, init+n)))
 709        return ((*i - init) == n);
 710
 711    *i = init;
 712    return JS_FALSE;
 713}
 714
 715/*
 716 * Parse a string in one of the date-time formats given by the W3C
 717 * "NOTE-datetime" specification. These formats make up a restricted
 718 * profile of the ISO 8601 format. Quoted here:
 719 *
 720 *   The formats are as follows. Exactly the components shown here
 721 *   must be present, with exactly this punctuation. Note that the "T"
 722 *   appears literally in the string, to indicate the beginning of the
 723 *   time element, as specified in ISO 8601.
 724 *
 725 *   Any combination of the date formats with the time formats is
 726 *   allowed, and also either the date or the time can be missing.
 727 *
 728 *   The specification is silent on the meaning when fields are
 729 *   ommitted so the interpretations are a guess, but hopefully a
 730 *   reasonable one. We default the month to January, the day to the
 731 *   1st, and hours minutes and seconds all to 0. If the date is
 732 *   missing entirely then we assume 1970-01-01 so that the time can
 733 *   be aded to a date later. If the time is missing then we assume
 734 *   00:00 UTC.  If the time is present but the time zone field is
 735 *   missing then we use local time.
 736 *
 737 * Date part:
 738 *
 739 *  Year:
 740 *     YYYY (eg 1997)
 741 *
 742 *  Year and month:
 743 *     YYYY-MM (eg 1997-07)
 744 *
 745 *  Complete date:
 746 *     YYYY-MM-DD (eg 1997-07-16)
 747 *
 748 * Time part:
 749 *
 750 *  Hours and minutes:
 751 *     Thh:mmTZD (eg T19:20+01:00)
 752 *
 753 *  Hours, minutes and seconds:
 754 *     Thh:mm:ssTZD (eg T19:20:30+01:00)
 755 *
 756 *  Hours, minutes, seconds and a decimal fraction of a second:
 757 *     Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
 758 *
 759 * where:
 760 *
 761 *   YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
 762 *   MM   = two-digit month (01=January, etc.)
 763 *   DD   = two-digit day of month (01 through 31)
 764 *   hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
 765 *   mm   = two digits of minute (00 through 59)
 766 *   ss   = two digits of second (00 through 59)
 767 *   s    = one or more digits representing a decimal fraction of a second
 768 *   TZD  = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
 769 */
 770
 771static JSBool
 772date_parseISOString(JSLinearString *str, jsdouble *result, JSContext *cx)
 773{
 774    jsdouble msec;
 775
 776    const jschar *s;
 777    size_t limit;
 778    size_t i = 0;
 779    int tzMul = 1;
 780    int dateMul = 1;
 781    size_t year = 1970;
 782    size_t month = 1;
 783    size_t day = 1;
 784    size_t hour = 0;
 785    size_t min = 0;
 786    size_t sec = 0;
 787    jsdouble frac = 0;
 788    bool isLocalTime = JS_FALSE;
 789    size_t tzHour = 0;
 790    size_t tzMin = 0;
 791
 792#define PEEK(ch) (i < limit && s[i] == ch)
 793
 794#define NEED(ch)                                                     \
 795    JS_BEGIN_MACRO                                                   \
 796        if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
 797    JS_END_MACRO
 798
 799#define DONE_DATE_UNLESS(ch)                                            \
 800    JS_BEGIN_MACRO                                                      \
 801        if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
 802    JS_END_MACRO
 803
 804#define DONE_UNLESS(ch)                                            \
 805    JS_BEGIN_MACRO                                                 \
 806        if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
 807    JS_END_MACRO
 808
 809#define NEED_NDIGITS(n, field)                                      \
 810    JS_BEGIN_MACRO                                                  \
 811        if (!ndigits(n, &field, s, &i, limit)) { goto syntax; }     \
 812    JS_END_MACRO
 813
 814    s = str->chars();
 815    limit = str->length();
 816
 817    if (PEEK('+') || PEEK('-')) {
 818        if (PEEK('-'))
 819            dateMul = -1;
 820        ++i;
 821        NEED_NDIGITS(6, year);
 822    } else if (!PEEK('T')) {
 823        NEED_NDIGITS(4, year);
 824    }
 825    DONE_DATE_UNLESS('-');
 826    NEED_NDIGITS(2, month);
 827    DONE_DATE_UNLESS('-');
 828    NEED_NDIGITS(2, day);
 829
 830 done_date:
 831    DONE_UNLESS('T');
 832    NEED_NDIGITS(2, hour);
 833    NEED(':');
 834    NEED_NDIGITS(2, min);
 835
 836    if (PEEK(':')) {
 837        ++i;
 838        NEED_NDIGITS(2, sec);
 839        if (PEEK('.')) {
 840            ++i;
 841            if (!fractional(&frac, s, &i, limit))
 842                goto syntax;
 843        }
 844    }
 845
 846    if (PEEK('Z')) {
 847        ++i;
 848    } else if (PEEK('+') || PEEK('-')) {
 849        if (PEEK('-'))
 850            tzMul = -1;
 851        ++i;
 852        NEED_NDIGITS(2, tzHour);
 853        /*
 854         * Non-standard extension to the ISO date format (permitted by ES5):
 855         * allow "-0700" as a time zone offset, not just "-07:00".
 856         */
 857        if (PEEK(':'))
 858          ++i;
 859        NEED_NDIGITS(2, tzMin);
 860    } else {
 861        isLocalTime = JS_TRUE;
 862    }
 863
 864 done:
 865    if (year > 275943 // ceil(1e8/365) + 1970
 866        || (month == 0 || month > 12)
 867        || (day == 0 || day > size_t(DaysInMonth(year,month)))
 868        || hour > 24
 869        || ((hour == 24) && (min > 0 || sec > 0))
 870        || min > 59
 871        || sec > 59
 872        || tzHour > 23
 873        || tzMin > 59)
 874        goto syntax;
 875
 876    if (i != limit)
 877        goto syntax;
 878
 879    month -= 1; /* convert month to 0-based */
 880
 881    msec = date_msecFromDate(dateMul * (jsdouble)year, month, day,
 882                             hour, min, sec,
 883                             frac * 1000.0);;
 884
 885    if (isLocalTime) {
 886        msec = UTC(msec, cx);
 887    } else {
 888        msec -= ((tzMul) * ((tzHour * msPerHour)
 889                            + (tzMin * msPerMinute)));
 890    }
 891
 892    if (msec < -8.64e15 || msec > 8.64e15)
 893        goto syntax;
 894
 895    *result = msec;
 896
 897    return JS_TRUE;
 898
 899 syntax:
 900    /* syntax error */
 901    *result = 0;
 902    return JS_FALSE;
 903
 904#undef PEEK
 905#undef NEED
 906#undef DONE_UNLESS
 907#undef NEED_NDIGITS
 908}
 909
 910static JSBool
 911date_parseString(JSLinearString *str, jsdouble *result, JSContext *cx)
 912{
 913    jsdouble msec;
 914
 915    const jschar *s;
 916    size_t limit;
 917    size_t i = 0;
 918    int year = -1;
 919    int mon = -1;
 920    int mday = -1;
 921    int hour = -1;
 922    int min = -1;
 923    int sec = -1;
 924    int c = -1;
 925    int n = -1;
 926    int tzoffset = -1;
 927    int prevc = 0;
 928    JSBool seenplusminus = JS_FALSE;
 929    int temp;
 930    JSBool seenmonthname = JS_FALSE;
 931
 932    if (date_parseISOString(str, result, cx))
 933        return JS_TRUE;
 934
 935    s = str->chars();
 936    limit = str->length();
 937    if (limit == 0)
 938        goto syntax;
 939    while (i < limit) {
 940        c = s[i];
 941        i++;
 942        if (c <= ' ' || c == ',' || c == '-') {
 943            if (c == '-' && '0' <= s[i] && s[i] <= '9') {
 944              prevc = c;
 945            }
 946            continue;
 947        }
 948        if (c == '(') { /* comments) */
 949            int depth = 1;
 950            while (i < limit) {
 951                c = s[i];
 952                i++;
 953                if (c == '(') depth++;
 954                else if (c == ')')
 955                    if (--depth <= 0)
 956                        break;
 957            }
 958            continue;
 959        }
 960        if ('0' <= c && c <= '9') {
 961            n = c - '0';
 962            while (i < limit && '0' <= (c = s[i]) && c <= '9') {
 963                n = n * 10 + c - '0';
 964                i++;
 965            }
 966
 967            /* allow TZA before the year, so
 968             * 'Wed Nov 05 21:49:11 GMT-0800 1997'
 969             * works */
 970
 971            /* uses of seenplusminus allow : in TZA, so Java
 972             * no-timezone style of GMT+4:30 works
 973             */
 974
 975            if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
 976                /* make ':' case below change tzoffset */
 977                seenplusminus = JS_TRUE;
 978
 979                /* offset */
 980                if (n < 24)
 981                    n = n * 60; /* EG. "GMT-3" */
 982                else
 983                    n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
 984                if (prevc == '+')       /* plus means east of GMT */
 985                    n = -n;
 986                if (tzoffset != 0 && tzoffset != -1)
 987                    goto syntax;
 988                tzoffset = n;
 989            } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
 990                if (c <= ' ' || c == ',' || c == '/' || i >= limit)
 991                    year = n;
 992                else
 993                    goto syntax;
 994            } else if (c == ':') {
 995                if (hour < 0)
 996                    hour = /*byte*/ n;
 997                else if (min < 0)
 998                    min = /*byte*/ n;
 999                else
1000                    goto syntax;
1001            } else if (c == '/') {
1002                /* until it is determined that mon is the actual
1003                   month, keep it as 1-based rather than 0-based */
1004                if (mon < 0)
1005                    mon = /*byte*/ n;
1006                else if (mday < 0)
1007                    mday = /*byte*/ n;
1008                else
1009                    goto syntax;
1010            } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
1011                goto syntax;
1012            } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */
1013                if (tzoffset < 0)
1014                    tzoffset -= n;
1015                else
1016                    tzoffset += n;
1017            } else if (hour >= 0 && min < 0) {
1018                min = /*byte*/ n;
1019            } else if (prevc == ':' && min >= 0 && sec < 0) {
1020                sec = /*byte*/ n;
1021            } else if (mon < 0) {
1022                mon = /*byte*/n;
1023            } else if (mon >= 0 && mday < 0) {
1024                mday = /*byte*/ n;
1025            } else if (mon >= 0 && mday >= 0 && year < 0) {
1026                year = n;
1027            } else {
1028                goto syntax;
1029            }
1030            prevc = 0;
1031        } else if (c == '/' || c == ':' || c == '+' || c == '-') {
1032            prevc = c;
1033        } else {
1034            size_t st = i - 1;
1035            int k;
1036            while (i < limit) {
1037                c = s[i];
1038                if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
1039                    break;
1040                i++;
1041            }
1042            if (i <= st + 1)
1043                goto syntax;
1044            for (k = ArrayLength(wtb); --k >= 0;)
1045                if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
1046                    int action = ttb[k];
1047                    if (action != 0) {
1048                        if (action < 0) {
1049                            /*
1050                             * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
1051                             * 12:30, instead of blindly adding 12 if PM.
1052                             */
1053                            JS_ASSERT(action == -1 || action == -2);
1054                            if (hour > 12 || hour < 0) {
1055                                goto syntax;
1056                            } else {
1057                                if (action == -1 && hour == 12) { /* am */
1058                                    hour = 0;
1059                                } else if (action == -2 && hour != 12) { /* pm */
1060                                    hour += 12;
1061                                }
1062                            }
1063                        } else if (action <= 13) { /* month! */
1064                            /* Adjust mon to be 1-based until the final values
1065                               for mon, mday and year are adjusted below */
1066                            if (seenmonthname) {
1067                                goto syntax;
1068                            }
1069                            seenmonthname = JS_TRUE;
1070                            temp = /*byte*/ (action - 2) + 1;
1071
1072                            if (mon < 0) {
1073                                mon = temp;
1074                            } else if (mday < 0) {
1075                                mday = mon;
1076                                mon = temp;
1077                            } else if (year < 0) {
1078                                year = mon;
1079                                mon = temp;
1080                            } else {
1081                                goto syntax;
1082                            }
1083                        } else {
1084                            tzoffset = action - 10000;
1085                        }
1086                    }
1087                    break;
1088                }
1089            if (k < 0)
1090                goto syntax;
1091            prevc = 0;
1092        }
1093    }
1094    if (year < 0 || mon < 0 || mday < 0)
1095        goto syntax;
1096    /*
1097      Case 1. The input string contains an English month name.
1098              The form of the string can be month f l, or f month l, or
1099              f l month which each evaluate to the same date.
1100              If f and l are both greater than or equal to 70, or
1101              both less than 70, the date is invalid.
1102              The year is taken to be the greater of the values f, l.
1103              If the year is greater than or equal to 70 and less than 100,
1104              it is considered to be the number of years after 1900.
1105      Case 2. The input string is of the form "f/m/l" where f, m and l are
1106              integers, e.g. 7/16/45.
1107              Adjust the mon, mday and year values to achieve 100% MSIE
1108              compatibility.
1109              a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
1110                 i.  If year < 100, it is the number of years after 1900
1111                 ii. If year >= 100, it is the number of years after 0.
1112              b. If 70 <= f < 100
1113                 i.  If m < 70, f/m/l is interpreted as
1114                     year/month/day where year is the number of years after
1115                     1900.
1116                 ii. If m >= 70, the date is invalid.
1117              c. If f >= 100
1118                 i.  If m < 70, f/m/l is interpreted as
1119                     year/month/day where year is the number of years after 0.
1120                 ii. If m >= 70, the date is invalid.
1121    */
1122    if (seenmonthname) {
1123        if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
1124            goto syntax;
1125        }
1126        if (mday > year) {
1127            temp = year;
1128            year = mday;
1129            mday = temp;
1130        }
1131        if (year >= 70 && year < 100) {
1132            year += 1900;
1133        }
1134    } else if (mon < 70) { /* (a) month/day/year */
1135        if (year < 100) {
1136            year += 1900;
1137        }
1138    } else if (mon < 100) { /* (b) year/month/day */
1139        if (mday < 70) {
1140            temp = year;
1141            year = mon + 1900;
1142            mon = mday;
1143            mday = temp;
1144        } else {
1145            goto syntax;
1146        }
1147    } else { /* (c) year/month/day */
1148        if (mday < 70) {
1149            temp = year;
1150            year = mon;
1151            mon = mday;
1152            mday = temp;
1153        } else {
1154            goto syntax;
1155        }
1156    }
1157    mon -= 1; /* convert month to 0-based */
1158    if (sec < 0)
1159        sec = 0;
1160    if (min < 0)
1161        min = 0;
1162    if (hour < 0)
1163        hour = 0;
1164
1165    msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
1166
1167    if (tzoffset == -1) { /* no time zone specified, have to use local */
1168        msec = UTC(msec, cx);
1169    } else {
1170        msec += tzoffset * msPerMinute;
1171    }
1172
1173    *result = msec;
1174    return JS_TRUE;
1175
1176syntax:
1177    /* syntax error */
1178    *result = 0;
1179    return JS_FALSE;
1180}
1181
1182static JSBool
1183date_parse(JSContext *cx, uintN argc, Value *vp)
1184{
1185    JSString *str;
1186    jsdouble result;
1187
1188    if (argc == 0) {
1189        vp->setDouble(js_NaN);
1190        return true;
1191    }
1192    str = ToString(cx, vp[2]);
1193    if (!str)
1194        return JS_FALSE;
1195    vp[2].setString(str);
1196    JSLinearString *linearStr = str->ensureLinear(cx);
1197    if (!linearStr)
1198        return false;
1199
1200    if (!date_parseString(linearStr, &result, cx)) {
1201        vp->setDouble(js_NaN);
1202        return true;
1203    }
1204
1205    result = TIMECLIP(result);
1206    vp->setNumber(result);
1207    return true;
1208}
1209
1210static inline jsdouble
1211NowAsMillis()
1212{
1213    return (jsdouble) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
1214}
1215
1216static JSBool
1217date_now(JSContext *cx, uintN argc, Value *vp)
1218{
1219    vp->setDouble(NowAsMillis());
1220    return JS_TRUE;
1221}
1222
1223/*
1224 * Set UTC time to a given time and invalidate cached local time.
1225 */
1226static JSBool
1227SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, Value *vp = NULL)
1228{
1229    JS_ASSERT(obj->isDate());
1230
1231    for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
1232         ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
1233         ind++) {
1234        obj->setSlot(ind, UndefinedValue());
1235    }
1236
1237    obj->setDateUTCTime(DoubleValue(t));
1238    if (vp)
1239        vp->setDouble(t);
1240    return true;
1241}
1242
1243static void
1244SetDateToNaN(JSContext *cx, JSObject *obj, Value *vp = NULL)
1245{
1246    jsdouble NaN = cx->runtime->NaNValue.getDoubleRef();
1247    SetUTCTime(cx, obj, NaN, vp);
1248}
1249
1250/*
1251 * Cache the local time, year, month, and so forth of the object.
1252 * If UTC time is not finite (e.g., NaN), the local time
1253 * slots will be set to the UTC time without conversion.
1254 */
1255static bool
1256FillLocalTimes(JSContext *cx, JSObject *obj)
1257{
1258    JS_ASSERT(obj->isDate());
1259
1260    jsdouble utcTime = obj->getDateUTCTime().toNumber();
1261
1262    if (!JSDOUBLE_IS_FINITE(utcTime)) {
1263        for (size_t ind = JSObject::JSSLOT_DATE_COMPONENTS_START;
1264             ind < JSObject::DATE_CLASS_RESERVED_SLOTS;
1265             ind++) {
1266            obj->setSlot(ind, DoubleValue(utcTime));
1267        }
1268        return true;
1269    }
1270
1271    jsdouble localTime = LocalTime(utcTime, cx);
1272
1273    obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_TIME, DoubleValue(localTime));
1274
1275    jsint year = (jsint) floor(localTime /(msPerDay*365.2425)) + 1970;
1276    jsdouble yearStartTime = (jsdouble) TimeFromYear(year);
1277
1278    /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
1279    jsint yearDays;
1280    if (yearStartTime > localTime) {
1281        year--;
1282        yearStartTime -= (msPerDay * DaysInYear(year));
1283        yearDays = DaysInYear(year);
1284    } else {
1285        yearDays = DaysInYear(year);
1286        jsdouble nextStart = yearStartTime + (msPerDay * yearDays);
1287        if (nextStart <= localTime) {
1288            year++;
1289            yearStartTime = nextStart;
1290            yearDays = DaysInYear(year);
1291        }
1292    }
1293
1294    obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR, Int32Value(year));
1295
1296    uint64_t yearTime = uint64_t(localTime - yearStartTime);
1297    jsint yearSeconds = uint32_t(yearTime / 1000);
1298
1299    jsint day = yearSeconds / jsint(SecondsPerDay);
1300
1301    jsint step = -1, next = 30;
1302    jsint month;
1303
1304    do {
1305        if (day <= next) {
1306            month = 0;
1307            break;
1308        }
1309        step = next;
1310        next += ((yearDays == 366) ? 29 : 28);
1311        if (day <= next) {
1312            month = 1;
1313            break;
1314        }
1315        step = next;
1316        if (day <= (next += 31)) {
1317            month = 2;
1318            break;
1319        }
1320        step = next;
1321        if (day <= (next += 30)) {
1322            month = 3;
1323            break;
1324        }
1325        step = next;
1326        if (day <= (next += 31)) {
1327            month = 4;
1328            break;
1329        }
1330        step = next;
1331        if (day <= (next += 30)) {
1332            month = 5;
1333            break;
1334        }
1335        step = next;
1336        if (day <= (next += 31)) {
1337            month = 6;
1338            break;
1339        }
1340        step = next;
1341        if (day <= (next += 31)) {
1342            month = 7;
1343            break;
1344        }
1345        step = next;
1346        if (day <= (next += 30)) {
1347            month = 8;
1348            break;
1349        }
1350        step = next;
1351        if (day <= (next += 31)) {
1352            month = 9;
1353            break;
1354        }
1355        step = next;
1356        if (day <= (next += 30)) {
1357            month = 10;
1358            break;
1359        }
1360        step = next;
1361        month = 11;
1362    } while (0);
1363
1364    obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH, Int32Value(month));
1365    obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DATE, Int32Value(day - step));
1366
1367    jsint weekday = WeekDay(localTime);
1368
1369    obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_DAY, Int32Value(weekday));
1370
1371    jsint seconds = yearSeconds % 60;
1372
1373    obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS, Int32Value(seconds));
1374
1375    jsint minutes = (yearSeconds / 60) % 60;
1376
1377    obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES, Int32Value(minutes));
1378
1379    jsint hours = (yearSeconds / (60 * 60)) % 24;
1380
1381    obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS, Int32Value(hours));
1382
1383    return true;
1384}
1385
1386/* Cache the local times in obj, if necessary. */
1387static inline bool
1388GetAndCacheLocalTime(JSContext *cx, JSObject *obj)
1389{
1390    JS_ASSERT(obj->isDate());
1391
1392    /* If the local time is undefined, we need to fill in the cached values. */
1393    if (obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).isUndefined()) {
1394        if (!FillLocalTimes(cx, obj))
1395            return false;
1396    }
1397    return true;
1398}
1399
1400static inline bool
1401GetAndCacheLocalTime(JSContext *cx, JSObject *obj, double *time)
1402{
1403    if (!obj || !GetAndCacheLocalTime(cx, obj))
1404        return false;
1405
1406    *time = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_TIME).toDouble();
1407    return true;
1408}
1409
1410/*
1411 * See ECMA 15.9.5.4 thru 15.9.5.23
1412 */
1413static JSBool
1414date_getTime(JSContext *cx, uintN argc, Value *vp)
1415{
1416    CallArgs args = CallArgsFromVp(argc, vp);
1417
1418    bool ok;
1419    JSObject *obj = NonGenericMethodGuard(cx, args, date_getTime, &DateClass, &ok);
1420    if (!obj)
1421        return ok;
1422
1423    args.rval() = obj->getDateUTCTime();
1424    return true;
1425}
1426
1427static JSBool
1428date_getYear(JSContext *cx, uintN argc, Value *vp)
1429{
1430    CallArgs args = CallArgsFromVp(argc, vp);
1431
1432    bool ok;
1433    JSObject *obj = NonGenericMethodGuard(cx, args, date_getYear, &DateClass, &ok);
1434    if (!obj)
1435        return ok;
1436
1437    if (!GetAndCacheLocalTime(cx, obj))
1438        return false;
1439
1440    Value yearVal = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
1441    if (yearVal.isInt32()) {
1442        /* Follow ECMA-262 to the letter, contrary to IE JScript. */
1443        jsint year = yearVal.toInt32() - 1900;
1444        args.rval().setInt32(year);
1445    } else {
1446        args.rval() = yearVal;
1447    }
1448
1449    return true;
1450}
1451
1452static JSBool
1453date_getFullYear(JSContext *cx, uintN argc, Value *vp)
1454{
1455    CallArgs args = CallArgsFromVp(argc, vp);
1456
1457    bool ok;
1458    JSObject *obj = NonGenericMethodGuard(cx, args, date_getFullYear, &DateClass, &ok);
1459    if (!obj)
1460        return ok;
1461
1462    if (!GetAndCacheLocalTime(cx, obj))
1463        return false;
1464
1465    args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_YEAR);
1466    return true;
1467}
1468
1469static JSBool
1470date_getUTCFullYear(JSContext *cx, uintN argc, Value *vp)
1471{
1472    CallArgs args = CallArgsFromVp(argc, vp);
1473
1474    bool ok;
1475    JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCFullYear, &DateClass, &ok);
1476    if (!obj)
1477        return ok;
1478
1479    double result = obj->getDateUTCTime().toNumber();
1480    if (JSDOUBLE_IS_FINITE(result))
1481        result = YearFromTime(result);
1482
1483    args.rval().setNumber(result);
1484    return true;
1485}
1486
1487static JSBool
1488date_getMonth(JSContext *cx, uintN argc, Value *vp)
1489{
1490    CallArgs args = CallArgsFromVp(argc, vp);
1491
1492    bool ok;
1493    JSObject *obj = NonGenericMethodGuard(cx, args, date_getMonth, &DateClass, &ok);
1494    if (!obj)
1495        return ok;
1496
1497    if (!GetAndCacheLocalTime(cx, obj))
1498        return false;
1499
1500    args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MONTH);
1501    return true;
1502}
1503
1504static JSBool
1505date_getUTCMonth(JSContext *cx, uintN argc, Value *vp)
1506{
1507    CallArgs args = CallArgsFromVp(argc, vp);
1508
1509    bool ok;
1510    JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMonth, &DateClass, &ok);
1511    if (!obj)
1512        return ok;
1513
1514    double result = obj->getDateUTCTime().toNumber();
1515    if (JSDOUBLE_IS_FINITE(result))
1516        result = MonthFromTime(result);
1517
1518    args.rval().setNumber(result);
1519    return true;
1520}
1521
1522static JSBool
1523date_getDate(JSContext *cx, uintN argc, Value *vp)
1524{
1525    CallArgs args = CallArgsFromVp(argc, vp);
1526
1527    bool ok;
1528    JSObject *obj = NonGenericMethodGuard(cx, args, date_getDate, &DateClass, &ok);
1529    if (!obj)
1530        return ok;
1531
1532    if (!GetAndCacheLocalTime(cx, obj))
1533        return false;
1534
1535    args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DATE);
1536    return true;
1537}
1538
1539static JSBool
1540date_getUTCDate(JSContext *cx, uintN argc, Value *vp)
1541{
1542    CallArgs args = CallArgsFromVp(argc, vp);
1543
1544    bool ok;
1545    JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCDate, &DateClass, &ok);
1546    if (!obj)
1547        return ok;
1548
1549    double result = obj->getDateUTCTime().toNumber();
1550    if (JSDOUBLE_IS_FINITE(result))
1551        result = DateFromTime(result);
1552
1553    args.rval().setNumber(result);
1554    return true;
1555}
1556
1557static JSBool
1558date_getDay(JSContext *cx, uintN argc, Value *vp)
1559{
1560    CallArgs args = CallArgsFromVp(argc, vp);
1561
1562    bool ok;
1563    JSObject *obj = NonGenericMethodGuard(cx, args, date_getDay, &DateClass, &ok);
1564    if (!obj)
1565        return ok;
1566
1567    if (!GetAndCacheLocalTime(cx, obj))
1568        return false;
1569
1570    args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_DAY);
1571    return true;
1572}
1573
1574static JSBool
1575date_getUTCDay(JSContext *cx, uintN argc, Value *vp)
1576{
1577    CallArgs args = CallArgsFromVp(argc, vp);
1578
1579    bool ok;
1580    JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCDay, &DateClass, &ok);
1581    if (!obj)
1582        return ok;
1583
1584    jsdouble result = obj->getDateUTCTime().toNumber();
1585    if (JSDOUBLE_IS_FINITE(result))
1586        result = WeekDay(result);
1587
1588    args.rval().setNumber(result);
1589    return true;
1590}
1591
1592static JSBool
1593date_getHours(JSContext *cx, uintN argc, Value *vp)
1594{
1595    CallArgs args = CallArgsFromVp(argc, vp);
1596
1597    bool ok;
1598    JSObject *obj = NonGenericMethodGuard(cx, args, date_getHours, &DateClass, &ok);
1599    if (!obj)
1600        return ok;
1601
1602    if (!GetAndCacheLocalTime(cx, obj))
1603        return false;
1604
1605    args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_HOURS);
1606    return true;
1607}
1608
1609static JSBool
1610date_getUTCHours(JSContext *cx, uintN argc, Value *vp)
1611{
1612    CallArgs args = CallArgsFromVp(argc, vp);
1613
1614    bool ok;
1615    JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCHours, &DateClass, &ok);
1616    if (!obj)
1617        return ok;
1618
1619    double result = obj->getDateUTCTime().toNumber();
1620    if (JSDOUBLE_IS_FINITE(result))
1621        result = HourFromTime(result);
1622
1623    args.rval().setNumber(result);
1624    return JS_TRUE;
1625}
1626
1627static JSBool
1628date_getMinutes(JSContext *cx, uintN argc, Value *vp)
1629{
1630    CallArgs args = CallArgsFromVp(argc, vp);
1631
1632    bool ok;
1633    JSObject *obj = NonGenericMethodGuard(cx, args, date_getMinutes, &DateClass, &ok);
1634    if (!obj)
1635        return ok;
1636
1637    if (!GetAndCacheLocalTime(cx, obj))
1638        return false;
1639
1640    args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_MINUTES);
1641    return true;
1642}
1643
1644static JSBool
1645date_getUTCMinutes(JSContext *cx, uintN argc, Value *vp)
1646{
1647    CallArgs args = CallArgsFromVp(argc, vp);
1648
1649    bool ok;
1650    JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMinutes, &DateClass, &ok);
1651    if (!obj)
1652        return ok;
1653
1654    double result = obj->getDateUTCTime().toNumber();
1655    if (JSDOUBLE_IS_FINITE(result))
1656        result = MinFromTime(result);
1657
1658    args.rval().setNumber(result);
1659    return true;
1660}
1661
1662/* Date.getSeconds is mapped to getUTCSeconds */
1663
1664static JSBool
1665date_getUTCSeconds(JSContext *cx, uintN argc, Value *vp)
1666{
1667    CallArgs args = CallArgsFromVp(argc, vp);
1668
1669    bool ok;
1670    JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCSeconds, &DateClass, &ok);
1671    if (!obj)
1672        return ok;
1673
1674    if (!GetAndCacheLocalTime(cx, obj))
1675        return false;
1676
1677    args.rval() = obj->getSlot(JSObject::JSSLOT_DATE_LOCAL_SECONDS);
1678    return true;
1679}
1680
1681/* Date.getMilliseconds is mapped to getUTCMilliseconds */
1682
1683static JSBool
1684date_getUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
1685{
1686    CallArgs args = CallArgsFromVp(argc, vp);
1687
1688    bool ok;
1689    JSObject *obj = NonGenericMethodGuard(cx, args, date_getUTCMilliseconds, &DateClass, &ok);
1690    if (!obj)
1691        return ok;
1692
1693    double result = obj->getDateUTCTime().toNumber();
1694    if (JSDOUBLE_IS_FINITE(result))
1695        result = msFromTime(result);
1696
1697    args.rval().setNumber(result);
1698    return true;
1699}
1700
1701static JSBool
1702date_getTimezoneOffset(JSContext *cx, uintN argc, Value *vp)
1703{
1704    CallArgs args = CallArgsFromVp(argc, vp);
1705
1706    bool ok;
1707    JSObject *obj = NonGenericMethodGuard(cx, args, date_getTimezoneOffset, &DateClass, &ok);
1708    if (!obj)
1709        return ok;
1710
1711    double utctime = obj->getDateUTCTime().toNumber();
1712
1713    double localtime;
1714    if (!GetAndCacheLocalTime(cx, obj, &localtime))
1715        return false;
1716
1717    /*
1718     * Return the time zone offset in minutes for the current locale that is
1719     * appropriate for this time. This value would be a constant except for
1720     * daylight savings time.
1721     */
1722    double result = (utctime - localtime) / msPerMinute;
1723    args.rval().setNumber(result);
1724    return true;
1725}
1726
1727static JSBool
1728date_setTime(JSContext *cx, uintN argc, Value *vp)
1729{
1730    CallArgs args = CallArgsFromVp(argc, vp);
1731
1732    bool ok;
1733    JSObject *obj = NonGenericMethodGuard(cx, args, date_setTime, &DateClass, &ok);
1734    if (!obj)
1735        return ok;
1736
1737    if (args.length() == 0) {
1738        SetDateToNaN(cx, obj, &args.rval());
1739        return true;
1740    }
1741
1742    jsdouble result;
1743    if (!ToNumber(cx, args[0], &result))
1744        return false;
1745
1746    return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
1747}
1748
1749static JSBool
1750date_makeTime(JSContext *cx, Native native, uintN maxargs, JSBool local, uintN argc, Value *vp)
1751{
1752    CallArgs args = CallArgsFromVp(argc, vp);
1753
1754    bool ok;
1755    JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
1756    if (!obj)
1757        return ok;
1758
1759    double result = obj->getDateUTCTime().toNumber();
1760
1761    /* just return NaN if the date is already NaN */
1762    if (!JSDOUBLE_IS_FINITE(result)) {
1763        args.rval().setNumber(result);
1764        return true;
1765    }
1766
1767    /*
1768     * Satisfy the ECMA rule that if a function is called with
1769     * fewer arguments than the specified formal arguments, the
1770     * remaining arguments are set to undefined.  Seems like all
1771     * the Date.setWhatever functions in ECMA are only varargs
1772     * beyond the first argument; this should be set to undefined
1773     * if it's not given.  This means that "d = new Date();
1774     * d.setMilliseconds()" returns NaN.  Blech.
1775     */
1776    if (args.length() == 0) {
1777        SetDateToNaN(cx, obj, &args.rval());
1778        return true;
1779    }
1780
1781    uintN numNums = Min(args.length(), maxargs);
1782    JS_ASSERT(numNums <= 4);
1783    double nums[4];
1784    for (uintN i = 0; i < numNums; i++) {
1785        if (!ToNumber(cx, args[i], &nums[i]))
1786            return false;
1787        if (!JSDOUBLE_IS_FINITE(nums[i])) {
1788            SetDateToNaN(cx, obj, &args.rval());
1789            return true;
1790        }
1791        nums[i] = js_DoubleToInteger(nums[i]);
1792    }
1793
1794    double lorutime;  /* Local or UTC version of *date */
1795    if (local)
1796        lorutime = LocalTime(result, cx);
1797    else
1798        lorutime = result;
1799
1800    double *argp = nums;
1801    double *stop = argp + numNums;
1802    double hour;
1803    if (maxargs >= 4 && argp < stop)
1804        hour = *argp++;
1805    else
1806        hour = HourFromTime(lorutime);
1807
1808    double min;
1809    if (maxargs >= 3 && argp < stop)
1810        min = *argp++;
1811    else
1812        min = MinFromTime(lorutime);
1813
1814    double sec;
1815    if (maxargs >= 2 && argp < stop)
1816        sec = *argp++;
1817    else
1818        sec = SecFromTime(lorutime);
1819
1820    double msec;
1821    if (maxargs >= 1 && argp < stop)
1822        msec = *argp;
1823    else
1824        msec = msFromTime(lorutime);
1825
1826    double msec_time = MakeTime(hour, min, sec, msec);
1827    result = MakeDate(Day(lorutime), msec_time);
1828
1829    if (local)
1830        result = UTC(result, cx);
1831
1832    return SetUTCTime(cx, obj, TIMECLIP(result), &ar

Large files files are truncated, but you can click here to view the full file