PageRenderTime 334ms CodeModel.GetById 72ms app.highlight 217ms RepoModel.GetById 7ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsdate.cpp

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

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