js/src/jsdate.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial