js/src/jsdate.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * JS date methods.
     9  *
    10  * "For example, OS/360 devotes 26 bytes of the permanently
    11  *  resident date-turnover routine to the proper handling of
    12  *  December 31 on leap years (when it is Day 366).  That
    13  *  might have been left to the operator."
    14  *
    15  * Frederick Brooks, 'The Second-System Effect'.
    16  */
    18 #include "jsdate.h"
    20 #include "mozilla/ArrayUtils.h"
    21 #include "mozilla/FloatingPoint.h"
    23 #include <ctype.h>
    24 #include <math.h>
    25 #include <string.h>
    27 #include "jsapi.h"
    28 #include "jscntxt.h"
    29 #include "jsnum.h"
    30 #include "jsobj.h"
    31 #include "jsprf.h"
    32 #include "jsstr.h"
    33 #include "jstypes.h"
    34 #include "jsutil.h"
    35 #include "prmjtime.h"
    37 #include "js/Date.h"
    38 #include "vm/DateTime.h"
    39 #include "vm/GlobalObject.h"
    40 #include "vm/Interpreter.h"
    41 #include "vm/NumericConversions.h"
    42 #include "vm/String.h"
    43 #include "vm/StringBuffer.h"
    45 #include "jsobjinlines.h"
    47 using namespace js;
    48 using namespace js::types;
    50 using mozilla::ArrayLength;
    51 using mozilla::IsFinite;
    52 using mozilla::IsNaN;
    53 using JS::GenericNaN;
    55 /*
    56  * The JS 'Date' object is patterned after the Java 'Date' object.
    57  * Here is a script:
    58  *
    59  *    today = new Date();
    60  *
    61  *    print(today.toLocaleString());
    62  *
    63  *    weekDay = today.getDay();
    64  *
    65  *
    66  * These Java (and ECMA-262) methods are supported:
    67  *
    68  *     UTC
    69  *     getDate (getUTCDate)
    70  *     getDay (getUTCDay)
    71  *     getHours (getUTCHours)
    72  *     getMinutes (getUTCMinutes)
    73  *     getMonth (getUTCMonth)
    74  *     getSeconds (getUTCSeconds)
    75  *     getMilliseconds (getUTCMilliseconds)
    76  *     getTime
    77  *     getTimezoneOffset
    78  *     getYear
    79  *     getFullYear (getUTCFullYear)
    80  *     parse
    81  *     setDate (setUTCDate)
    82  *     setHours (setUTCHours)
    83  *     setMinutes (setUTCMinutes)
    84  *     setMonth (setUTCMonth)
    85  *     setSeconds (setUTCSeconds)
    86  *     setMilliseconds (setUTCMilliseconds)
    87  *     setTime
    88  *     setYear (setFullYear, setUTCFullYear)
    89  *     toGMTString (toUTCString)
    90  *     toLocaleString
    91  *     toString
    92  *
    93  *
    94  * These Java methods are not supported
    95  *
    96  *     setDay
    97  *     before
    98  *     after
    99  *     equals
   100  *     hashCode
   101  */
   103 static inline double
   104 Day(double t)
   105 {
   106     return floor(t / msPerDay);
   107 }
   109 static double
   110 TimeWithinDay(double t)
   111 {
   112     double result = fmod(t, msPerDay);
   113     if (result < 0)
   114         result += msPerDay;
   115     return result;
   116 }
   118 /* ES5 15.9.1.3. */
   119 static inline bool
   120 IsLeapYear(double year)
   121 {
   122     JS_ASSERT(ToInteger(year) == year);
   123     return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0);
   124 }
   126 static inline double
   127 DaysInYear(double year)
   128 {
   129     if (!IsFinite(year))
   130         return GenericNaN();
   131     return IsLeapYear(year) ? 366 : 365;
   132 }
   134 static inline double
   135 DayFromYear(double y)
   136 {
   137     return 365 * (y - 1970) +
   138            floor((y - 1969) / 4.0) -
   139            floor((y - 1901) / 100.0) +
   140            floor((y - 1601) / 400.0);
   141 }
   143 static inline double
   144 TimeFromYear(double y)
   145 {
   146     return DayFromYear(y) * msPerDay;
   147 }
   149 static double
   150 YearFromTime(double t)
   151 {
   152     if (!IsFinite(t))
   153         return GenericNaN();
   155     JS_ASSERT(ToInteger(t) == t);
   157     double y = floor(t / (msPerDay * 365.2425)) + 1970;
   158     double t2 = TimeFromYear(y);
   160     /*
   161      * Adjust the year if the approximation was wrong.  Since the year was
   162      * computed using the average number of ms per year, it will usually
   163      * be wrong for dates within several hours of a year transition.
   164      */
   165     if (t2 > t) {
   166         y--;
   167     } else {
   168         if (t2 + msPerDay * DaysInYear(y) <= t)
   169             y++;
   170     }
   171     return y;
   172 }
   174 static inline int
   175 DaysInFebruary(double year)
   176 {
   177     return IsLeapYear(year) ? 29 : 28;
   178 }
   180 /* ES5 15.9.1.4. */
   181 static inline double
   182 DayWithinYear(double t, double year)
   183 {
   184     JS_ASSERT_IF(IsFinite(t), YearFromTime(t) == year);
   185     return Day(t) - DayFromYear(year);
   186 }
   188 static double
   189 MonthFromTime(double t)
   190 {
   191     if (!IsFinite(t))
   192         return GenericNaN();
   194     double year = YearFromTime(t);
   195     double d = DayWithinYear(t, year);
   197     int step;
   198     if (d < (step = 31))
   199         return 0;
   200     if (d < (step += DaysInFebruary(year)))
   201         return 1;
   202     if (d < (step += 31))
   203         return 2;
   204     if (d < (step += 30))
   205         return 3;
   206     if (d < (step += 31))
   207         return 4;
   208     if (d < (step += 30))
   209         return 5;
   210     if (d < (step += 31))
   211         return 6;
   212     if (d < (step += 31))
   213         return 7;
   214     if (d < (step += 30))
   215         return 8;
   216     if (d < (step += 31))
   217         return 9;
   218     if (d < (step += 30))
   219         return 10;
   220     return 11;
   221 }
   223 /* ES5 15.9.1.5. */
   224 static double
   225 DateFromTime(double t)
   226 {
   227     if (!IsFinite(t))
   228         return GenericNaN();
   230     double year = YearFromTime(t);
   231     double d = DayWithinYear(t, year);
   233     int next;
   234     if (d <= (next = 30))
   235         return d + 1;
   236     int step = next;
   237     if (d <= (next += DaysInFebruary(year)))
   238         return d - step;
   239     step = next;
   240     if (d <= (next += 31))
   241         return d - step;
   242     step = next;
   243     if (d <= (next += 30))
   244         return d - step;
   245     step = next;
   246     if (d <= (next += 31))
   247         return d - step;
   248     step = next;
   249     if (d <= (next += 30))
   250         return d - step;
   251     step = next;
   252     if (d <= (next += 31))
   253         return d - step;
   254     step = next;
   255     if (d <= (next += 31))
   256         return d - step;
   257     step = next;
   258     if (d <= (next += 30))
   259         return d - step;
   260     step = next;
   261     if (d <= (next += 31))
   262         return d - step;
   263     step = next;
   264     if (d <= (next += 30))
   265         return d - step;
   266     step = next;
   267     return d - step;
   268 }
   270 /* ES5 15.9.1.6. */
   271 static int
   272 WeekDay(double t)
   273 {
   274     /*
   275      * We can't assert TimeClip(t) == t because we call this function with
   276      * local times, which can be offset outside TimeClip's permitted range.
   277      */
   278     JS_ASSERT(ToInteger(t) == t);
   279     int result = (int(Day(t)) + 4) % 7;
   280     if (result < 0)
   281         result += 7;
   282     return result;
   283 }
   285 static inline int
   286 DayFromMonth(int month, bool isLeapYear)
   287 {
   288     /*
   289      * The following array contains the day of year for the first day of
   290      * each month, where index 0 is January, and day 0 is January 1.
   291      */
   292     static const int firstDayOfMonth[2][13] = {
   293         {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
   294         {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
   295     };
   297     JS_ASSERT(0 <= month && month <= 12);
   298     return firstDayOfMonth[isLeapYear][month];
   299 }
   301 template<typename T>
   302 static inline int
   303 DayFromMonth(T month, bool isLeapYear) MOZ_DELETE;
   305 /* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
   306 static double
   307 MakeDay(double year, double month, double date)
   308 {
   309     /* Step 1. */
   310     if (!IsFinite(year) || !IsFinite(month) || !IsFinite(date))
   311         return GenericNaN();
   313     /* Steps 2-4. */
   314     double y = ToInteger(year);
   315     double m = ToInteger(month);
   316     double dt = ToInteger(date);
   318     /* Step 5. */
   319     double ym = y + floor(m / 12);
   321     /* Step 6. */
   322     int mn = int(fmod(m, 12.0));
   323     if (mn < 0)
   324         mn += 12;
   326     /* Steps 7-8. */
   327     bool leap = IsLeapYear(ym);
   329     double yearday = floor(TimeFromYear(ym) / msPerDay);
   330     double monthday = DayFromMonth(mn, leap);
   332     return yearday + monthday + dt - 1;
   333 }
   335 /* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
   336 static inline double
   337 MakeDate(double day, double time)
   338 {
   339     /* Step 1. */
   340     if (!IsFinite(day) || !IsFinite(time))
   341         return GenericNaN();
   343     /* Step 2. */
   344     return day * msPerDay + time;
   345 }
   347 JS_PUBLIC_API(double)
   348 JS::MakeDate(double year, unsigned month, unsigned day)
   349 {
   350     return TimeClip(::MakeDate(MakeDay(year, month, day), 0));
   351 }
   353 JS_PUBLIC_API(double)
   354 JS::YearFromTime(double time)
   355 {
   356     return ::YearFromTime(time);
   357 }
   359 JS_PUBLIC_API(double)
   360 JS::MonthFromTime(double time)
   361 {
   362     return ::MonthFromTime(time);
   363 }
   365 JS_PUBLIC_API(double)
   366 JS::DayFromTime(double time)
   367 {
   368     return DateFromTime(time);
   369 }
   371 /*
   372  * Find a year for which any given date will fall on the same weekday.
   373  *
   374  * This function should be used with caution when used other than
   375  * for determining DST; it hasn't been proven not to produce an
   376  * incorrect year for times near year boundaries.
   377  */
   378 static int
   379 EquivalentYearForDST(int year)
   380 {
   381     /*
   382      * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
   383      *
   384      * yearStartingWith[0][i] is an example non-leap year where
   385      * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
   386      *
   387      * yearStartingWith[1][i] is an example leap year where
   388      * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
   389      */
   390     static const int yearStartingWith[2][7] = {
   391         {1978, 1973, 1974, 1975, 1981, 1971, 1977},
   392         {1984, 1996, 1980, 1992, 1976, 1988, 1972}
   393     };
   395     int day = int(DayFromYear(year) + 4) % 7;
   396     if (day < 0)
   397         day += 7;
   399     return yearStartingWith[IsLeapYear(year)][day];
   400 }
   402 /* ES5 15.9.1.8. */
   403 static double
   404 DaylightSavingTA(double t, DateTimeInfo *dtInfo)
   405 {
   406     if (!IsFinite(t))
   407         return GenericNaN();
   409     /*
   410      * If earlier than 1970 or after 2038, potentially beyond the ken of
   411      * many OSes, map it to an equivalent year before asking.
   412      */
   413     if (t < 0.0 || t > 2145916800000.0) {
   414         int year = EquivalentYearForDST(int(YearFromTime(t)));
   415         double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
   416         t = MakeDate(day, TimeWithinDay(t));
   417     }
   419     int64_t utcMilliseconds = static_cast<int64_t>(t);
   420     int64_t offsetMilliseconds = dtInfo->getDSTOffsetMilliseconds(utcMilliseconds);
   421     return static_cast<double>(offsetMilliseconds);
   422 }
   424 static double
   425 AdjustTime(double date, DateTimeInfo *dtInfo)
   426 {
   427     double t = DaylightSavingTA(date, dtInfo) + dtInfo->localTZA();
   428     t = (dtInfo->localTZA() >= 0) ? fmod(t, msPerDay) : -fmod(msPerDay - t, msPerDay);
   429     return t;
   430 }
   432 /* ES5 15.9.1.9. */
   433 static double
   434 LocalTime(double t, DateTimeInfo *dtInfo)
   435 {
   436     return t + AdjustTime(t, dtInfo);
   437 }
   439 static double
   440 UTC(double t, DateTimeInfo *dtInfo)
   441 {
   442     return t - AdjustTime(t - dtInfo->localTZA(), dtInfo);
   443 }
   445 /* ES5 15.9.1.10. */
   446 static double
   447 HourFromTime(double t)
   448 {
   449     double result = fmod(floor(t/msPerHour), HoursPerDay);
   450     if (result < 0)
   451         result += HoursPerDay;
   452     return result;
   453 }
   455 static double
   456 MinFromTime(double t)
   457 {
   458     double result = fmod(floor(t / msPerMinute), MinutesPerHour);
   459     if (result < 0)
   460         result += MinutesPerHour;
   461     return result;
   462 }
   464 static double
   465 SecFromTime(double t)
   466 {
   467     double result = fmod(floor(t / msPerSecond), SecondsPerMinute);
   468     if (result < 0)
   469         result += SecondsPerMinute;
   470     return result;
   471 }
   473 static double
   474 msFromTime(double t)
   475 {
   476     double result = fmod(t, msPerSecond);
   477     if (result < 0)
   478         result += msPerSecond;
   479     return result;
   480 }
   482 /* ES5 15.9.1.11. */
   483 static double
   484 MakeTime(double hour, double min, double sec, double ms)
   485 {
   486     /* Step 1. */
   487     if (!IsFinite(hour) ||
   488         !IsFinite(min) ||
   489         !IsFinite(sec) ||
   490         !IsFinite(ms))
   491     {
   492         return GenericNaN();
   493     }
   495     /* Step 2. */
   496     double h = ToInteger(hour);
   498     /* Step 3. */
   499     double m = ToInteger(min);
   501     /* Step 4. */
   502     double s = ToInteger(sec);
   504     /* Step 5. */
   505     double milli = ToInteger(ms);
   507     /* Steps 6-7. */
   508     return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
   509 }
   511 /**
   512  * end of ECMA 'support' functions
   513  */
   515 static bool
   516 date_convert(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
   517 {
   518     JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
   519     JS_ASSERT(obj->is<DateObject>());
   521     return DefaultValue(cx, obj, (hint == JSTYPE_VOID) ? JSTYPE_STRING : hint, vp);
   522 }
   524 /* for use by date_parse */
   526 static const char* const 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 };
   540 static const 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 };
   550 /* helper for date_parse */
   551 static bool
   552 date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,
   553                    int count, int ignoreCase)
   554 {
   555     bool result = false;
   556     /* return true if matches, otherwise, false */
   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     }
   572     if (count == 0) {
   573         result = true;
   574     }
   576     return result;
   577 }
   579 /* find UTC time from given date... no 1900 correction! */
   580 static double
   581 date_msecFromDate(double year, double mon, double mday, double hour,
   582                   double min, double sec, double msec)
   583 {
   584     return MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, msec));
   585 }
   587 /* compute the time in msec (unclipped) from the given args */
   588 #define MAXARGS        7
   590 static bool
   591 date_msecFromArgs(JSContext *cx, CallArgs args, double *rval)
   592 {
   593     unsigned loop;
   594     double array[MAXARGS];
   595     double msec_time;
   597     for (loop = 0; loop < MAXARGS; loop++) {
   598         if (loop < args.length()) {
   599             double d;
   600             if (!ToNumber(cx, args[loop], &d))
   601                 return false;
   602             /* return NaN if any arg is not finite */
   603             if (!IsFinite(d)) {
   604                 *rval = GenericNaN();
   605                 return true;
   606             }
   607             array[loop] = ToInteger(d);
   608         } else {
   609             if (loop == 2) {
   610                 array[loop] = 1; /* Default the date argument to 1. */
   611             } else {
   612                 array[loop] = 0;
   613             }
   614         }
   615     }
   617     /* adjust 2-digit years into the 20th century */
   618     if (array[0] >= 0 && array[0] <= 99)
   619         array[0] += 1900;
   621     msec_time = date_msecFromDate(array[0], array[1], array[2],
   622                                   array[3], array[4], array[5], array[6]);
   623     *rval = msec_time;
   624     return true;
   625 }
   627 /*
   628  * See ECMA 15.9.4.[3-10];
   629  */
   630 static bool
   631 date_UTC(JSContext *cx, unsigned argc, Value *vp)
   632 {
   633     CallArgs args = CallArgsFromVp(argc, vp);
   635     double msec_time;
   636     if (!date_msecFromArgs(cx, args, &msec_time))
   637         return false;
   639     msec_time = TimeClip(msec_time);
   641     args.rval().setNumber(msec_time);
   642     return true;
   643 }
   645 /*
   646  * Read and convert decimal digits from s[*i] into *result
   647  * while *i < limit.
   648  *
   649  * Succeed if any digits are converted. Advance *i only
   650  * as digits are consumed.
   651  */
   652 static bool
   653 digits(size_t *result, const jschar *s, size_t *i, size_t limit)
   654 {
   655     size_t init = *i;
   656     *result = 0;
   657     while (*i < limit &&
   658            ('0' <= s[*i] && s[*i] <= '9')) {
   659         *result *= 10;
   660         *result += (s[*i] - '0');
   661         ++(*i);
   662     }
   663     return *i != init;
   664 }
   666 /*
   667  * Read and convert decimal digits to the right of a decimal point,
   668  * representing a fractional integer, from s[*i] into *result
   669  * while *i < limit.
   670  *
   671  * Succeed if any digits are converted. Advance *i only
   672  * as digits are consumed.
   673  */
   674 static bool
   675 fractional(double *result, const jschar *s, size_t *i, size_t limit)
   676 {
   677     double factor = 0.1;
   678     size_t init = *i;
   679     *result = 0.0;
   680     while (*i < limit &&
   681            ('0' <= s[*i] && s[*i] <= '9')) {
   682         *result += (s[*i] - '0') * factor;
   683         factor *= 0.1;
   684         ++(*i);
   685     }
   686     return *i != init;
   687 }
   689 /*
   690  * Read and convert exactly n decimal digits from s[*i]
   691  * to s[min(*i+n,limit)] into *result.
   692  *
   693  * Succeed if exactly n digits are converted. Advance *i only
   694  * on success.
   695  */
   696 static bool
   697 ndigits(size_t n, size_t *result, const jschar *s, size_t* i, size_t limit)
   698 {
   699     size_t init = *i;
   701     if (digits(result, s, i, Min(limit, init+n)))
   702         return (*i - init) == n;
   704     *i = init;
   705     return false;
   706 }
   708 static int
   709 DaysInMonth(int year, int month)
   710 {
   711     bool leap = IsLeapYear(year);
   712     int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap));
   713     return result;
   714 }
   716 /*
   717  * Parse a string in one of the date-time formats given by the W3C
   718  * "NOTE-datetime" specification. These formats make up a restricted
   719  * profile of the ISO 8601 format. Quoted here:
   720  *
   721  *   The formats are as follows. Exactly the components shown here
   722  *   must be present, with exactly this punctuation. Note that the "T"
   723  *   appears literally in the string, to indicate the beginning of the
   724  *   time element, as specified in ISO 8601.
   725  *
   726  *   Any combination of the date formats with the time formats is
   727  *   allowed, and also either the date or the time can be missing.
   728  *
   729  *   The specification is silent on the meaning when fields are
   730  *   ommitted so the interpretations are a guess, but hopefully a
   731  *   reasonable one. We default the month to January, the day to the
   732  *   1st, and hours minutes and seconds all to 0. If the date is
   733  *   missing entirely then we assume 1970-01-01 so that the time can
   734  *   be aded to a date later. If the time is missing then we assume
   735  *   00:00 UTC.  If the time is present but the time zone field is
   736  *   missing then we use local time.
   737  *
   738  * Date part:
   739  *
   740  *  Year:
   741  *     YYYY (eg 1997)
   742  *
   743  *  Year and month:
   744  *     YYYY-MM (eg 1997-07)
   745  *
   746  *  Complete date:
   747  *     YYYY-MM-DD (eg 1997-07-16)
   748  *
   749  * Time part:
   750  *
   751  *  Hours and minutes:
   752  *     Thh:mmTZD (eg T19:20+01:00)
   753  *
   754  *  Hours, minutes and seconds:
   755  *     Thh:mm:ssTZD (eg T19:20:30+01:00)
   756  *
   757  *  Hours, minutes, seconds and a decimal fraction of a second:
   758  *     Thh:mm:ss.sTZD (eg T19:20:30.45+01:00)
   759  *
   760  * where:
   761  *
   762  *   YYYY = four-digit year or six digit year as +YYYYYY or -YYYYYY
   763  *   MM   = two-digit month (01=January, etc.)
   764  *   DD   = two-digit day of month (01 through 31)
   765  *   hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
   766  *   mm   = two digits of minute (00 through 59)
   767  *   ss   = two digits of second (00 through 59)
   768  *   s    = one or more digits representing a decimal fraction of a second
   769  *   TZD  = time zone designator (Z or +hh:mm or -hh:mm or missing for local)
   770  */
   772 static bool
   773 date_parseISOString(JSLinearString *str, double *result, DateTimeInfo *dtInfo)
   774 {
   775     double msec;
   777     const jschar *s;
   778     size_t limit;
   779     size_t i = 0;
   780     int tzMul = 1;
   781     int dateMul = 1;
   782     size_t year = 1970;
   783     size_t month = 1;
   784     size_t day = 1;
   785     size_t hour = 0;
   786     size_t min = 0;
   787     size_t sec = 0;
   788     double frac = 0;
   789     bool isLocalTime = false;
   790     size_t tzHour = 0;
   791     size_t tzMin = 0;
   793 #define PEEK(ch) (i < limit && s[i] == ch)
   795 #define NEED(ch)                                                     \
   796     JS_BEGIN_MACRO                                                   \
   797         if (i >= limit || s[i] != ch) { goto syntax; } else { ++i; } \
   798     JS_END_MACRO
   800 #define DONE_DATE_UNLESS(ch)                                            \
   801     JS_BEGIN_MACRO                                                      \
   802         if (i >= limit || s[i] != ch) { goto done_date; } else { ++i; } \
   803     JS_END_MACRO
   805 #define DONE_UNLESS(ch)                                            \
   806     JS_BEGIN_MACRO                                                 \
   807         if (i >= limit || s[i] != ch) { goto done; } else { ++i; } \
   808     JS_END_MACRO
   810 #define NEED_NDIGITS(n, field)                                      \
   811     JS_BEGIN_MACRO                                                  \
   812         if (!ndigits(n, &field, s, &i, limit)) { goto syntax; }     \
   813     JS_END_MACRO
   815     s = str->chars();
   816     limit = str->length();
   818     if (PEEK('+') || PEEK('-')) {
   819         if (PEEK('-'))
   820             dateMul = -1;
   821         ++i;
   822         NEED_NDIGITS(6, year);
   823     } else if (!PEEK('T')) {
   824         NEED_NDIGITS(4, year);
   825     }
   826     DONE_DATE_UNLESS('-');
   827     NEED_NDIGITS(2, month);
   828     DONE_DATE_UNLESS('-');
   829     NEED_NDIGITS(2, day);
   831  done_date:
   832     DONE_UNLESS('T');
   833     NEED_NDIGITS(2, hour);
   834     NEED(':');
   835     NEED_NDIGITS(2, min);
   837     if (PEEK(':')) {
   838         ++i;
   839         NEED_NDIGITS(2, sec);
   840         if (PEEK('.')) {
   841             ++i;
   842             if (!fractional(&frac, s, &i, limit))
   843                 goto syntax;
   844         }
   845     }
   847     if (PEEK('Z')) {
   848         ++i;
   849     } else if (PEEK('+') || PEEK('-')) {
   850         if (PEEK('-'))
   851             tzMul = -1;
   852         ++i;
   853         NEED_NDIGITS(2, tzHour);
   854         /*
   855          * Non-standard extension to the ISO date format (permitted by ES5):
   856          * allow "-0700" as a time zone offset, not just "-07:00".
   857          */
   858         if (PEEK(':'))
   859           ++i;
   860         NEED_NDIGITS(2, tzMin);
   861     } else {
   862         isLocalTime = true;
   863     }
   865  done:
   866     if (year > 275943 // ceil(1e8/365) + 1970
   867         || (month == 0 || month > 12)
   868         || (day == 0 || day > size_t(DaysInMonth(year,month)))
   869         || hour > 24
   870         || ((hour == 24) && (min > 0 || sec > 0))
   871         || min > 59
   872         || sec > 59
   873         || tzHour > 23
   874         || tzMin > 59)
   875         goto syntax;
   877     if (i != limit)
   878         goto syntax;
   880     month -= 1; /* convert month to 0-based */
   882     msec = date_msecFromDate(dateMul * (double)year, month, day,
   883                              hour, min, sec,
   884                              frac * 1000.0);;
   886     if (isLocalTime) {
   887         msec = UTC(msec, dtInfo);
   888     } else {
   889         msec -= ((tzMul) * ((tzHour * msPerHour)
   890                             + (tzMin * msPerMinute)));
   891     }
   893     if (msec < -8.64e15 || msec > 8.64e15)
   894         goto syntax;
   896     *result = msec;
   898     return true;
   900  syntax:
   901     /* syntax error */
   902     *result = 0;
   903     return false;
   905 #undef PEEK
   906 #undef NEED
   907 #undef DONE_UNLESS
   908 #undef NEED_NDIGITS
   909 }
   911 static bool
   912 date_parseString(JSLinearString *str, double *result, DateTimeInfo *dtInfo)
   913 {
   914     double msec;
   916     const jschar *s;
   917     size_t limit;
   918     size_t i = 0;
   919     int year = -1;
   920     int mon = -1;
   921     int mday = -1;
   922     int hour = -1;
   923     int min = -1;
   924     int sec = -1;
   925     int c = -1;
   926     int n = -1;
   927     int tzoffset = -1;
   928     int prevc = 0;
   929     bool seenplusminus = false;
   930     int temp;
   931     bool seenmonthname = false;
   933     if (date_parseISOString(str, result, dtInfo))
   934         return true;
   936     s = str->chars();
   937     limit = str->length();
   938     if (limit == 0)
   939         goto syntax;
   940     while (i < limit) {
   941         c = s[i];
   942         i++;
   943         if (c <= ' ' || c == ',' || c == '-') {
   944             if (c == '-' && '0' <= s[i] && s[i] <= '9') {
   945               prevc = c;
   946             }
   947             continue;
   948         }
   949         if (c == '(') { /* comments) */
   950             int depth = 1;
   951             while (i < limit) {
   952                 c = s[i];
   953                 i++;
   954                 if (c == '(') depth++;
   955                 else if (c == ')')
   956                     if (--depth <= 0)
   957                         break;
   958             }
   959             continue;
   960         }
   961         if ('0' <= c && c <= '9') {
   962             n = c - '0';
   963             while (i < limit && '0' <= (c = s[i]) && c <= '9') {
   964                 n = n * 10 + c - '0';
   965                 i++;
   966             }
   968             /* allow TZA before the year, so
   969              * 'Wed Nov 05 21:49:11 GMT-0800 1997'
   970              * works */
   972             /* uses of seenplusminus allow : in TZA, so Java
   973              * no-timezone style of GMT+4:30 works
   974              */
   976             if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {
   977                 /* make ':' case below change tzoffset */
   978                 seenplusminus = true;
   980                 /* offset */
   981                 if (n < 24)
   982                     n = n * 60; /* EG. "GMT-3" */
   983                 else
   984                     n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
   985                 if (prevc == '+')       /* plus means east of GMT */
   986                     n = -n;
   987                 if (tzoffset != 0 && tzoffset != -1)
   988                     goto syntax;
   989                 tzoffset = n;
   990             } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) {
   991                 if (c <= ' ' || c == ',' || c == '/' || i >= limit)
   992                     year = n;
   993                 else
   994                     goto syntax;
   995             } else if (c == ':') {
   996                 if (hour < 0)
   997                     hour = /*byte*/ n;
   998                 else if (min < 0)
   999                     min = /*byte*/ n;
  1000                 else
  1001                     goto syntax;
  1002             } else if (c == '/') {
  1003                 /* until it is determined that mon is the actual
  1004                    month, keep it as 1-based rather than 0-based */
  1005                 if (mon < 0)
  1006                     mon = /*byte*/ n;
  1007                 else if (mday < 0)
  1008                     mday = /*byte*/ n;
  1009                 else
  1010                     goto syntax;
  1011             } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') {
  1012                 goto syntax;
  1013             } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */
  1014                 if (tzoffset < 0)
  1015                     tzoffset -= n;
  1016                 else
  1017                     tzoffset += n;
  1018             } else if (hour >= 0 && min < 0) {
  1019                 min = /*byte*/ n;
  1020             } else if (prevc == ':' && min >= 0 && sec < 0) {
  1021                 sec = /*byte*/ n;
  1022             } else if (mon < 0) {
  1023                 mon = /*byte*/n;
  1024             } else if (mon >= 0 && mday < 0) {
  1025                 mday = /*byte*/ n;
  1026             } else if (mon >= 0 && mday >= 0 && year < 0) {
  1027                 year = n;
  1028             } else {
  1029                 goto syntax;
  1031             prevc = 0;
  1032         } else if (c == '/' || c == ':' || c == '+' || c == '-') {
  1033             prevc = c;
  1034         } else {
  1035             size_t st = i - 1;
  1036             int k;
  1037             while (i < limit) {
  1038                 c = s[i];
  1039                 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
  1040                     break;
  1041                 i++;
  1043             if (i <= st + 1)
  1044                 goto syntax;
  1045             for (k = ArrayLength(wtb); --k >= 0;)
  1046                 if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {
  1047                     int action = ttb[k];
  1048                     if (action != 0) {
  1049                         if (action < 0) {
  1050                             /*
  1051                              * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
  1052                              * 12:30, instead of blindly adding 12 if PM.
  1053                              */
  1054                             JS_ASSERT(action == -1 || action == -2);
  1055                             if (hour > 12 || hour < 0) {
  1056                                 goto syntax;
  1057                             } else {
  1058                                 if (action == -1 && hour == 12) { /* am */
  1059                                     hour = 0;
  1060                                 } else if (action == -2 && hour != 12) { /* pm */
  1061                                     hour += 12;
  1064                         } else if (action <= 13) { /* month! */
  1065                             /* Adjust mon to be 1-based until the final values
  1066                                for mon, mday and year are adjusted below */
  1067                             if (seenmonthname) {
  1068                                 goto syntax;
  1070                             seenmonthname = true;
  1071                             temp = /*byte*/ (action - 2) + 1;
  1073                             if (mon < 0) {
  1074                                 mon = temp;
  1075                             } else if (mday < 0) {
  1076                                 mday = mon;
  1077                                 mon = temp;
  1078                             } else if (year < 0) {
  1079                                 year = mon;
  1080                                 mon = temp;
  1081                             } else {
  1082                                 goto syntax;
  1084                         } else {
  1085                             tzoffset = action - 10000;
  1088                     break;
  1090             if (k < 0)
  1091                 goto syntax;
  1092             prevc = 0;
  1095     if (year < 0 || mon < 0 || mday < 0)
  1096         goto syntax;
  1097     /*
  1098       Case 1. The input string contains an English month name.
  1099               The form of the string can be month f l, or f month l, or
  1100               f l month which each evaluate to the same date.
  1101               If f and l are both greater than or equal to 70, or
  1102               both less than 70, the date is invalid.
  1103               The year is taken to be the greater of the values f, l.
  1104               If the year is greater than or equal to 70 and less than 100,
  1105               it is considered to be the number of years after 1900.
  1106       Case 2. The input string is of the form "f/m/l" where f, m and l are
  1107               integers, e.g. 7/16/45.
  1108               Adjust the mon, mday and year values to achieve 100% MSIE
  1109               compatibility.
  1110               a. If 0 <= f < 70, f/m/l is interpreted as month/day/year.
  1111                  i.  If year < 100, it is the number of years after 1900
  1112                  ii. If year >= 100, it is the number of years after 0.
  1113               b. If 70 <= f < 100
  1114                  i.  If m < 70, f/m/l is interpreted as
  1115                      year/month/day where year is the number of years after
  1116                      1900.
  1117                  ii. If m >= 70, the date is invalid.
  1118               c. If f >= 100
  1119                  i.  If m < 70, f/m/l is interpreted as
  1120                      year/month/day where year is the number of years after 0.
  1121                  ii. If m >= 70, the date is invalid.
  1122     */
  1123     if (seenmonthname) {
  1124         if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) {
  1125             goto syntax;
  1127         if (mday > year) {
  1128             temp = year;
  1129             year = mday;
  1130             mday = temp;
  1132         if (year >= 70 && year < 100) {
  1133             year += 1900;
  1135     } else if (mon < 70) { /* (a) month/day/year */
  1136         if (year < 100) {
  1137             year += 1900;
  1139     } else if (mon < 100) { /* (b) year/month/day */
  1140         if (mday < 70) {
  1141             temp = year;
  1142             year = mon + 1900;
  1143             mon = mday;
  1144             mday = temp;
  1145         } else {
  1146             goto syntax;
  1148     } else { /* (c) year/month/day */
  1149         if (mday < 70) {
  1150             temp = year;
  1151             year = mon;
  1152             mon = mday;
  1153             mday = temp;
  1154         } else {
  1155             goto syntax;
  1158     mon -= 1; /* convert month to 0-based */
  1159     if (sec < 0)
  1160         sec = 0;
  1161     if (min < 0)
  1162         min = 0;
  1163     if (hour < 0)
  1164         hour = 0;
  1166     msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
  1168     if (tzoffset == -1) { /* no time zone specified, have to use local */
  1169         msec = UTC(msec, dtInfo);
  1170     } else {
  1171         msec += tzoffset * msPerMinute;
  1174     *result = msec;
  1175     return true;
  1177 syntax:
  1178     /* syntax error */
  1179     *result = 0;
  1180     return false;
  1183 static bool
  1184 date_parse(JSContext *cx, unsigned argc, Value *vp)
  1186     CallArgs args = CallArgsFromVp(argc, vp);
  1187     if (args.length() == 0) {
  1188         args.rval().setNaN();
  1189         return true;
  1192     JSString *str = ToString<CanGC>(cx, args[0]);
  1193     if (!str)
  1194         return false;
  1196     JSLinearString *linearStr = str->ensureLinear(cx);
  1197     if (!linearStr)
  1198         return false;
  1200     double result;
  1201     if (!date_parseString(linearStr, &result, &cx->runtime()->dateTimeInfo)) {
  1202         args.rval().setNaN();
  1203         return true;
  1206     result = TimeClip(result);
  1207     args.rval().setNumber(result);
  1208     return true;
  1211 static inline double
  1212 NowAsMillis()
  1214     return (double) (PRMJ_Now() / PRMJ_USEC_PER_MSEC);
  1217 static bool
  1218 date_now(JSContext *cx, unsigned argc, Value *vp)
  1220     CallArgs args = CallArgsFromVp(argc, vp);
  1221     args.rval().setDouble(NowAsMillis());
  1222     return true;
  1225 void
  1226 DateObject::setUTCTime(double t, Value *vp)
  1228     for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
  1229         setReservedSlot(ind, UndefinedValue());
  1231     setFixedSlot(UTC_TIME_SLOT, DoubleValue(t));
  1232     if (vp)
  1233         vp->setDouble(t);
  1236 void
  1237 DateObject::fillLocalTimeSlots(DateTimeInfo *dtInfo)
  1239     /* Check if the cache is already populated. */
  1240     if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() &&
  1241         getReservedSlot(TZA_SLOT).toDouble() == dtInfo->localTZA())
  1243         return;
  1246     /* Remember timezone used to generate the local cache. */
  1247     setReservedSlot(TZA_SLOT, DoubleValue(dtInfo->localTZA()));
  1249     double utcTime = UTCTime().toNumber();
  1251     if (!IsFinite(utcTime)) {
  1252         for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
  1253             setReservedSlot(ind, DoubleValue(utcTime));
  1254         return;
  1257     double localTime = LocalTime(utcTime, dtInfo);
  1259     setReservedSlot(LOCAL_TIME_SLOT, DoubleValue(localTime));
  1261     int year = (int) floor(localTime /(msPerDay * 365.2425)) + 1970;
  1262     double yearStartTime = TimeFromYear(year);
  1264     /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
  1265     int yearDays;
  1266     if (yearStartTime > localTime) {
  1267         year--;
  1268         yearStartTime -= (msPerDay * DaysInYear(year));
  1269         yearDays = DaysInYear(year);
  1270     } else {
  1271         yearDays = DaysInYear(year);
  1272         double nextStart = yearStartTime + (msPerDay * yearDays);
  1273         if (nextStart <= localTime) {
  1274             year++;
  1275             yearStartTime = nextStart;
  1276             yearDays = DaysInYear(year);
  1280     setReservedSlot(LOCAL_YEAR_SLOT, Int32Value(year));
  1282     uint64_t yearTime = uint64_t(localTime - yearStartTime);
  1283     int yearSeconds = uint32_t(yearTime / 1000);
  1285     int day = yearSeconds / int(SecondsPerDay);
  1287     int step = -1, next = 30;
  1288     int month;
  1290     do {
  1291         if (day <= next) {
  1292             month = 0;
  1293             break;
  1295         step = next;
  1296         next += ((yearDays == 366) ? 29 : 28);
  1297         if (day <= next) {
  1298             month = 1;
  1299             break;
  1301         step = next;
  1302         if (day <= (next += 31)) {
  1303             month = 2;
  1304             break;
  1306         step = next;
  1307         if (day <= (next += 30)) {
  1308             month = 3;
  1309             break;
  1311         step = next;
  1312         if (day <= (next += 31)) {
  1313             month = 4;
  1314             break;
  1316         step = next;
  1317         if (day <= (next += 30)) {
  1318             month = 5;
  1319             break;
  1321         step = next;
  1322         if (day <= (next += 31)) {
  1323             month = 6;
  1324             break;
  1326         step = next;
  1327         if (day <= (next += 31)) {
  1328             month = 7;
  1329             break;
  1331         step = next;
  1332         if (day <= (next += 30)) {
  1333             month = 8;
  1334             break;
  1336         step = next;
  1337         if (day <= (next += 31)) {
  1338             month = 9;
  1339             break;
  1341         step = next;
  1342         if (day <= (next += 30)) {
  1343             month = 10;
  1344             break;
  1346         step = next;
  1347         month = 11;
  1348     } while (0);
  1350     setReservedSlot(LOCAL_MONTH_SLOT, Int32Value(month));
  1351     setReservedSlot(LOCAL_DATE_SLOT, Int32Value(day - step));
  1353     int weekday = WeekDay(localTime);
  1354     setReservedSlot(LOCAL_DAY_SLOT, Int32Value(weekday));
  1356     int seconds = yearSeconds % 60;
  1357     setReservedSlot(LOCAL_SECONDS_SLOT, Int32Value(seconds));
  1359     int minutes = (yearSeconds / 60) % 60;
  1360     setReservedSlot(LOCAL_MINUTES_SLOT, Int32Value(minutes));
  1362     int hours = (yearSeconds / (60 * 60)) % 24;
  1363     setReservedSlot(LOCAL_HOURS_SLOT, Int32Value(hours));
  1366 inline double
  1367 DateObject::cachedLocalTime(DateTimeInfo *dtInfo)
  1369     fillLocalTimeSlots(dtInfo);
  1370     return getReservedSlot(LOCAL_TIME_SLOT).toDouble();
  1373 MOZ_ALWAYS_INLINE bool
  1374 IsDate(HandleValue v)
  1376     return v.isObject() && v.toObject().is<DateObject>();
  1379 /*
  1380  * See ECMA 15.9.5.4 thru 15.9.5.23
  1381  */
  1382 /* static */ MOZ_ALWAYS_INLINE bool
  1383 DateObject::getTime_impl(JSContext *cx, CallArgs args)
  1385     args.rval().set(args.thisv().toObject().as<DateObject>().UTCTime());
  1386     return true;
  1389 static bool
  1390 date_getTime(JSContext *cx, unsigned argc, Value *vp)
  1392     CallArgs args = CallArgsFromVp(argc, vp);
  1393     return CallNonGenericMethod<IsDate, DateObject::getTime_impl>(cx, args);
  1396 /* static */ MOZ_ALWAYS_INLINE bool
  1397 DateObject::getYear_impl(JSContext *cx, CallArgs args)
  1399     DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
  1400     dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
  1402     Value yearVal = dateObj->getReservedSlot(LOCAL_YEAR_SLOT);
  1403     if (yearVal.isInt32()) {
  1404         /* Follow ECMA-262 to the letter, contrary to IE JScript. */
  1405         int year = yearVal.toInt32() - 1900;
  1406         args.rval().setInt32(year);
  1407     } else {
  1408         args.rval().set(yearVal);
  1411     return true;
  1414 static bool
  1415 date_getYear(JSContext *cx, unsigned argc, Value *vp)
  1417     CallArgs args = CallArgsFromVp(argc, vp);
  1418     return CallNonGenericMethod<IsDate, DateObject::getYear_impl>(cx, args);
  1421 /* static */ MOZ_ALWAYS_INLINE bool
  1422 DateObject::getFullYear_impl(JSContext *cx, CallArgs args)
  1424     DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
  1425     dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
  1427     args.rval().set(dateObj->getReservedSlot(LOCAL_YEAR_SLOT));
  1428     return true;
  1431 static bool
  1432 date_getFullYear(JSContext *cx, unsigned argc, Value *vp)
  1434     CallArgs args = CallArgsFromVp(argc, vp);
  1435     return CallNonGenericMethod<IsDate, DateObject::getFullYear_impl>(cx, args);
  1438 /* static */ MOZ_ALWAYS_INLINE bool
  1439 DateObject::getUTCFullYear_impl(JSContext *cx, CallArgs args)
  1441     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
  1442     if (IsFinite(result))
  1443         result = YearFromTime(result);
  1445     args.rval().setNumber(result);
  1446     return true;
  1449 static bool
  1450 date_getUTCFullYear(JSContext *cx, unsigned argc, Value *vp)
  1452     CallArgs args = CallArgsFromVp(argc, vp);
  1453     return CallNonGenericMethod<IsDate, DateObject::getUTCFullYear_impl>(cx, args);
  1456 /* static */ MOZ_ALWAYS_INLINE bool
  1457 DateObject::getMonth_impl(JSContext *cx, CallArgs args)
  1459     DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
  1460     dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
  1462     args.rval().set(dateObj->getReservedSlot(LOCAL_MONTH_SLOT));
  1463     return true;
  1466 static bool
  1467 date_getMonth(JSContext *cx, unsigned argc, Value *vp)
  1469     CallArgs args = CallArgsFromVp(argc, vp);
  1470     return CallNonGenericMethod<IsDate, DateObject::getMonth_impl>(cx, args);
  1473 /* static */ MOZ_ALWAYS_INLINE bool
  1474 DateObject::getUTCMonth_impl(JSContext *cx, CallArgs args)
  1476     double d = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
  1477     args.rval().setNumber(MonthFromTime(d));
  1478     return true;
  1481 static bool
  1482 date_getUTCMonth(JSContext *cx, unsigned argc, Value *vp)
  1484     CallArgs args = CallArgsFromVp(argc, vp);
  1485     return CallNonGenericMethod<IsDate, DateObject::getUTCMonth_impl>(cx, args);
  1488 /* static */ MOZ_ALWAYS_INLINE bool
  1489 DateObject::getDate_impl(JSContext *cx, CallArgs args)
  1491     DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
  1492     dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
  1494     args.rval().set(dateObj->getReservedSlot(LOCAL_DATE_SLOT));
  1495     return true;
  1498 static bool
  1499 date_getDate(JSContext *cx, unsigned argc, Value *vp)
  1501     CallArgs args = CallArgsFromVp(argc, vp);
  1502     return CallNonGenericMethod<IsDate, DateObject::getDate_impl>(cx, args);
  1505 /* static */ MOZ_ALWAYS_INLINE bool
  1506 DateObject::getUTCDate_impl(JSContext *cx, CallArgs args)
  1508     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
  1509     if (IsFinite(result))
  1510         result = DateFromTime(result);
  1512     args.rval().setNumber(result);
  1513     return true;
  1516 static bool
  1517 date_getUTCDate(JSContext *cx, unsigned argc, Value *vp)
  1519     CallArgs args = CallArgsFromVp(argc, vp);
  1520     return CallNonGenericMethod<IsDate, DateObject::getUTCDate_impl>(cx, args);
  1523 /* static */ MOZ_ALWAYS_INLINE bool
  1524 DateObject::getDay_impl(JSContext *cx, CallArgs args)
  1526     DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
  1527     dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
  1529     args.rval().set(dateObj->getReservedSlot(LOCAL_DAY_SLOT));
  1530     return true;
  1533 static bool
  1534 date_getDay(JSContext *cx, unsigned argc, Value *vp)
  1536     CallArgs args = CallArgsFromVp(argc, vp);
  1537     return CallNonGenericMethod<IsDate, DateObject::getDay_impl>(cx, args);
  1540 /* static */ MOZ_ALWAYS_INLINE bool
  1541 DateObject::getUTCDay_impl(JSContext *cx, CallArgs args)
  1543     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
  1544     if (IsFinite(result))
  1545         result = WeekDay(result);
  1547     args.rval().setNumber(result);
  1548     return true;
  1551 static bool
  1552 date_getUTCDay(JSContext *cx, unsigned argc, Value *vp)
  1554     CallArgs args = CallArgsFromVp(argc, vp);
  1555     return CallNonGenericMethod<IsDate, DateObject::getUTCDay_impl>(cx, args);
  1558 /* static */ MOZ_ALWAYS_INLINE bool
  1559 DateObject::getHours_impl(JSContext *cx, CallArgs args)
  1561     DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
  1562     dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
  1564     args.rval().set(dateObj->getReservedSlot(LOCAL_HOURS_SLOT));
  1565     return true;
  1568 static bool
  1569 date_getHours(JSContext *cx, unsigned argc, Value *vp)
  1571     CallArgs args = CallArgsFromVp(argc, vp);
  1572     return CallNonGenericMethod<IsDate, DateObject::getHours_impl>(cx, args);
  1575 /* static */ MOZ_ALWAYS_INLINE bool
  1576 DateObject::getUTCHours_impl(JSContext *cx, CallArgs args)
  1578     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
  1579     if (IsFinite(result))
  1580         result = HourFromTime(result);
  1582     args.rval().setNumber(result);
  1583     return true;
  1586 static bool
  1587 date_getUTCHours(JSContext *cx, unsigned argc, Value *vp)
  1589     CallArgs args = CallArgsFromVp(argc, vp);
  1590     return CallNonGenericMethod<IsDate, DateObject::getUTCHours_impl>(cx, args);
  1593 /* static */ MOZ_ALWAYS_INLINE bool
  1594 DateObject::getMinutes_impl(JSContext *cx, CallArgs args)
  1596     DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
  1597     dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
  1599     args.rval().set(dateObj->getReservedSlot(LOCAL_MINUTES_SLOT));
  1600     return true;
  1603 static bool
  1604 date_getMinutes(JSContext *cx, unsigned argc, Value *vp)
  1606     CallArgs args = CallArgsFromVp(argc, vp);
  1607     return CallNonGenericMethod<IsDate, DateObject::getMinutes_impl>(cx, args);
  1610 /* static */ MOZ_ALWAYS_INLINE bool
  1611 DateObject::getUTCMinutes_impl(JSContext *cx, CallArgs args)
  1613     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
  1614     if (IsFinite(result))
  1615         result = MinFromTime(result);
  1617     args.rval().setNumber(result);
  1618     return true;
  1621 static bool
  1622 date_getUTCMinutes(JSContext *cx, unsigned argc, Value *vp)
  1624     CallArgs args = CallArgsFromVp(argc, vp);
  1625     return CallNonGenericMethod<IsDate, DateObject::getUTCMinutes_impl>(cx, args);
  1628 /* Date.getSeconds is mapped to getUTCSeconds */
  1630 /* static */ MOZ_ALWAYS_INLINE bool
  1631 DateObject::getUTCSeconds_impl(JSContext *cx, CallArgs args)
  1633     DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
  1634     dateObj->fillLocalTimeSlots(&cx->runtime()->dateTimeInfo);
  1636     args.rval().set(dateObj->getReservedSlot(LOCAL_SECONDS_SLOT));
  1637     return true;
  1640 static bool
  1641 date_getUTCSeconds(JSContext *cx, unsigned argc, Value *vp)
  1643     CallArgs args = CallArgsFromVp(argc, vp);
  1644     return CallNonGenericMethod<IsDate, DateObject::getUTCSeconds_impl>(cx, args);
  1647 /* Date.getMilliseconds is mapped to getUTCMilliseconds */
  1649 /* static */ MOZ_ALWAYS_INLINE bool
  1650 DateObject::getUTCMilliseconds_impl(JSContext *cx, CallArgs args)
  1652     double result = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
  1653     if (IsFinite(result))
  1654         result = msFromTime(result);
  1656     args.rval().setNumber(result);
  1657     return true;
  1660 static bool
  1661 date_getUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp)
  1663     CallArgs args = CallArgsFromVp(argc, vp);
  1664     return CallNonGenericMethod<IsDate, DateObject::getUTCMilliseconds_impl>(cx, args);
  1667 /* static */ MOZ_ALWAYS_INLINE bool
  1668 DateObject::getTimezoneOffset_impl(JSContext *cx, CallArgs args)
  1670     DateObject *dateObj = &args.thisv().toObject().as<DateObject>();
  1671     double utctime = dateObj->UTCTime().toNumber();
  1672     double localtime = dateObj->cachedLocalTime(&cx->runtime()->dateTimeInfo);
  1674     /*
  1675      * Return the time zone offset in minutes for the current locale that is
  1676      * appropriate for this time. This value would be a constant except for
  1677      * daylight savings time.
  1678      */
  1679     double result = (utctime - localtime) / msPerMinute;
  1680     args.rval().setNumber(result);
  1681     return true;
  1684 static bool
  1685 date_getTimezoneOffset(JSContext *cx, unsigned argc, Value *vp)
  1687     CallArgs args = CallArgsFromVp(argc, vp);
  1688     return CallNonGenericMethod<IsDate, DateObject::getTimezoneOffset_impl>(cx, args);
  1691 MOZ_ALWAYS_INLINE bool
  1692 date_setTime_impl(JSContext *cx, CallArgs args)
  1694     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  1695     if (args.length() == 0) {
  1696         dateObj->setUTCTime(GenericNaN(), args.rval().address());
  1697         return true;
  1700     double result;
  1701     if (!ToNumber(cx, args[0], &result))
  1702         return false;
  1704     dateObj->setUTCTime(TimeClip(result), args.rval().address());
  1705     return true;
  1708 static bool
  1709 date_setTime(JSContext *cx, unsigned argc, Value *vp)
  1711     CallArgs args = CallArgsFromVp(argc, vp);
  1712     return CallNonGenericMethod<IsDate, date_setTime_impl>(cx, args);
  1715 static bool
  1716 GetMsecsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *millis)
  1718     if (args.length() <= i) {
  1719         *millis = msFromTime(t);
  1720         return true;
  1722     return ToNumber(cx, args[i], millis);
  1725 static bool
  1726 GetSecsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *sec)
  1728     if (args.length() <= i) {
  1729         *sec = SecFromTime(t);
  1730         return true;
  1732     return ToNumber(cx, args[i], sec);
  1735 static bool
  1736 GetMinsOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *mins)
  1738     if (args.length() <= i) {
  1739         *mins = MinFromTime(t);
  1740         return true;
  1742     return ToNumber(cx, args[i], mins);
  1745 /* ES5 15.9.5.28. */
  1746 MOZ_ALWAYS_INLINE bool
  1747 date_setMilliseconds_impl(JSContext *cx, CallArgs args)
  1749     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  1751     /* Step 1. */
  1752     double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
  1754     /* Step 2. */
  1755     double milli;
  1756     if (!ToNumber(cx, args.get(0), &milli))
  1757         return false;
  1758     double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
  1760     /* Step 3. */
  1761     double u = TimeClip(UTC(MakeDate(Day(t), time), &cx->runtime()->dateTimeInfo));
  1763     /* Steps 4-5. */
  1764     dateObj->setUTCTime(u, args.rval().address());
  1765     return true;
  1768 static bool
  1769 date_setMilliseconds(JSContext *cx, unsigned argc, Value *vp)
  1771     CallArgs args = CallArgsFromVp(argc, vp);
  1772     return CallNonGenericMethod<IsDate, date_setMilliseconds_impl>(cx, args);
  1775 /* ES5 15.9.5.29. */
  1776 MOZ_ALWAYS_INLINE bool
  1777 date_setUTCMilliseconds_impl(JSContext *cx, CallArgs args)
  1779     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  1781     /* Step 1. */
  1782     double t = dateObj->UTCTime().toNumber();
  1784     /* Step 2. */
  1785     double milli;
  1786     if (!ToNumber(cx, args.get(0), &milli))
  1787         return false;
  1788     double time = MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), milli);
  1790     /* Step 3. */
  1791     double v = TimeClip(MakeDate(Day(t), time));
  1793     /* Steps 4-5. */
  1794     dateObj->setUTCTime(v, args.rval().address());
  1795     return true;
  1798 static bool
  1799 date_setUTCMilliseconds(JSContext *cx, unsigned argc, Value *vp)
  1801     CallArgs args = CallArgsFromVp(argc, vp);
  1802     return CallNonGenericMethod<IsDate, date_setUTCMilliseconds_impl>(cx, args);
  1805 /* ES5 15.9.5.30. */
  1806 MOZ_ALWAYS_INLINE bool
  1807 date_setSeconds_impl(JSContext *cx, CallArgs args)
  1809     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  1811     /* Step 1. */
  1812     double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
  1814     /* Step 2. */
  1815     double s;
  1816     if (!ToNumber(cx, args.get(0), &s))
  1817         return false;
  1819     /* Step 3. */
  1820     double milli;
  1821     if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
  1822         return false;
  1824     /* Step 4. */
  1825     double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
  1827     /* Step 5. */
  1828     double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
  1830     /* Steps 6-7. */
  1831     dateObj->setUTCTime(u, args.rval().address());
  1832     return true;
  1835 /* ES5 15.9.5.31. */
  1836 static bool
  1837 date_setSeconds(JSContext *cx, unsigned argc, Value *vp)
  1839     CallArgs args = CallArgsFromVp(argc, vp);
  1840     return CallNonGenericMethod<IsDate, date_setSeconds_impl>(cx, args);
  1843 MOZ_ALWAYS_INLINE bool
  1844 date_setUTCSeconds_impl(JSContext *cx, CallArgs args)
  1846     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  1848     /* Step 1. */
  1849     double t = dateObj->UTCTime().toNumber();
  1851     /* Step 2. */
  1852     double s;
  1853     if (!ToNumber(cx, args.get(0), &s))
  1854         return false;
  1856     /* Step 3. */
  1857     double milli;
  1858     if (!GetMsecsOrDefault(cx, args, 1, t, &milli))
  1859         return false;
  1861     /* Step 4. */
  1862     double date = MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), s, milli));
  1864     /* Step 5. */
  1865     double v = TimeClip(date);
  1867     /* Steps 6-7. */
  1868     dateObj->setUTCTime(v, args.rval().address());
  1869     return true;
  1872 /* ES5 15.9.5.32. */
  1873 static bool
  1874 date_setUTCSeconds(JSContext *cx, unsigned argc, Value *vp)
  1876     CallArgs args = CallArgsFromVp(argc, vp);
  1877     return CallNonGenericMethod<IsDate, date_setUTCSeconds_impl>(cx, args);
  1880 MOZ_ALWAYS_INLINE bool
  1881 date_setMinutes_impl(JSContext *cx, CallArgs args)
  1883     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  1885     /* Step 1. */
  1886     double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
  1888     /* Step 2. */
  1889     double m;
  1890     if (!ToNumber(cx, args.get(0), &m))
  1891         return false;
  1893     /* Step 3. */
  1894     double s;
  1895     if (!GetSecsOrDefault(cx, args, 1, t, &s))
  1896         return false;
  1898     /* Step 4. */
  1899     double milli;
  1900     if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
  1901         return false;
  1903     /* Step 5. */
  1904     double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
  1906     /* Step 6. */
  1907     double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
  1909     /* Steps 7-8. */
  1910     dateObj->setUTCTime(u, args.rval().address());
  1911     return true;
  1914 /* ES5 15.9.5.33. */
  1915 static bool
  1916 date_setMinutes(JSContext *cx, unsigned argc, Value *vp)
  1918     CallArgs args = CallArgsFromVp(argc, vp);
  1919     return CallNonGenericMethod<IsDate, date_setMinutes_impl>(cx, args);
  1922 MOZ_ALWAYS_INLINE bool
  1923 date_setUTCMinutes_impl(JSContext *cx, CallArgs args)
  1925     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  1927     /* Step 1. */
  1928     double t = dateObj->UTCTime().toNumber();
  1930     /* Step 2. */
  1931     double m;
  1932     if (!ToNumber(cx, args.get(0), &m))
  1933         return false;
  1935     /* Step 3. */
  1936     double s;
  1937     if (!GetSecsOrDefault(cx, args, 1, t, &s))
  1938         return false;
  1940     /* Step 4. */
  1941     double milli;
  1942     if (!GetMsecsOrDefault(cx, args, 2, t, &milli))
  1943         return false;
  1945     /* Step 5. */
  1946     double date = MakeDate(Day(t), MakeTime(HourFromTime(t), m, s, milli));
  1948     /* Step 6. */
  1949     double v = TimeClip(date);
  1951     /* Steps 7-8. */
  1952     dateObj->setUTCTime(v, args.rval().address());
  1953     return true;
  1956 /* ES5 15.9.5.34. */
  1957 static bool
  1958 date_setUTCMinutes(JSContext *cx, unsigned argc, Value *vp)
  1960     CallArgs args = CallArgsFromVp(argc, vp);
  1961     return CallNonGenericMethod<IsDate, date_setUTCMinutes_impl>(cx, args);
  1964 MOZ_ALWAYS_INLINE bool
  1965 date_setHours_impl(JSContext *cx, CallArgs args)
  1967     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  1969     /* Step 1. */
  1970     double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
  1972     /* Step 2. */
  1973     double h;
  1974     if (!ToNumber(cx, args.get(0), &h))
  1975         return false;
  1977     /* Step 3. */
  1978     double m;
  1979     if (!GetMinsOrDefault(cx, args, 1, t, &m))
  1980         return false;
  1982     /* Step 4. */
  1983     double s;
  1984     if (!GetSecsOrDefault(cx, args, 2, t, &s))
  1985         return false;
  1987     /* Step 5. */
  1988     double milli;
  1989     if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
  1990         return false;
  1992     /* Step 6. */
  1993     double date = MakeDate(Day(t), MakeTime(h, m, s, milli));
  1995     /* Step 6. */
  1996     double u = TimeClip(UTC(date, &cx->runtime()->dateTimeInfo));
  1998     /* Steps 7-8. */
  1999     dateObj->setUTCTime(u, args.rval().address());
  2000     return true;
  2003 /* ES5 15.9.5.35. */
  2004 static bool
  2005 date_setHours(JSContext *cx, unsigned argc, Value *vp)
  2007     CallArgs args = CallArgsFromVp(argc, vp);
  2008     return CallNonGenericMethod<IsDate, date_setHours_impl>(cx, args);
  2011 MOZ_ALWAYS_INLINE bool
  2012 date_setUTCHours_impl(JSContext *cx, CallArgs args)
  2014     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2016     /* Step 1. */
  2017     double t = dateObj->UTCTime().toNumber();
  2019     /* Step 2. */
  2020     double h;
  2021     if (!ToNumber(cx, args.get(0), &h))
  2022         return false;
  2024     /* Step 3. */
  2025     double m;
  2026     if (!GetMinsOrDefault(cx, args, 1, t, &m))
  2027         return false;
  2029     /* Step 4. */
  2030     double s;
  2031     if (!GetSecsOrDefault(cx, args, 2, t, &s))
  2032         return false;
  2034     /* Step 5. */
  2035     double milli;
  2036     if (!GetMsecsOrDefault(cx, args, 3, t, &milli))
  2037         return false;
  2039     /* Step 6. */
  2040     double newDate = MakeDate(Day(t), MakeTime(h, m, s, milli));
  2042     /* Step 7. */
  2043     double v = TimeClip(newDate);
  2045     /* Steps 8-9. */
  2046     dateObj->setUTCTime(v, args.rval().address());
  2047     return true;
  2050 /* ES5 15.9.5.36. */
  2051 static bool
  2052 date_setUTCHours(JSContext *cx, unsigned argc, Value *vp)
  2054     CallArgs args = CallArgsFromVp(argc, vp);
  2055     return CallNonGenericMethod<IsDate, date_setUTCHours_impl>(cx, args);
  2058 MOZ_ALWAYS_INLINE bool
  2059 date_setDate_impl(JSContext *cx, CallArgs args)
  2061     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2063     /* Step 1. */
  2064     double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
  2066     /* Step 2. */
  2067     double date;
  2068     if (!ToNumber(cx, args.get(0), &date))
  2069         return false;
  2071     /* Step 3. */
  2072     double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
  2074     /* Step 4. */
  2075     double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
  2077     /* Steps 5-6. */
  2078     dateObj->setUTCTime(u, args.rval().address());
  2079     return true;
  2082 /* ES5 15.9.5.37. */
  2083 static bool
  2084 date_setDate(JSContext *cx, unsigned argc, Value *vp)
  2086     CallArgs args = CallArgsFromVp(argc, vp);
  2087     return CallNonGenericMethod<IsDate, date_setDate_impl>(cx, args);
  2090 MOZ_ALWAYS_INLINE bool
  2091 date_setUTCDate_impl(JSContext *cx, CallArgs args)
  2093     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2095     /* Step 1. */
  2096     double t = dateObj->UTCTime().toNumber();
  2098     /* Step 2. */
  2099     double date;
  2100     if (!ToNumber(cx, args.get(0), &date))
  2101         return false;
  2103     /* Step 3. */
  2104     double newDate = MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t));
  2106     /* Step 4. */
  2107     double v = TimeClip(newDate);
  2109     /* Steps 5-6. */
  2110     dateObj->setUTCTime(v, args.rval().address());
  2111     return true;
  2114 static bool
  2115 date_setUTCDate(JSContext *cx, unsigned argc, Value *vp)
  2117     CallArgs args = CallArgsFromVp(argc, vp);
  2118     return CallNonGenericMethod<IsDate, date_setUTCDate_impl>(cx, args);
  2121 static bool
  2122 GetDateOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *date)
  2124     if (args.length() <= i) {
  2125         *date = DateFromTime(t);
  2126         return true;
  2128     return ToNumber(cx, args[i], date);
  2131 static bool
  2132 GetMonthOrDefault(JSContext *cx, const CallArgs &args, unsigned i, double t, double *month)
  2134     if (args.length() <= i) {
  2135         *month = MonthFromTime(t);
  2136         return true;
  2138     return ToNumber(cx, args[i], month);
  2141 /* ES5 15.9.5.38. */
  2142 MOZ_ALWAYS_INLINE bool
  2143 date_setMonth_impl(JSContext *cx, CallArgs args)
  2145     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2147     /* Step 1. */
  2148     double t = LocalTime(dateObj->UTCTime().toNumber(), &cx->runtime()->dateTimeInfo);
  2150     /* Step 2. */
  2151     double m;
  2152     if (!ToNumber(cx, args.get(0), &m))
  2153         return false;
  2155     /* Step 3. */
  2156     double date;
  2157     if (!GetDateOrDefault(cx, args, 1, t, &date))
  2158         return false;
  2160     /* Step 4. */
  2161     double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
  2163     /* Step 5. */
  2164     double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
  2166     /* Steps 6-7. */
  2167     dateObj->setUTCTime(u, args.rval().address());
  2168     return true;
  2171 static bool
  2172 date_setMonth(JSContext *cx, unsigned argc, Value *vp)
  2174     CallArgs args = CallArgsFromVp(argc, vp);
  2175     return CallNonGenericMethod<IsDate, date_setMonth_impl>(cx, args);
  2178 /* ES5 15.9.5.39. */
  2179 MOZ_ALWAYS_INLINE bool
  2180 date_setUTCMonth_impl(JSContext *cx, CallArgs args)
  2182     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2184     /* Step 1. */
  2185     double t = dateObj->UTCTime().toNumber();
  2187     /* Step 2. */
  2188     double m;
  2189     if (!ToNumber(cx, args.get(0), &m))
  2190         return false;
  2192     /* Step 3. */
  2193     double date;
  2194     if (!GetDateOrDefault(cx, args, 1, t, &date))
  2195         return false;
  2197     /* Step 4. */
  2198     double newDate = MakeDate(MakeDay(YearFromTime(t), m, date), TimeWithinDay(t));
  2200     /* Step 5. */
  2201     double v = TimeClip(newDate);
  2203     /* Steps 6-7. */
  2204     dateObj->setUTCTime(v, args.rval().address());
  2205     return true;
  2208 static bool
  2209 date_setUTCMonth(JSContext *cx, unsigned argc, Value *vp)
  2211     CallArgs args = CallArgsFromVp(argc, vp);
  2212     return CallNonGenericMethod<IsDate, date_setUTCMonth_impl>(cx, args);
  2215 static double
  2216 ThisLocalTimeOrZero(Handle<DateObject*> dateObj, DateTimeInfo *dtInfo)
  2218     double t = dateObj->UTCTime().toNumber();
  2219     if (IsNaN(t))
  2220         return +0;
  2221     return LocalTime(t, dtInfo);
  2224 static double
  2225 ThisUTCTimeOrZero(Handle<DateObject*> dateObj)
  2227     double t = dateObj->as<DateObject>().UTCTime().toNumber();
  2228     return IsNaN(t) ? +0 : t;
  2231 /* ES5 15.9.5.40. */
  2232 MOZ_ALWAYS_INLINE bool
  2233 date_setFullYear_impl(JSContext *cx, CallArgs args)
  2235     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2237     /* Step 1. */
  2238     double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
  2240     /* Step 2. */
  2241     double y;
  2242     if (!ToNumber(cx, args.get(0), &y))
  2243         return false;
  2245     /* Step 3. */
  2246     double m;
  2247     if (!GetMonthOrDefault(cx, args, 1, t, &m))
  2248         return false;
  2250     /* Step 4. */
  2251     double date;
  2252     if (!GetDateOrDefault(cx, args, 2, t, &date))
  2253         return false;
  2255     /* Step 5. */
  2256     double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
  2258     /* Step 6. */
  2259     double u = TimeClip(UTC(newDate, &cx->runtime()->dateTimeInfo));
  2261     /* Steps 7-8. */
  2262     dateObj->setUTCTime(u, args.rval().address());
  2263     return true;
  2266 static bool
  2267 date_setFullYear(JSContext *cx, unsigned argc, Value *vp)
  2269     CallArgs args = CallArgsFromVp(argc, vp);
  2270     return CallNonGenericMethod<IsDate, date_setFullYear_impl>(cx, args);
  2273 /* ES5 15.9.5.41. */
  2274 MOZ_ALWAYS_INLINE bool
  2275 date_setUTCFullYear_impl(JSContext *cx, CallArgs args)
  2277     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2279     /* Step 1. */
  2280     double t = ThisUTCTimeOrZero(dateObj);
  2282     /* Step 2. */
  2283     double y;
  2284     if (!ToNumber(cx, args.get(0), &y))
  2285         return false;
  2287     /* Step 3. */
  2288     double m;
  2289     if (!GetMonthOrDefault(cx, args, 1, t, &m))
  2290         return false;
  2292     /* Step 4. */
  2293     double date;
  2294     if (!GetDateOrDefault(cx, args, 2, t, &date))
  2295         return false;
  2297     /* Step 5. */
  2298     double newDate = MakeDate(MakeDay(y, m, date), TimeWithinDay(t));
  2300     /* Step 6. */
  2301     double v = TimeClip(newDate);
  2303     /* Steps 7-8. */
  2304     dateObj->setUTCTime(v, args.rval().address());
  2305     return true;
  2308 static bool
  2309 date_setUTCFullYear(JSContext *cx, unsigned argc, Value *vp)
  2311     CallArgs args = CallArgsFromVp(argc, vp);
  2312     return CallNonGenericMethod<IsDate, date_setUTCFullYear_impl>(cx, args);
  2315 /* ES5 Annex B.2.5. */
  2316 MOZ_ALWAYS_INLINE bool
  2317 date_setYear_impl(JSContext *cx, CallArgs args)
  2319     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2321     /* Step 1. */
  2322     double t = ThisLocalTimeOrZero(dateObj, &cx->runtime()->dateTimeInfo);
  2324     /* Step 2. */
  2325     double y;
  2326     if (!ToNumber(cx, args.get(0), &y))
  2327         return false;
  2329     /* Step 3. */
  2330     if (IsNaN(y)) {
  2331         dateObj->setUTCTime(GenericNaN(), args.rval().address());
  2332         return true;
  2335     /* Step 4. */
  2336     double yint = ToInteger(y);
  2337     if (0 <= yint && yint <= 99)
  2338         yint += 1900;
  2340     /* Step 5. */
  2341     double day = MakeDay(yint, MonthFromTime(t), DateFromTime(t));
  2343     /* Step 6. */
  2344     double u = UTC(MakeDate(day, TimeWithinDay(t)), &cx->runtime()->dateTimeInfo);
  2346     /* Steps 7-8. */
  2347     dateObj->setUTCTime(TimeClip(u), args.rval().address());
  2348     return true;
  2351 static bool
  2352 date_setYear(JSContext *cx, unsigned argc, Value *vp)
  2354     CallArgs args = CallArgsFromVp(argc, vp);
  2355     return CallNonGenericMethod<IsDate, date_setYear_impl>(cx, args);
  2358 /* constants for toString, toUTCString */
  2359 static const char js_NaN_date_str[] = "Invalid Date";
  2360 static const char * const days[] =
  2362    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
  2363 };
  2364 static const char * const months[] =
  2366    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  2367 };
  2370 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
  2371 // requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
  2372 static void
  2373 print_gmt_string(char* buf, size_t size, double utctime)
  2375     JS_ASSERT(TimeClip(utctime) == utctime);
  2376     JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
  2377                 days[int(WeekDay(utctime))],
  2378                 int(DateFromTime(utctime)),
  2379                 months[int(MonthFromTime(utctime))],
  2380                 int(YearFromTime(utctime)),
  2381                 int(HourFromTime(utctime)),
  2382                 int(MinFromTime(utctime)),
  2383                 int(SecFromTime(utctime)));
  2386 static void
  2387 print_iso_string(char* buf, size_t size, double utctime)
  2389     JS_ASSERT(TimeClip(utctime) == utctime);
  2390     JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
  2391                 int(YearFromTime(utctime)),
  2392                 int(MonthFromTime(utctime)) + 1,
  2393                 int(DateFromTime(utctime)),
  2394                 int(HourFromTime(utctime)),
  2395                 int(MinFromTime(utctime)),
  2396                 int(SecFromTime(utctime)),
  2397                 int(msFromTime(utctime)));
  2400 /* ES5 B.2.6. */
  2401 MOZ_ALWAYS_INLINE bool
  2402 date_toGMTString_impl(JSContext *cx, CallArgs args)
  2404     double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
  2406     char buf[100];
  2407     if (!IsFinite(utctime))
  2408         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
  2409     else
  2410         print_gmt_string(buf, sizeof buf, utctime);
  2412     JSString *str = JS_NewStringCopyZ(cx, buf);
  2413     if (!str)
  2414         return false;
  2415     args.rval().setString(str);
  2416     return true;
  2419 /* ES5 15.9.5.43. */
  2420 static bool
  2421 date_toGMTString(JSContext *cx, unsigned argc, Value *vp)
  2423     CallArgs args = CallArgsFromVp(argc, vp);
  2424     return CallNonGenericMethod<IsDate, date_toGMTString_impl>(cx, args);
  2427 MOZ_ALWAYS_INLINE bool
  2428 date_toISOString_impl(JSContext *cx, CallArgs args)
  2430     double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
  2431     if (!IsFinite(utctime)) {
  2432         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INVALID_DATE);
  2433         return false;
  2436     char buf[100];
  2437     print_iso_string(buf, sizeof buf, utctime);
  2439     JSString *str = JS_NewStringCopyZ(cx, buf);
  2440     if (!str)
  2441         return false;
  2442     args.rval().setString(str);
  2443     return true;
  2447 static bool
  2448 date_toISOString(JSContext *cx, unsigned argc, Value *vp)
  2450     CallArgs args = CallArgsFromVp(argc, vp);
  2451     return CallNonGenericMethod<IsDate, date_toISOString_impl>(cx, args);
  2454 /* ES5 15.9.5.44. */
  2455 static bool
  2456 date_toJSON(JSContext *cx, unsigned argc, Value *vp)
  2458     CallArgs args = CallArgsFromVp(argc, vp);
  2460     /* Step 1. */
  2461     RootedObject obj(cx, ToObject(cx, args.thisv()));
  2462     if (!obj)
  2463         return false;
  2465     /* Step 2. */
  2466     RootedValue tv(cx, ObjectValue(*obj));
  2467     if (!ToPrimitive(cx, JSTYPE_NUMBER, &tv))
  2468         return false;
  2470     /* Step 3. */
  2471     if (tv.isDouble() && !IsFinite(tv.toDouble())) {
  2472         args.rval().setNull();
  2473         return true;
  2476     /* Step 4. */
  2477     RootedValue toISO(cx);
  2478     if (!JSObject::getProperty(cx, obj, obj, cx->names().toISOString, &toISO))
  2479         return false;
  2481     /* Step 5. */
  2482     if (!js_IsCallable(toISO)) {
  2483         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr,
  2484                                      JSMSG_BAD_TOISOSTRING_PROP);
  2485         return false;
  2488     /* Step 6. */
  2489     InvokeArgs args2(cx);
  2490     if (!args2.init(0))
  2491         return false;
  2493     args2.setCallee(toISO);
  2494     args2.setThis(ObjectValue(*obj));
  2496     if (!Invoke(cx, args2))
  2497         return false;
  2498     args.rval().set(args2.rval());
  2499     return true;
  2502 /* for Date.toLocaleFormat; interface to PRMJTime date struct.
  2503  */
  2504 static void
  2505 new_explode(double timeval, PRMJTime *split, DateTimeInfo *dtInfo)
  2507     double year = YearFromTime(timeval);
  2509     split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
  2510     split->tm_sec = int8_t(SecFromTime(timeval));
  2511     split->tm_min = int8_t(MinFromTime(timeval));
  2512     split->tm_hour = int8_t(HourFromTime(timeval));
  2513     split->tm_mday = int8_t(DateFromTime(timeval));
  2514     split->tm_mon = int8_t(MonthFromTime(timeval));
  2515     split->tm_wday = int8_t(WeekDay(timeval));
  2516     split->tm_year = year;
  2517     split->tm_yday = int16_t(DayWithinYear(timeval, year));
  2519     /* not sure how this affects things, but it doesn't seem
  2520        to matter. */
  2521     split->tm_isdst = (DaylightSavingTA(timeval, dtInfo) != 0);
  2524 typedef enum formatspec {
  2525     FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
  2526 } formatspec;
  2528 /* helper function */
  2529 static bool
  2530 date_format(JSContext *cx, double date, formatspec format, MutableHandleValue rval)
  2532     char buf[100];
  2533     char tzbuf[100];
  2534     bool usetz;
  2535     size_t i, tzlen;
  2536     PRMJTime split;
  2538     if (!IsFinite(date)) {
  2539         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
  2540     } else {
  2541         JS_ASSERT(TimeClip(date) == date);
  2543         double local = LocalTime(date, &cx->runtime()->dateTimeInfo);
  2545         /* offset from GMT in minutes.  The offset includes daylight savings,
  2546            if it applies. */
  2547         int minutes = (int) floor(AdjustTime(date, &cx->runtime()->dateTimeInfo) / msPerMinute);
  2549         /* map 510 minutes to 0830 hours */
  2550         int offset = (minutes / 60) * 100 + minutes % 60;
  2552         /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
  2553          * printed as 'GMT-0800' rather than as 'PST' to avoid
  2554          * operating-system dependence on strftime (which
  2555          * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
  2556          * PST as 'Pacific Standard Time.'  This way we always know
  2557          * what we're getting, and can parse it if we produce it.
  2558          * The OS TZA string is included as a comment.
  2559          */
  2561         /* get a timezone string from the OS to include as a
  2562            comment. */
  2563         new_explode(date, &split, &cx->runtime()->dateTimeInfo);
  2564         if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
  2566             /* Decide whether to use the resulting timezone string.
  2568              * Reject it if it contains any non-ASCII, non-alphanumeric
  2569              * characters.  It's then likely in some other character
  2570              * encoding, and we probably won't display it correctly.
  2571              */
  2572             usetz = true;
  2573             tzlen = strlen(tzbuf);
  2574             if (tzlen > 100) {
  2575                 usetz = false;
  2576             } else {
  2577                 for (i = 0; i < tzlen; i++) {
  2578                     jschar c = tzbuf[i];
  2579                     if (c > 127 ||
  2580                         !(isalpha(c) || isdigit(c) ||
  2581                           c == ' ' || c == '(' || c == ')')) {
  2582                         usetz = false;
  2587             /* Also reject it if it's not parenthesized or if it's '()'. */
  2588             if (tzbuf[0] != '(' || tzbuf[1] == ')')
  2589                 usetz = false;
  2590         } else
  2591             usetz = false;
  2593         switch (format) {
  2594           case FORMATSPEC_FULL:
  2595             /*
  2596              * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
  2597              * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
  2598              */
  2599             /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
  2600             JS_snprintf(buf, sizeof buf,
  2601                         "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
  2602                         days[int(WeekDay(local))],
  2603                         months[int(MonthFromTime(local))],
  2604                         int(DateFromTime(local)),
  2605                         int(YearFromTime(local)),
  2606                         int(HourFromTime(local)),
  2607                         int(MinFromTime(local)),
  2608                         int(SecFromTime(local)),
  2609                         offset,
  2610                         usetz ? " " : "",
  2611                         usetz ? tzbuf : "");
  2612             break;
  2613           case FORMATSPEC_DATE:
  2614             /* Tue Oct 31 2000 */
  2615             JS_snprintf(buf, sizeof buf,
  2616                         "%s %s %.2d %.4d",
  2617                         days[int(WeekDay(local))],
  2618                         months[int(MonthFromTime(local))],
  2619                         int(DateFromTime(local)),
  2620                         int(YearFromTime(local)));
  2621             break;
  2622           case FORMATSPEC_TIME:
  2623             /* 09:41:40 GMT-0800 (PST) */
  2624             JS_snprintf(buf, sizeof buf,
  2625                         "%.2d:%.2d:%.2d GMT%+.4d%s%s",
  2626                         int(HourFromTime(local)),
  2627                         int(MinFromTime(local)),
  2628                         int(SecFromTime(local)),
  2629                         offset,
  2630                         usetz ? " " : "",
  2631                         usetz ? tzbuf : "");
  2632             break;
  2636     JSString *str = JS_NewStringCopyZ(cx, buf);
  2637     if (!str)
  2638         return false;
  2639     rval.setString(str);
  2640     return true;
  2643 static bool
  2644 ToLocaleFormatHelper(JSContext *cx, HandleObject obj, const char *format, MutableHandleValue rval)
  2646     double utctime = obj->as<DateObject>().UTCTime().toNumber();
  2648     char buf[100];
  2649     if (!IsFinite(utctime)) {
  2650         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
  2651     } else {
  2652         int result_len;
  2653         double local = LocalTime(utctime, &cx->runtime()->dateTimeInfo);
  2654         PRMJTime split;
  2655         new_explode(local, &split, &cx->runtime()->dateTimeInfo);
  2657         /* Let PRMJTime format it. */
  2658         result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
  2660         /* If it failed, default to toString. */
  2661         if (result_len == 0)
  2662             return date_format(cx, utctime, FORMATSPEC_FULL, rval);
  2664         /* Hacked check against undesired 2-digit year 00/00/00 form. */
  2665         if (strcmp(format, "%x") == 0 && result_len >= 6 &&
  2666             /* Format %x means use OS settings, which may have 2-digit yr, so
  2667                hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
  2668             !isdigit(buf[result_len - 3]) &&
  2669             isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
  2670             /* ...but not if starts with 4-digit year, like 2022/3/11. */
  2671             !(isdigit(buf[0]) && isdigit(buf[1]) &&
  2672               isdigit(buf[2]) && isdigit(buf[3]))) {
  2673             JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
  2674                         "%d", js_DateGetYear(cx, obj));
  2679     if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode)
  2680         return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval);
  2682     JSString *str = JS_NewStringCopyZ(cx, buf);
  2683     if (!str)
  2684         return false;
  2685     rval.setString(str);
  2686     return true;
  2689 #if !EXPOSE_INTL_API
  2690 static bool
  2691 ToLocaleStringHelper(JSContext *cx, Handle<DateObject*> dateObj, MutableHandleValue rval)
  2693     /*
  2694      * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
  2695      * with msvc; '%#c' requests that a full year be used in the result string.
  2696      */
  2697     return ToLocaleFormatHelper(cx, dateObj,
  2698 #if defined(_WIN32) && !defined(__MWERKS__)
  2699                           "%#c"
  2700 #else
  2701                           "%c"
  2702 #endif
  2703                          , rval);
  2706 /* ES5 15.9.5.5. */
  2707 MOZ_ALWAYS_INLINE bool
  2708 date_toLocaleString_impl(JSContext *cx, CallArgs args)
  2710     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2711     return ToLocaleStringHelper(cx, dateObj, args.rval());
  2714 static bool
  2715 date_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
  2717     CallArgs args = CallArgsFromVp(argc, vp);
  2718     return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args);
  2721 /* ES5 15.9.5.6. */
  2722 MOZ_ALWAYS_INLINE bool
  2723 date_toLocaleDateString_impl(JSContext *cx, CallArgs args)
  2725     /*
  2726      * Use '%#x' for windows, because '%x' is backward-compatible and non-y2k
  2727      * with msvc; '%#x' requests that a full year be used in the result string.
  2728      */
  2729     static const char format[] =
  2730 #if defined(_WIN32) && !defined(__MWERKS__)
  2731                                    "%#x"
  2732 #else
  2733                                    "%x"
  2734 #endif
  2737     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2738     return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
  2741 static bool
  2742 date_toLocaleDateString(JSContext *cx, unsigned argc, Value *vp)
  2744     CallArgs args = CallArgsFromVp(argc, vp);
  2745     return CallNonGenericMethod<IsDate, date_toLocaleDateString_impl>(cx, args);
  2748 /* ES5 15.9.5.7. */
  2749 MOZ_ALWAYS_INLINE bool
  2750 date_toLocaleTimeString_impl(JSContext *cx, CallArgs args)
  2752     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2753     return ToLocaleFormatHelper(cx, dateObj, "%X", args.rval());
  2756 static bool
  2757 date_toLocaleTimeString(JSContext *cx, unsigned argc, Value *vp)
  2759     CallArgs args = CallArgsFromVp(argc, vp);
  2760     return CallNonGenericMethod<IsDate, date_toLocaleTimeString_impl>(cx, args);
  2762 #endif /* !EXPOSE_INTL_API */
  2764 MOZ_ALWAYS_INLINE bool
  2765 date_toLocaleFormat_impl(JSContext *cx, CallArgs args)
  2767     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2769     if (args.length() == 0) {
  2770         /*
  2771          * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
  2772          * with msvc; '%#c' requests that a full year be used in the result string.
  2773          */
  2774         return ToLocaleFormatHelper(cx, dateObj,
  2775 #if defined(_WIN32) && !defined(__MWERKS__)
  2776                               "%#c"
  2777 #else
  2778                               "%c"
  2779 #endif
  2780                              , args.rval());
  2783     RootedString fmt(cx, ToString<CanGC>(cx, args[0]));
  2784     if (!fmt)
  2785         return false;
  2787     JSAutoByteString fmtbytes(cx, fmt);
  2788     if (!fmtbytes)
  2789         return false;
  2791     return ToLocaleFormatHelper(cx, dateObj, fmtbytes.ptr(), args.rval());
  2794 static bool
  2795 date_toLocaleFormat(JSContext *cx, unsigned argc, Value *vp)
  2797     CallArgs args = CallArgsFromVp(argc, vp);
  2798     return CallNonGenericMethod<IsDate, date_toLocaleFormat_impl>(cx, args);
  2801 /* ES5 15.9.5.4. */
  2802 MOZ_ALWAYS_INLINE bool
  2803 date_toTimeString_impl(JSContext *cx, CallArgs args)
  2805     return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
  2806                        FORMATSPEC_TIME, args.rval());
  2809 static bool
  2810 date_toTimeString(JSContext *cx, unsigned argc, Value *vp)
  2812     CallArgs args = CallArgsFromVp(argc, vp);
  2813     return CallNonGenericMethod<IsDate, date_toTimeString_impl>(cx, args);
  2816 /* ES5 15.9.5.3. */
  2817 MOZ_ALWAYS_INLINE bool
  2818 date_toDateString_impl(JSContext *cx, CallArgs args)
  2820     return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
  2821                        FORMATSPEC_DATE, args.rval());
  2824 static bool
  2825 date_toDateString(JSContext *cx, unsigned argc, Value *vp)
  2827     CallArgs args = CallArgsFromVp(argc, vp);
  2828     return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args);
  2831 #if JS_HAS_TOSOURCE
  2832 MOZ_ALWAYS_INLINE bool
  2833 date_toSource_impl(JSContext *cx, CallArgs args)
  2835     StringBuffer sb(cx);
  2836     if (!sb.append("(new Date(") ||
  2837         !NumberValueToStringBuffer(cx, args.thisv().toObject().as<DateObject>().UTCTime(), sb) ||
  2838         !sb.append("))"))
  2840         return false;
  2843     JSString *str = sb.finishString();
  2844     if (!str)
  2845         return false;
  2846     args.rval().setString(str);
  2847     return true;
  2850 static bool
  2851 date_toSource(JSContext *cx, unsigned argc, Value *vp)
  2853     CallArgs args = CallArgsFromVp(argc, vp);
  2854     return CallNonGenericMethod<IsDate, date_toSource_impl>(cx, args);
  2856 #endif
  2858 MOZ_ALWAYS_INLINE bool
  2859 date_toString_impl(JSContext *cx, CallArgs args)
  2861     return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
  2862                        FORMATSPEC_FULL, args.rval());
  2865 static bool
  2866 date_toString(JSContext *cx, unsigned argc, Value *vp)
  2868     CallArgs args = CallArgsFromVp(argc, vp);
  2869     return CallNonGenericMethod<IsDate, date_toString_impl>(cx, args);
  2872 MOZ_ALWAYS_INLINE bool
  2873 date_valueOf_impl(JSContext *cx, CallArgs args)
  2875     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
  2876     args.rval().set(dateObj->UTCTime());
  2877     return true;
  2880 static bool
  2881 date_valueOf(JSContext *cx, unsigned argc, Value *vp)
  2883     CallArgs args = CallArgsFromVp(argc, vp);
  2884     return CallNonGenericMethod<IsDate, date_valueOf_impl>(cx, args);
  2887 static const JSFunctionSpec date_static_methods[] = {
  2888     JS_FN("UTC",                 date_UTC,                MAXARGS,0),
  2889     JS_FN("parse",               date_parse,              1,0),
  2890     JS_FN("now",                 date_now,                0,0),
  2891     JS_FS_END
  2892 };
  2894 static const JSFunctionSpec date_methods[] = {
  2895     JS_FN("getTime",             date_getTime,            0,0),
  2896     JS_FN("getTimezoneOffset",   date_getTimezoneOffset,  0,0),
  2897     JS_FN("getYear",             date_getYear,            0,0),
  2898     JS_FN("getFullYear",         date_getFullYear,        0,0),
  2899     JS_FN("getUTCFullYear",      date_getUTCFullYear,     0,0),
  2900     JS_FN("getMonth",            date_getMonth,           0,0),
  2901     JS_FN("getUTCMonth",         date_getUTCMonth,        0,0),
  2902     JS_FN("getDate",             date_getDate,            0,0),
  2903     JS_FN("getUTCDate",          date_getUTCDate,         0,0),
  2904     JS_FN("getDay",              date_getDay,             0,0),
  2905     JS_FN("getUTCDay",           date_getUTCDay,          0,0),
  2906     JS_FN("getHours",            date_getHours,           0,0),
  2907     JS_FN("getUTCHours",         date_getUTCHours,        0,0),
  2908     JS_FN("getMinutes",          date_getMinutes,         0,0),
  2909     JS_FN("getUTCMinutes",       date_getUTCMinutes,      0,0),
  2910     JS_FN("getSeconds",          date_getUTCSeconds,      0,0),
  2911     JS_FN("getUTCSeconds",       date_getUTCSeconds,      0,0),
  2912     JS_FN("getMilliseconds",     date_getUTCMilliseconds, 0,0),
  2913     JS_FN("getUTCMilliseconds",  date_getUTCMilliseconds, 0,0),
  2914     JS_FN("setTime",             date_setTime,            1,0),
  2915     JS_FN("setYear",             date_setYear,            1,0),
  2916     JS_FN("setFullYear",         date_setFullYear,        3,0),
  2917     JS_FN("setUTCFullYear",      date_setUTCFullYear,     3,0),
  2918     JS_FN("setMonth",            date_setMonth,           2,0),
  2919     JS_FN("setUTCMonth",         date_setUTCMonth,        2,0),
  2920     JS_FN("setDate",             date_setDate,            1,0),
  2921     JS_FN("setUTCDate",          date_setUTCDate,         1,0),
  2922     JS_FN("setHours",            date_setHours,           4,0),
  2923     JS_FN("setUTCHours",         date_setUTCHours,        4,0),
  2924     JS_FN("setMinutes",          date_setMinutes,         3,0),
  2925     JS_FN("setUTCMinutes",       date_setUTCMinutes,      3,0),
  2926     JS_FN("setSeconds",          date_setSeconds,         2,0),
  2927     JS_FN("setUTCSeconds",       date_setUTCSeconds,      2,0),
  2928     JS_FN("setMilliseconds",     date_setMilliseconds,    1,0),
  2929     JS_FN("setUTCMilliseconds",  date_setUTCMilliseconds, 1,0),
  2930     JS_FN("toUTCString",         date_toGMTString,        0,0),
  2931     JS_FN("toLocaleFormat",      date_toLocaleFormat,     0,0),
  2932 #if EXPOSE_INTL_API
  2933     JS_SELF_HOSTED_FN(js_toLocaleString_str, "Date_toLocaleString", 0,0),
  2934     JS_SELF_HOSTED_FN("toLocaleDateString", "Date_toLocaleDateString", 0,0),
  2935     JS_SELF_HOSTED_FN("toLocaleTimeString", "Date_toLocaleTimeString", 0,0),
  2936 #else
  2937     JS_FN(js_toLocaleString_str, date_toLocaleString,     0,0),
  2938     JS_FN("toLocaleDateString",  date_toLocaleDateString, 0,0),
  2939     JS_FN("toLocaleTimeString",  date_toLocaleTimeString, 0,0),
  2940 #endif
  2941     JS_FN("toDateString",        date_toDateString,       0,0),
  2942     JS_FN("toTimeString",        date_toTimeString,       0,0),
  2943     JS_FN("toISOString",         date_toISOString,        0,0),
  2944     JS_FN(js_toJSON_str,         date_toJSON,             1,0),
  2945 #if JS_HAS_TOSOURCE
  2946     JS_FN(js_toSource_str,       date_toSource,           0,0),
  2947 #endif
  2948     JS_FN(js_toString_str,       date_toString,           0,0),
  2949     JS_FN(js_valueOf_str,        date_valueOf,            0,0),
  2950     JS_FS_END
  2951 };
  2953 bool
  2954 js_Date(JSContext *cx, unsigned argc, Value *vp)
  2956     CallArgs args = CallArgsFromVp(argc, vp);
  2958     /* Date called as function. */
  2959     if (!args.isConstructing())
  2960         return date_format(cx, NowAsMillis(), FORMATSPEC_FULL, args.rval());
  2962     /* Date called as constructor. */
  2963     double d;
  2964     if (args.length() == 0) {
  2965         /* ES5 15.9.3.3. */
  2966         d = NowAsMillis();
  2967     } else if (args.length() == 1) {
  2968         /* ES5 15.9.3.2. */
  2970         /* Step 1. */
  2971         if (!ToPrimitive(cx, args[0]))
  2972             return false;
  2974         if (args[0].isString()) {
  2975             /* Step 2. */
  2976             JSString *str = args[0].toString();
  2977             if (!str)
  2978                 return false;
  2980             JSLinearString *linearStr = str->ensureLinear(cx);
  2981             if (!linearStr)
  2982                 return false;
  2984             if (!date_parseString(linearStr, &d, &cx->runtime()->dateTimeInfo))
  2985                 d = GenericNaN();
  2986             else
  2987                 d = TimeClip(d);
  2988         } else {
  2989             /* Step 3. */
  2990             if (!ToNumber(cx, args[0], &d))
  2991                 return false;
  2992             d = TimeClip(d);
  2994     } else {
  2995         double msec_time;
  2996         if (!date_msecFromArgs(cx, args, &msec_time))
  2997             return false;
  2999         if (IsFinite(msec_time)) {
  3000             msec_time = UTC(msec_time, &cx->runtime()->dateTimeInfo);
  3001             msec_time = TimeClip(msec_time);
  3003         d = msec_time;
  3006     JSObject *obj = js_NewDateObjectMsec(cx, d);
  3007     if (!obj)
  3008         return false;
  3010     args.rval().setObject(*obj);
  3011     return true;
  3014 static bool
  3015 FinishDateClassInit(JSContext *cx, HandleObject ctor, HandleObject proto)
  3017     proto->as<DateObject>().setUTCTime(GenericNaN());
  3019     /*
  3020      * Date.prototype.toGMTString has the same initial value as
  3021      * Date.prototype.toUTCString.
  3022      */
  3023     RootedValue toUTCStringFun(cx);
  3024     RootedId toUTCStringId(cx, NameToId(cx->names().toUTCString));
  3025     RootedId toGMTStringId(cx, NameToId(cx->names().toGMTString));
  3026     return baseops::GetProperty(cx, proto, toUTCStringId, &toUTCStringFun) &&
  3027            baseops::DefineGeneric(cx, proto, toGMTStringId, toUTCStringFun,
  3028                                   JS_PropertyStub, JS_StrictPropertyStub, 0);
  3031 const Class DateObject::class_ = {
  3032     js_Date_str,
  3033     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
  3034     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
  3035     JS_PropertyStub,         /* addProperty */
  3036     JS_DeletePropertyStub,   /* delProperty */
  3037     JS_PropertyStub,         /* getProperty */
  3038     JS_StrictPropertyStub,   /* setProperty */
  3039     JS_EnumerateStub,
  3040     JS_ResolveStub,
  3041     date_convert,
  3042     nullptr,                 /* finalize */
  3043     nullptr,                 /* call */
  3044     nullptr,                 /* hasInstance */
  3045     nullptr,                 /* construct */
  3046     nullptr,                 /* trace */
  3048         GenericCreateConstructor<js_Date, NAME_OFFSET(Date), MAXARGS>,
  3049         GenericCreatePrototype<&DateObject::class_>,
  3050         date_static_methods,
  3051         date_methods,
  3052         FinishDateClassInit
  3054 };
  3056 JS_FRIEND_API(JSObject *)
  3057 js_NewDateObjectMsec(JSContext *cx, double msec_time)
  3059     JSObject *obj = NewBuiltinClassInstance(cx, &DateObject::class_);
  3060     if (!obj)
  3061         return nullptr;
  3062     obj->as<DateObject>().setUTCTime(msec_time);
  3063     return obj;
  3066 JS_FRIEND_API(JSObject *)
  3067 js_NewDateObject(JSContext *cx, int year, int mon, int mday,
  3068                  int hour, int min, int sec)
  3070     JS_ASSERT(mon < 12);
  3071     double msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
  3072     return js_NewDateObjectMsec(cx, UTC(msec_time, &cx->runtime()->dateTimeInfo));
  3075 JS_FRIEND_API(bool)
  3076 js_DateIsValid(JSObject *obj)
  3078     return obj->is<DateObject>() && !IsNaN(obj->as<DateObject>().UTCTime().toNumber());
  3081 JS_FRIEND_API(int)
  3082 js_DateGetYear(JSContext *cx, JSObject *obj)
  3084     /* Preserve legacy API behavior of returning 0 for invalid dates. */
  3085     JS_ASSERT(obj);
  3086     double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
  3087     if (IsNaN(localtime))
  3088         return 0;
  3090     return (int) YearFromTime(localtime);
  3093 JS_FRIEND_API(int)
  3094 js_DateGetMonth(JSContext *cx, JSObject *obj)
  3096     JS_ASSERT(obj);
  3097     double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
  3098     if (IsNaN(localtime))
  3099         return 0;
  3101     return (int) MonthFromTime(localtime);
  3104 JS_FRIEND_API(int)
  3105 js_DateGetDate(JSContext *cx, JSObject *obj)
  3107     JS_ASSERT(obj);
  3108     double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
  3109     if (IsNaN(localtime))
  3110         return 0;
  3112     return (int) DateFromTime(localtime);
  3115 JS_FRIEND_API(int)
  3116 js_DateGetHours(JSContext *cx, JSObject *obj)
  3118     JS_ASSERT(obj);
  3119     double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
  3120     if (IsNaN(localtime))
  3121         return 0;
  3123     return (int) HourFromTime(localtime);
  3126 JS_FRIEND_API(int)
  3127 js_DateGetMinutes(JSContext *cx, JSObject *obj)
  3129     JS_ASSERT(obj);
  3130     double localtime = obj->as<DateObject>().cachedLocalTime(&cx->runtime()->dateTimeInfo);
  3131     if (IsNaN(localtime))
  3132         return 0;
  3134     return (int) MinFromTime(localtime);
  3137 JS_FRIEND_API(int)
  3138 js_DateGetSeconds(JSObject *obj)
  3140     if (!obj->is<DateObject>())
  3141         return 0;
  3143     double utctime = obj->as<DateObject>().UTCTime().toNumber();
  3144     if (IsNaN(utctime))
  3145         return 0;
  3146     return (int) SecFromTime(utctime);
  3149 JS_FRIEND_API(double)
  3150 js_DateGetMsecSinceEpoch(JSObject *obj)
  3152     return obj->is<DateObject>() ? obj->as<DateObject>().UTCTime().toNumber() : 0;
  3156 static const NativeImpl sReadOnlyDateMethods[] = {
  3157     DateObject::getTime_impl,
  3158     DateObject::getYear_impl,
  3159     DateObject::getFullYear_impl,
  3160     DateObject::getUTCFullYear_impl,
  3161     DateObject::getMonth_impl,
  3162     DateObject::getUTCMonth_impl,
  3163     DateObject::getDate_impl,
  3164     DateObject::getUTCDate_impl,
  3165     DateObject::getDay_impl,
  3166     DateObject::getUTCDay_impl,
  3167     DateObject::getHours_impl,
  3168     DateObject::getUTCHours_impl,
  3169     DateObject::getMinutes_impl,
  3170     DateObject::getUTCMinutes_impl,
  3171     DateObject::getUTCSeconds_impl,
  3172     DateObject::getUTCMilliseconds_impl,
  3173     DateObject::getTimezoneOffset_impl,
  3174     date_toGMTString_impl,
  3175     date_toISOString_impl,
  3176     date_toLocaleFormat_impl,
  3177     date_toTimeString_impl,
  3178     date_toDateString_impl,
  3179     date_toSource_impl,
  3180     date_toString_impl,
  3181     date_valueOf_impl
  3182 };

mercurial