PageRenderTime 175ms CodeModel.GetById 3ms app.highlight 154ms RepoModel.GetById 2ms app.codeStats 0ms

/js/src/jsdate.cpp

http://github.com/zpao/v8monkey
C++ | 2820 lines | 2011 code | 393 blank | 416 comment | 507 complexity | d888822aeeaa26b4c5976840971fb528 MD5 | raw 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), &args.rval());
1833}
1834
1835static JSBool
1836date_setMilliseconds(JSContext *cx, uintN argc, Value *vp)
1837{
1838    return date_makeTime(cx, date_setMilliseconds, 1, JS_TRUE, argc, vp);
1839}
1840
1841static JSBool
1842date_setUTCMilliseconds(JSContext *cx, uintN argc, Value *vp)
1843{
1844    return date_makeTime(cx, date_setUTCMilliseconds, 1, JS_FALSE, argc, vp);
1845}
1846
1847static JSBool
1848date_setSeconds(JSContext *cx, uintN argc, Value *vp)
1849{
1850    return date_makeTime(cx, date_setSeconds, 2, JS_TRUE, argc, vp);
1851}
1852
1853static JSBool
1854date_setUTCSeconds(JSContext *cx, uintN argc, Value *vp)
1855{
1856    return date_makeTime(cx, date_setUTCSeconds, 2, JS_FALSE, argc, vp);
1857}
1858
1859static JSBool
1860date_setMinutes(JSContext *cx, uintN argc, Value *vp)
1861{
1862    return date_makeTime(cx, date_setMinutes, 3, JS_TRUE, argc, vp);
1863}
1864
1865static JSBool
1866date_setUTCMinutes(JSContext *cx, uintN argc, Value *vp)
1867{
1868    return date_makeTime(cx, date_setUTCMinutes, 3, JS_FALSE, argc, vp);
1869}
1870
1871static JSBool
1872date_setHours(JSContext *cx, uintN argc, Value *vp)
1873{
1874    return date_makeTime(cx, date_setHours, 4, JS_TRUE, argc, vp);
1875}
1876
1877static JSBool
1878date_setUTCHours(JSContext *cx, uintN argc, Value *vp)
1879{
1880    return date_makeTime(cx, date_setUTCHours, 4, JS_FALSE, argc, vp);
1881}
1882
1883static JSBool
1884date_makeDate(JSContext *cx, Native native, uintN maxargs, JSBool local, uintN argc, Value *vp)
1885{
1886    CallArgs args = CallArgsFromVp(argc, vp);
1887
1888    bool ok;
1889    JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
1890    if (!obj)
1891        return ok;
1892
1893    double result = obj->getDateUTCTime().toNumber();
1894
1895    /* see complaint about ECMA in date_MakeTime */
1896    if (args.length() == 0) {
1897        SetDateToNaN(cx, obj, &args.rval());
1898        return true;
1899    }
1900
1901    uintN numNums = Min(args.length(), maxargs);
1902    JS_ASSERT(1 <= numNums && numNums <= 3);
1903    double nums[3];
1904    for (uintN i = 0; i < numNums; i++) {
1905        if (!ToNumber(cx, args[i], &nums[i]))
1906            return JS_FALSE;
1907        if (!JSDOUBLE_IS_FINITE(nums[i])) {
1908            SetDateToNaN(cx, obj, &args.rval());
1909            return true;
1910        }
1911        nums[i] = js_DoubleToInteger(nums[i]);
1912    }
1913
1914    /*
1915     * return NaN if date is NaN and we're not setting the year, If we are, use
1916     * 0 as the time.
1917     */
1918    double lorutime; /* local or UTC version of *date */
1919    if (!(JSDOUBLE_IS_FINITE(result))) {
1920        if (maxargs < 3) {
1921            args.rval().setDouble(result);
1922            return true;
1923        }
1924        lorutime = +0.;
1925    } else {
1926        lorutime = local ? LocalTime(result, cx) : result;
1927    }
1928
1929    double *argp = nums;
1930    double *stop = argp + numNums;
1931    double year;
1932    if (maxargs >= 3 && argp < stop)
1933        year = *argp++;
1934    else
1935        year = YearFromTime(lorutime);
1936
1937    double month;
1938    if (maxargs >= 2 && argp < stop)
1939        month = *argp++;
1940    else
1941        month = MonthFromTime(lorutime);
1942
1943    double day;
1944    if (maxargs >= 1 && argp < stop)
1945        day = *argp++;
1946    else
1947        day = DateFromTime(lorutime);
1948
1949    day = MakeDay(year, month, day); /* day within year */
1950    result = MakeDate(day, TimeWithinDay(lorutime));
1951
1952    if (local)
1953        result = UTC(result, cx);
1954
1955    return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
1956}
1957
1958static JSBool
1959date_setDate(JSContext *cx, uintN argc, Value *vp)
1960{
1961    return date_makeDate(cx, date_setDate, 1, JS_TRUE, argc, vp);
1962}
1963
1964static JSBool
1965date_setUTCDate(JSContext *cx, uintN argc, Value *vp)
1966{
1967    return date_makeDate(cx, date_setUTCDate, 1, JS_FALSE, argc, vp);
1968}
1969
1970static JSBool
1971date_setMonth(JSContext *cx, uintN argc, Value *vp)
1972{
1973    return date_makeDate(cx, date_setMonth, 2, JS_TRUE, argc, vp);
1974}
1975
1976static JSBool
1977date_setUTCMonth(JSContext *cx, uintN argc, Value *vp)
1978{
1979    return date_makeDate(cx, date_setUTCMonth, 2, JS_FALSE, argc, vp);
1980}
1981
1982static JSBool
1983date_setFullYear(JSContext *cx, uintN argc, Value *vp)
1984{
1985    return date_makeDate(cx, date_setFullYear, 3, JS_TRUE, argc, vp);
1986}
1987
1988static JSBool
1989date_setUTCFullYear(JSContext *cx, uintN argc, Value *vp)
1990{
1991    return date_makeDate(cx, date_setUTCFullYear, 3, JS_FALSE, argc, vp);
1992}
1993
1994static JSBool
1995date_setYear(JSContext *cx, uintN argc, Value *vp)
1996{
1997    CallArgs args = CallArgsFromVp(argc, vp);
1998
1999    bool ok;
2000    JSObject *obj = NonGenericMethodGuard(cx, args, date_setYear, &DateClass, &ok);
2001    if (!obj)
2002        return ok;
2003
2004    if (args.length() == 0) {
2005        /* Call this only after verifying that obj.[[Class]] = "Date". */
2006        SetDateToNaN(cx, obj, &args.rval());
2007        return true;
2008    }
2009
2010    double result = obj->getDateUTCTime().toNumber();
2011
2012    double year;
2013    if (!ToNumber(cx, args[0], &year))
2014        return false;
2015    if (!JSDOUBLE_IS_FINITE(year)) {
2016        SetDateToNaN(cx, obj, &args.rval());
2017        return true;
2018    }
2019    year = js_DoubleToInteger(year);
2020    if (year >= 0 && year <= 99)
2021        year += 1900;
2022
2023    double t = JSDOUBLE_IS_FINITE(result) ? LocalTime(result, cx) : +0.0;
2024    double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
2025    result = MakeDate(day, TimeWithinDay(t));
2026    result = UTC(result, cx);
2027
2028    return SetUTCTime(cx, obj, TIMECLIP(result), &args.rval());
2029}
2030
2031/* constants for toString, toUTCString */
2032static char js_NaN_date_str[] = "Invalid Date";
2033static const char* days[] =
2034{
2035   "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
2036};
2037static const char* months[] =
2038{
2039   "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
2040};
2041
2042
2043// Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2044// requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
2045static void
2046print_gmt_string(char* buf, size_t size, jsdouble utctime)
2047{
2048    JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
2049                days[WeekDay(utctime)],
2050                DateFromTime(utctime),
2051                months[MonthFromTime(utctime)],
2052                YearFromTime(utctime),
2053                HourFromTime(utctime),
2054                MinFromTime(utctime),
2055                SecFromTime(utctime));
2056}
2057
2058static void
2059print_iso_string(char* buf, size_t size, jsdouble utctime)
2060{
2061    JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
2062                YearFromTime(utctime),
2063                MonthFromTime(utctime) + 1,
2064                DateFromTime(utctime),
2065                HourFromTime(utctime),
2066                MinFromTime(utctime),
2067                SecFromTime(utctime),
2068                msFromTime(utctime));
2069}
2070
2071static JSBool
2072date_utc_format(JSContext *cx, Native native, CallArgs args,
2073                void (*printFunc)(char*, size_t, jsdouble))
2074{
2075    bool ok;
2076    JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
2077    if (!obj)
2078        return ok;
2079
2080    double utctime = obj->getDateUTCTime().toNumber();
2081
2082    char buf[100];
2083    if (!JSDOUBLE_IS_FINITE(utctime)) {
2084        if (printFunc == print_iso_string) {
2085            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DATE);
2086            return false;
2087        }
2088
2089        JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2090    } else {
2091        (*printFunc)(buf, sizeof buf, utctime);
2092    }
2093
2094    JSString *str = JS_NewStringCopyZ(cx, buf);
2095    if (!str)
2096        return false;
2097    args.rval().setString(str);
2098    return true;
2099}
2100
2101static JSBool
2102date_toGMTString(JSContext *cx, uintN argc, Value *vp)
2103{
2104    return date_utc_format(cx, date_toGMTString, CallArgsFromVp(argc, vp), print_gmt_string);
2105}
2106
2107static JSBool
2108date_toISOString(JSContext *cx, uintN argc, Value *vp)
2109{
2110    return date_utc_format(cx, date_toISOString, CallArgsFromVp(argc, vp), print_iso_string);
2111}
2112
2113/* ES5 15.9.5.44. */
2114static JSBool
2115date_toJSON(JSContext *cx, uintN argc, Value *vp)
2116{
2117    /* Step 1. */
2118    JSObject *obj = ToObject(cx, &vp[1]);
2119    if (!obj)
2120        return false;
2121
2122    /* Step 2. */
2123    Value tv = ObjectValue(*obj);
2124    if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv))
2125        return false;
2126
2127    /* Step 3. */
2128    if (tv.isDouble() && !JSDOUBLE_IS_FINITE(tv.toDouble())) {
2129        vp->setNull();
2130        return true;
2131    }
2132
2133    /* Step 4. */
2134    Value &toISO = vp[0];
2135    if (!obj->getProperty(cx, cx->runtime->atomState.toISOStringAtom, &toISO))
2136        return false;
2137
2138    /* Step 5. */
2139    if (!js_IsCallable(toISO)) {
2140        JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
2141                                     JSMSG_BAD_TOISOSTRING_PROP);
2142        return false;
2143    }
2144
2145    /* Step 6. */
2146    InvokeArgsGuard args;
2147    if (!cx->stack.pushInvokeArgs(cx, 0, &args))
2148        return false;
2149
2150    args.calleev() = toISO;
2151    args.thisv().setObject(*obj);
2152
2153    if (!Invoke(cx, args))
2154        return false;
2155    *vp = args.rval();
2156    return true;
2157}
2158
2159/* for Date.toLocaleString; interface to PRMJTime date struct.
2160 */
2161static void
2162new_explode(jsdouble timeval, PRMJTime *split, JSContext *cx)
2163{
2164    jsint year = YearFromTime(timeval);
2165
2166    split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
2167    split->tm_sec = int8_t(SecFromTime(timeval));
2168    split->tm_min = int8_t(MinFromTime(timeval));
2169    split->tm_hour = int8_t(HourFromTime(timeval));
2170    split->tm_mday = int8_t(DateFromTime(timeval));
2171    split->tm_mon = int8_t(MonthFromTime(timeval));
2172    split->tm_wday = int8_t(WeekDay(timeval));
2173    split->tm_year = year;
2174    split->tm_yday = int16_t(DayWithinYear(timeval, year));
2175
2176    /* not sure how this affects things, but it doesn't seem
2177       to matter. */
2178    split->tm_isdst = (DaylightSavingTA(timeval, cx) != 0);
2179}
2180
2181typedef enum formatspec {
2182    FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
2183} formatspec;
2184
2185/* helper function */
2186static JSBool
2187date_format(JSContext *cx, jsdouble date, formatspec format, CallReceiver call)
2188{
2189    char buf[100];
2190    JSString *str;
2191    char tzbuf[100];
2192    JSBool usetz;
2193    size_t i, tzlen;
2194    PRMJTime split;
2195
2196    if (!JSDOUBLE_IS_FINITE(date)) {
2197        JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2198    } else {
2199        jsdouble local = LocalTime(date, cx);
2200
2201        /* offset from GMT in minutes.  The offset includes daylight savings,
2202           if it applies. */
2203        jsint minutes = (jsint) floor(AdjustTime(date, cx) / msPerMinute);
2204
2205        /* map 510 minutes to 0830 hours */
2206        intN offset = (minutes / 60) * 100 + minutes % 60;
2207
2208        /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
2209         * printed as 'GMT-0800' rather than as 'PST' to avoid
2210         * operating-system dependence on strftime (which
2211         * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
2212         * PST as 'Pacific Standard Time.'  This way we always know
2213         * what we're getting, and can parse it if we produce it.
2214         * The OS TZA string is included as a comment.
2215         */
2216
2217        /* get a timezone string from the OS to include as a
2218           comment. */
2219        new_explode(date, &split, cx);
2220        if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
2221
2222            /* Decide whether to use the resulting timezone string.
2223             *
2224             * Reject it if it contains any non-ASCII, non-alphanumeric
2225             * characters.  It's then likely in some other character
2226             * encoding, and we probably won't display it correctly.
2227             */
2228            usetz = JS_TRUE;
2229            tzlen = strlen(tzbuf);
2230            if (tzlen > 100) {
2231                usetz = JS_FALSE;
2232            } else {
2233                for (i = 0; i < tzlen; i++) {
2234                    jschar c = tzbuf[i];
2235                    if (c > 127 ||
2236                        !(isalpha(c) || isdigit(c) ||
2237                          c == ' ' || c == '(' || c == ')')) {
2238                        usetz = JS_FALSE;
2239                    }
2240                }
2241            }
2242
2243            /* Also reject it if it's not parenthesized or if it's '()'. */
2244            if (tzbuf[0] != '(' || tzbuf[1] == ')')
2245                usetz = JS_FALSE;
2246        } else
2247            usetz = JS_FALSE;
2248
2249        switch (format) {
2250          case FORMATSPEC_FULL:
2251            /*
2252             * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
2253             * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
2254             */
2255            /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
2256            JS_snprintf(buf, sizeof buf,
2257                        "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
2258                        days[WeekDay(local)],
2259                        months[MonthFromTime(local)],
2260                        DateFromTime(local),
2261                        YearFromTime(local),
2262                        HourFromTime(local),
2263                        MinFromTime(local),
2264                        SecFromTime(local),
2265                        offset,
2266                        usetz ? " " : "",
2267                        usetz ? tzbuf : "");
2268            break;
2269          case FORMATSPEC_DATE:
2270            /* Tue Oct 31 2000 */
2271            JS_snprintf(buf, sizeof buf,
2272                        "%s %s %.2d %.4d",
2273                        days[WeekDay(local)],
2274                        months[MonthFromTime(local)],
2275                        DateFromTime(local),
2276                        YearFromTime(local));
2277            break;
2278          case FORMATSPEC_TIME:
2279            /* 09:41:40 GMT-0800 (PST) */
2280            JS_snprintf(buf, sizeof buf,
2281                        "%.2d:%.2d:%.2d GMT%+.4d%s%s",
2282                        HourFromTime(local),
2283                        MinFromTime(local),
2284                        SecFromTime(local),
2285                        offset,
2286                        usetz ? " " : "",
2287                        usetz ? tzbuf : "");
2288            break;
2289        }
2290    }
2291
2292    str = JS_NewStringCopyZ(cx, buf);
2293    if (!str)
2294        return JS_FALSE;
2295    call.rval().setString(str);
2296    return JS_TRUE;
2297}
2298
2299static bool
2300ToLocaleHelper(JSContext *cx, CallReceiver call, JSObject *obj, const char *format)
2301{
2302    double utctime = obj->getDateUTCTime().toNumber();
2303
2304    char buf[100];
2305    if (!JSDOUBLE_IS_FINITE(utctime)) {
2306        JS_snprintf(buf, sizeof buf, js_NaN_date_str);
2307    } else {
2308        intN result_len;
2309        jsdouble local = LocalTime(utctime, cx);
2310        PRMJTime split;
2311        new_explode(local, &split, cx);
2312
2313        /* let PRMJTime format it.       */
2314        result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
2315
2316        /* If it failed, default to toString. */
2317        if (result_len == 0)
2318            return date_format(cx, utctime, FORMATSPEC_FULL, call);
2319
2320        /* Hacked check against undesired 2-digit year 00/00/00 form. */
2321        if (strcmp(format, "%x") == 0 && result_len >= 6 &&
2322            /* Format %x means use OS settings, which may have 2-digit yr, so
2323               hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
2324            !isdigit(buf[result_len - 3]) &&
2325            isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
2326            /* ...but not if starts with 4-digit year, like 2022/3/11. */
2327            !(isdigit(buf[0]) && isdigit(buf[1]) &&
2328              isdigit(buf[2]) && isdigit(buf[3]))) {
2329            JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
2330                        "%d", js_DateGetYear(cx, obj));
2331        }
2332
2333    }
2334
2335    if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
2336        return cx->localeCallbacks->localeToUnicode(cx, buf, &call.rval());
2337
2338    JSString *str = JS_NewStringCopyZ(cx, buf);
2339    if (!str)
2340        return false;
2341    call.rval().setString(str);
2342    return true;
2343}
2344
2345/*
2346 * NB: Because of NonGenericMethodGuard, the calling native return immediately
2347 * after calling date_toLocaleHelper, even if it returns 'true'.
2348 */
2349static JSBool
2350date_toLocaleHelper(JSContext *cx, uintN argc, Value *vp, Native native, const char *format)
2351{
2352    CallArgs args = CallArgsFromVp(argc, vp);
2353
2354    bool ok;
2355    JSObject *obj = NonGenericMethodGuard(cx, args, native, &DateClass, &ok);
2356    if (!obj)
2357        return ok;
2358
2359    return ToLocaleHelper(cx, args, obj, format);
2360}
2361
2362static JSBool
2363date_toLocaleStringHelper(JSContext *cx, Native native, uintN argc, Value *vp)
2364{
2365    /*
2366     * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
2367     * with msvc; '%#c' requests that a full year be used in the result string.
2368     */
2369    return date_toLocaleHelper(cx, argc, vp, native,
2370#if defined(_WIN32) && !defined(__MWERKS__)
2371                                   "%#c"
2372#else
2373                                   "%c"
2374#endif
2375                               );
2376}
2377
2378static JSBool
2379date_toLocaleString(JSContext *cx, uintN argc, Value *vp)
2380{
2381    return date_toLocaleStringHelper(cx, date_toLocaleString, argc, vp);
2382}
2383
2384static JSBool
2385date_toLocaleDateString(JSContext *cx, uintN argc, Value *vp)
2386{
2387    /*
2388     * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
2389     * with msvc; '%#x' requests that a full year be used in the result string.
2390     */
2391    return date_toLocaleHelper(cx, argc, vp, date_toLocaleDateString,
2392#if defined(_WIN32) && !defined(__MWERKS__)
2393                                   "%#x"
2394#else
2395                                   "%x"
2396#endif
2397                               );
2398}
2399
2400static JSBool
2401date_toLocaleTimeString(JSContext *cx, uintN argc, Value *vp)
2402{
2403    return date_toLocaleHelper(cx, argc, vp, date_toLocaleTimeString, "%X");
2404}
2405
2406static JSBool
2407date_toLocaleFormat(JSContext *cx, uintN argc, Value *vp)
2408{
2409    if (argc == 0)
2410        return date_toLocaleStringHelper(cx, date_toLocaleFormat, argc, vp);
2411
2412    CallArgs args = CallArgsFromVp(argc, vp);
2413
2414    bool ok;
2415    JSObject *obj = NonGenericMethodGuard(cx, args, date_toLocaleFormat, &DateClass, &ok);
2416    if (!obj)
2417        return ok;
2418
2419    JSString *fmt = ToString(cx, args[0]);
2420    if (!fmt)
2421        return false;
2422
2423    args[0].setString(fmt);
2424    JSAutoByteString fmtbytes(cx, fmt);
2425    if (!fmtbytes)
2426        return false;
2427
2428    return ToLocaleHelper(cx, args, obj, fmtbytes.ptr());
2429}
2430
2431static JSBool
2432date_toTimeString(JSContext *cx, uintN argc, Value *vp)
2433{
2434    CallArgs args = CallArgsFromVp(argc, vp);
2435
2436    bool ok;
2437    JSObject *obj = NonGenericMethodGuard(cx, args, date_toTimeString, &DateClass, &ok);
2438    if (!obj)
2439        return ok;
2440
2441    return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_TIME, args);
2442}
2443
2444static JSBool
2445date_toDateString(JSContext *cx, uintN argc, Value *vp)
2446{
2447    CallArgs args = CallArgsFromVp(argc, vp);
2448
2449    bool ok;
2450    JSObject *obj = NonGenericMethodGuard(cx, args, date_toDateString, &DateClass, &ok);
2451    if (!obj)
2452        return ok;
2453
2454    return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_DATE, args);
2455}
2456
2457#if JS_HAS_TOSOURCE
2458static JSBool
2459date_toSource(JSContext *cx, uintN argc, Value *vp)
2460{
2461    CallArgs args = CallArgsFromVp(argc, vp);
2462
2463    bool ok;
2464    JSObject *obj = NonGenericMethodGuard(cx, args, date_toSource, &DateClass, &ok);
2465    if (!obj)
2466        return ok;
2467
2468    StringBuffer sb(cx);
2469    if (!sb.append("(new Date(") || !NumberValueToStringBuffer(cx, obj->getDateUTCTime(), sb) ||
2470        !sb.append("))"))
2471    {
2472        return false;
2473    }
2474
2475    JSString *str = sb.finishString();
2476    if (!str)
2477        return false;
2478    args.rval().setString(str);
2479    return true;
2480}
2481#endif
2482
2483static JSBool
2484date_toString(JSContext *cx, uintN argc, Value *vp)
2485{
2486    CallArgs args = CallArgsFromVp(argc, vp);
2487
2488    bool ok;
2489    JSObject *obj = NonGenericMethodGuard(cx, args, date_toString, &DateClass, &ok);
2490    if (!obj)
2491        return ok;
2492
2493    return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_FULL, args);
2494}
2495
2496static JSBool
2497date_valueOf(JSContext *cx, uintN argc, Value *vp)
2498{
2499    CallArgs args = CallArgsFromVp(argc, vp);
2500
2501    bool ok;
2502    JSObject *obj = NonGenericMethodGuard(cx, args, date_valueOf, &DateClass, &ok);
2503    if (!obj)
2504        return ok;
2505
2506    /* If called directly with no arguments, convert to a time number. */
2507    if (argc == 0) {
2508        args.rval() = obj->getDateUTCTime();
2509        return true;
2510    }
2511
2512    /* Convert to number only if the hint was given, otherwise favor string. */
2513    JSString *str = ToString(cx, args[0]);
2514    if (!str)
2515        return false;
2516    JSLinearString *linear_str = str->ensureLinear(cx);
2517    if (!linear_str)
2518        return false;
2519    JSAtom *number_str = cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER];
2520    if (EqualStrings(linear_str, number_str)) {
2521        args.rval() = obj->getDateUTCTime();
2522        return true;
2523    }
2524    return date_format(cx, obj->getDateUTCTime().toNumber(), FORMATSPEC_FULL, args);
2525}
2526
2527static JSFunctionSpec date_static_methods[] = {
2528    JS_FN("UTC",                 date_UTC,                MAXARGS,0),
2529    JS_FN("parse",               date_parse,              1,0),
2530    JS_FN("now",                 date_now,                0,0),
2531    JS_FS_END
2532};
2533
2534static JSFunctionSpec date_methods[] = {
2535    JS_FN("getTime",             date_getTime,            0,0),
2536    JS_FN("getTimezoneOffset",   date_getTimezoneOffset,  0,0),
2537    JS_FN("getYear",             date_getYear,            0,0),
2538    JS_FN("getFullYear",         date_getFullYear,        0,0),
2539    JS_FN("getUTCFullYear",      date_getUTCFullYear,     0,0),
2540    JS_FN("getMonth",            date_getMonth,           0,0),
2541    JS_FN("getUTCMonth",         date_getUTCMonth,        0,0),
2542    JS_FN("getDate",             date_getDate,            0,0),
2543    JS_FN("getUTCDate",          date_getUTCDate,         0,0),
2544    JS_FN("getDay",              date_getDay,             0,0),
2545    JS_FN("getUTCDay",           date_getUTCDay,          0,0),
2546    JS_FN("getHours",            date_getHours,           0,0),
2547    JS_FN("getUTCHours",         date_getUTCHours,        0,0),
2548    JS_FN("getMinutes",          date_getMinutes,         0,0),
2549    JS_FN("getUTCMinutes",       date_getUTCMinutes,      0,0),
2550    JS_FN("getSeconds",          date_getUTCSeconds,      0,0),
2551    JS_FN("getUTCSeconds",       date_getUTCSeconds,      0,0),
2552    JS_FN("getMilliseconds",     date_getUTCMilliseconds, 0,0),
2553    JS_FN("getUTCMilliseconds",  date_getUTCMilliseconds, 0,0),
2554    JS_FN("setTime",             date_setTime,            1,0),
2555    JS_FN("setYear",             date_setYear,            1,0),
2556    JS_FN("setFullYear",         date_setFullYear,        3,0),
2557    JS_FN("setUTCFullYear",      date_setUTCFullYear,     3,0),
2558    JS_FN("setMonth",            date_setMonth,           2,0),
2559    JS_FN("setUTCMonth",         date_setUTCMonth,        2,0),
2560    JS_FN("setDate",             date_setDate,            1,0),
2561    JS_FN("setUTCDate",          date_setUTCDate,         1,0),
2562    JS_FN("setHours",            date_setHours,           4,0),
2563    JS_FN("setUTCHours",         date_setUTCHours,        4,0),
2564    JS_FN("setMinutes",          date_setMinutes,         3,0),
2565    JS_FN("setUTCMinutes",       date_setUTCMinutes,      3,0),
2566    JS_FN("setSeconds",          date_setSeconds,         2,0),
2567    JS_FN("setUTCSeconds",       date_setUTCSeconds,      2,0),
2568    JS_FN("setMilliseconds",     date_setMilliseconds,    1,0),
2569    JS_FN("setUTCMilliseconds",  date_setUTCMilliseconds, 1,0),
2570    JS_FN("toUTCString",         date_toGMTString,        0,0),
2571    JS_FN(js_toLocaleString_str, date_toLocaleString,     0,0),
2572    JS_FN("toLocaleDateString",  date_toLocaleDateString, 0,0),
2573    JS_FN("toLocaleTimeString",  date_toLocaleTimeString, 0,0),
2574    JS_FN("toLocaleFormat",      date_toLocaleFormat,     0,0),
2575    JS_FN("toDateString",        date_toDateString,       0,0),
2576    JS_FN("toTimeString",        date_toTimeString,       0,0),
2577    JS_FN("toISOString",         date_toISOString,        0,0),
2578    JS_FN(js_toJSON_str,         date_toJSON,             1,0),
2579#if JS_HAS_TOSOURCE
2580    JS_FN(js_toSource_str,       date_toSource,           0,0),
2581#endif
2582    JS_FN(js_toString_str,       date_toString,           0,0),
2583    JS_FN(js_valueOf_str,        date_valueOf,            0,0),
2584    JS_FS_END
2585};
2586
2587JSBool
2588js_Date(JSContext *cx, uintN argc, Value *vp)
2589{
2590    CallArgs args = CallArgsFromVp(argc, vp);
2591
2592    /* Date called as function. */
2593    if (!IsConstructing(args))
2594        return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args);
2595
2596    /* Date called as constructor. */
2597    jsdouble d;
2598    if (args.length() == 0) {
2599        d = NowAsMillis();
2600    } else if (args.length() == 1) {
2601        if (!args[0].isString()) {
2602            /* the argument is a millisecond number */
2603            if (!ToNumber(cx, args[0], &d))
2604                return false;
2605            d = TIMECLIP(d);
2606        } else {
2607            /* the argument is a string; parse it. */
2608            JSString *str = ToString(cx, args[0]);
2609            if (!str)
2610                return false;
2611            args[0].setString(str);
2612            JSLinearString *linearStr = str->ensureLinear(cx);
2613            if (!linearStr)
2614                return false;
2615
2616            if (!date_parseString(linearStr, &d, cx))
2617                d = js_NaN;
2618            else
2619                d = TIMECLIP(d);
2620        }
2621    } else {
2622        jsdouble msec_time;
2623        if (!date_msecFromArgs(cx, args, &msec_time))
2624            return false;
2625
2626        if (JSDOUBLE_IS_FINITE(msec_time)) {
2627            msec_time = UTC(msec_time, cx);
2628            msec_time = TIMECLIP(msec_time);
2629        }
2630        d = msec_time;
2631    }
2632
2633    JSObject *obj = js_NewDateObjectMsec(cx, d);
2634    if (!obj)
2635        return false;
2636
2637    args.rval().setObject(*obj);
2638    return true;
2639}
2640
2641JSObject *
2642js_InitDateClass(JSContext *cx, JSObject *obj)
2643{
2644    JS_ASSERT(obj->isNative());
2645
2646    /* Set the static LocalTZA. */
2647    LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);
2648
2649    GlobalObject *global = &obj->asGlobal();
2650
2651    JSObject *dateProto = global->createBlankPrototype(cx, &DateClass);
2652    if (!dateProto)
2653        return NULL;
2654    SetDateToNaN(cx, dateProto);
2655
2656    JSFunction *ctor = global->createConstructor(cx, js_Date, &DateClass,
2657                                                 CLASS_ATOM(cx, Date), MAXARGS);
2658    if (!ctor)
2659        return NULL;
2660
2661    if (!LinkConstructorAndPrototype(cx, ctor, dateProto))
2662        return NULL;
2663
2664    if (!DefinePropertiesAndBrand(cx, ctor, NULL, date_static_methods))
2665        return NULL;
2666
2667    /*
2668     * Define all Date.prototype.* functions, then brand for trace-jitted code.
2669     * Date.prototype.toGMTString has the same initial value as
2670     * Date.prototype.toUTCString.
2671     */
2672    if (!JS_DefineFunctions(cx, dateProto, date_methods))
2673        return NULL;
2674    Value toUTCStringFun;
2675    jsid toUTCStringId = ATOM_TO_JSID(cx->runtime->atomState.toUTCStringAtom);
2676    jsid toGMTStringId = ATOM_TO_JSID(cx->runtime->atomState.toGMTStringAtom);
2677    if (!js_GetProperty(cx, dateProto, toUTCStringId, &toUTCStringFun) ||
2678        !js_DefineProperty(cx, dateProto, toGMTStringId, &toUTCStringFun,
2679                           JS_PropertyStub, JS_StrictPropertyStub, 0))
2680    {
2681        return NULL;
2682    }
2683
2684    if (!DefineConstructorAndPrototype(cx, global, JSProto_Date, ctor, dateProto))
2685        return NULL;
2686
2687    return dateProto;
2688}
2689
2690JS_FRIEND_API(JSObject *)
2691js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
2692{
2693    JSObject *obj = NewBuiltinClassInstance(cx, &DateClass);
2694    if (!obj)
2695        return NULL;
2696    if (!SetUTCTime(cx, obj, msec_time))
2697        return NULL;
2698    return obj;
2699}
2700
2701JS_FRIEND_API(JSObject *)
2702js_NewDateObject(JSContext* cx, int year, int mon, int mday,
2703                 int hour, int min, int sec)
2704{
2705    JSObject *obj;
2706    jsdouble msec_time;
2707
2708    JS_ASSERT(mon < 12);
2709    msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
2710    obj = js_NewDateObjectMsec(cx, UTC(msec_time, cx));
2711    return obj;
2712}
2713
2714JS_FRIEND_API(JSBool)
2715js_DateIsValid(JSContext *cx, JSObject* obj)
2716{
2717    return obj->isDate() && !JSDOUBLE_IS_NaN(obj->getDateUTCTime().toNumber());
2718}
2719
2720JS_FRIEND_API(int)
2721js_DateGetYear(JSContext *cx, JSObject* obj)
2722{
2723    jsdouble localtime;
2724
2725    /* Preserve legacy API behavior of returning 0 for invalid dates. */
2726    if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2727        JSDOUBLE_IS_NaN(localtime)) {
2728        return 0;
2729    }
2730
2731    return (int) YearFromTime(localtime);
2732}
2733
2734JS_FRIEND_API(int)
2735js_DateGetMonth(JSContext *cx, JSObject* obj)
2736{
2737    jsdouble localtime;
2738
2739    if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2740        JSDOUBLE_IS_NaN(localtime)) {
2741        return 0;
2742    }
2743
2744    return (int) MonthFromTime(localtime);
2745}
2746
2747JS_FRIEND_API(int)
2748js_DateGetDate(JSContext *cx, JSObject* obj)
2749{
2750    jsdouble localtime;
2751
2752    if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2753        JSDOUBLE_IS_NaN(localtime)) {
2754        return 0;
2755    }
2756
2757    return (int) DateFromTime(localtime);
2758}
2759
2760JS_FRIEND_API(int)
2761js_DateGetHours(JSContext *cx, JSObject* obj)
2762{
2763    jsdouble localtime;
2764
2765    if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2766        JSDOUBLE_IS_NaN(localtime)) {
2767        return 0;
2768    }
2769
2770    return (int) HourFromTime(localtime);
2771}
2772
2773JS_FRIEND_API(int)
2774js_DateGetMinutes(JSContext *cx, JSObject* obj)
2775{
2776    jsdouble localtime;
2777
2778    if (!GetAndCacheLocalTime(cx, obj, &localtime) ||
2779        JSDOUBLE_IS_NaN(localtime)) {
2780        return 0;
2781    }
2782
2783    return (int) MinFromTime(localtime);
2784}
2785
2786JS_FRIEND_API(int)
2787js_DateGetSeconds(JSContext *cx, JSObject* obj)
2788{
2789    if (!obj->isDate()) 
2790        return 0;
2791    
2792    double utctime = obj->getDateUTCTime().toNumber();
2793    if (JSDOUBLE_IS_NaN(utctime))
2794        return 0;
2795    return (int) SecFromTime(utctime);
2796}
2797
2798JS_FRIEND_API(jsdouble)
2799js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
2800{
2801    return obj->isDate() ? obj->getDateUTCTime().toNumber() : 0;
2802}
2803
2804#ifdef JS_THREADSAFE
2805#include "prinrval.h"
2806
2807JS_FRIEND_API(uint32_t)
2808js_IntervalNow()
2809{
2810    return uint32_t(PR_IntervalToMilliseconds(PR_IntervalNow()));
2811}
2812
2813#else /* !JS_THREADSAFE */
2814
2815JS_FRIEND_API(uint32_t)
2816js_IntervalNow()
2817{
2818    return uint32_t(PRMJ_Now() / PRMJ_USEC_PER_MSEC);
2819}
2820#endif