/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the JavaScript 2 Prototype. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU Public License (the "GPL"), in which case the * provisions of the GPL are applicable instead of those above. * If you wish to allow use of your version of this file only * under the terms of the GPL and not to allow others to use your * version of this file under the NPL, indicate your decision by * deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete * the provisions above, a recipient may use your version of this * file under either the NPL or the GPL. */ #ifdef _WIN32 #include "msvc_pragma.h" #endif #include #include #include #include #include #include #include "world.h" #include "utilities.h" #include "js2value.h" #include "numerics.h" #include "reader.h" #include "parser.h" #include "regexp.h" #include "js2engine.h" #include "bytecodecontainer.h" #include "js2metadata.h" #include "jslong.h" #include "fdlibm_ns.h" #include "prmjtime.h" namespace JavaScript { namespace MetaData { #define HalfTimeDomain 8.64e15 #define HoursPerDay 24.0 #define MinutesPerDay (HoursPerDay * MinutesPerHour) #define MinutesPerHour 60.0 #define SecondsPerDay (MinutesPerDay * SecondsPerMinute) #define SecondsPerHour (MinutesPerHour * SecondsPerMinute) #define SecondsPerMinute 60.0 #ifdef XP_PC /* Work around msvc double optimization bug by making these runtime values; if * they're available at compile time, msvc optimizes division by them by * computing the reciprocal and multiplying instead of dividing - this loses * when the reciprocal isn't representable in a double. */ static float64 msPerSecond = 1000.0; static float64 msPerDay = SecondsPerDay * 1000.0; static float64 msPerHour = SecondsPerHour * 1000.0; static float64 msPerMinute = SecondsPerMinute * 1000.0; #else #define msPerDay (SecondsPerDay * msPerSecond) #define msPerHour (SecondsPerHour * msPerSecond) #define msPerMinute (SecondsPerMinute * msPerSecond) #define msPerSecond 1000.0 #endif /* LocalTZA gets set by initDateObject() */ static float64 LocalTZA; /* * The following array contains the day of year for the first day of * each month, where index 0 is January, and day 0 is January 1. */ static float64 firstDayOfMonth[2][12] = { {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0}, {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0} }; #define MakeTime(hour, min, sec, ms) (((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms) #define MakeDate(day, time) (day * msPerDay + time) #define Day(t) floor((t) / msPerDay) #define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \ && !((d < 0 ? -d : d) > HalfTimeDomain)) \ ? JS2Engine::truncateFloat64(d + (+0.)) : nan) #define LocalTime(t) ((t) + LocalTZA + DaylightSavingTA(t)) #define DayFromMonth(m, leap) firstDayOfMonth[leap][(int32)m]; #define DayFromYear(y) (365 * ((y)-1970) + fd::floor(((y)-1969)/4.0) \ - fd::floor(((y)-1901)/100.0) + fd::floor(((y)-1601)/400.0)) #define TimeFromYear(y) (DayFromYear(y) * msPerDay) #define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) ? 366 : 365) #define InLeapYear(t) (bool)(DaysInYear(YearFromTime(t)) == 366) #define DayWithinYear(t, year) ((int32)(Day(t) - DayFromYear(year))) typedef enum formatspec { FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME } formatspec; /* constants for toString, toUTCString */ static char js_NaN_date_str[] = "Invalid Date"; static const char* days[] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; static const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static float64 DaylightSavingTA(float64 t) { volatile int64 PR_t; int64 ms2us; int64 offset; float64 result; /* abort if NaN */ if (JSDOUBLE_IS_NaN(t)) return t; /* put our t in an LL, and map it to usec for prtime */ JSLL_D2L(PR_t, t); JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC); JSLL_MUL(PR_t, PR_t, ms2us); offset = PRMJ_DSTOffset(PR_t); JSLL_DIV(offset, offset, ms2us); JSLL_L2D(result, offset); return result; } static float64 UTC(float64 t) { return t - LocalTZA - DaylightSavingTA(t - LocalTZA); } static float64 MakeDay(float64 year, float64 month, float64 date) { float64 result; bool leap; float64 yearday; float64 monthday; year += fd::floor(month / 12); month = fd::fmod(month, 12.0); if (month < 0) month += 12; leap = (DaysInYear((int32) year) == 366); yearday = floor(TimeFromYear(year) / msPerDay); monthday = DayFromMonth(month, leap); result = yearday + monthday + date - 1; return result; } static float64 TimeWithinDay(float64 t) { float64 result; result = fd::fmod(t, msPerDay); if (result < 0) result += msPerDay; return result; } /* math here has to be f.p, because we need * floor((1968 - 1969) / 4) == -1 */ static int32 YearFromTime(float64 t) { int32 y = (int32) floor(t /(msPerDay*365.2425)) + 1970; float64 t2 = (float64) TimeFromYear(y); if (t2 > t) { y--; } else { if (t2 + msPerDay * DaysInYear(y) <= t) y++; } return y; } static int32 MonthFromTime(float64 t) { int32 d, step; int32 year = YearFromTime(t); d = DayWithinYear(t, year); if (d < (step = 31)) return 0; step += (InLeapYear(t) ? 29 : 28); if (d < step) return 1; if (d < (step += 31)) return 2; if (d < (step += 30)) return 3; if (d < (step += 31)) return 4; if (d < (step += 30)) return 5; if (d < (step += 31)) return 6; if (d < (step += 31)) return 7; if (d < (step += 30)) return 8; if (d < (step += 31)) return 9; if (d < (step += 30)) return 10; return 11; } static int32 DateFromTime(float64 t) { int32 d, step, next; int32 year = YearFromTime(t); d = DayWithinYear(t, year); if (d <= (next = 30)) return d + 1; step = next; next += (InLeapYear(t) ? 29 : 28); if (d <= next) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 30)) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 30)) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 30)) return d - step; step = next; if (d <= (next += 31)) return d - step; step = next; if (d <= (next += 30)) return d - step; step = next; return d - step; } static int32 WeekDay(float64 t) { int32 result; result = (int32) Day(t) + 4; result = result % 7; if (result < 0) result += 7; return result; } static float64 *Date_getProlog(JS2Metadata *meta, const js2val thisValue) { if (!JS2VAL_IS_OBJECT(thisValue) || (JS2VAL_TO_OBJECT(thisValue)->kind != SimpleInstanceKind) || ((checked_cast(JS2VAL_TO_OBJECT(thisValue)))->type != meta->dateClass)) meta->reportError(Exception::typeError, "You really need a date", meta->engine->errorPos()); DateInstance *dateInst = checked_cast(JS2VAL_TO_OBJECT(thisValue)); return &dateInst->ms; } /* for use by date_parse */ static char* wtb[] = { "am", "pm", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december", "gmt", "ut", "utc", "est", "edt", "cst", "cdt", "mst", "mdt", "pst", "pdt" /* time zone table needs to be expanded */ }; static int ttb[] = { -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */ 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */ 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */ 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */ 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */ }; static int32 HourFromTime(float64 t) { int32 result = (int32)fd::fmod(floor(t/msPerHour), HoursPerDay); if (result < 0) result += (int32)HoursPerDay; return result; } static int32 MinFromTime(float64 t) { int32 result = (int32)fd::fmod(floor(t / msPerMinute), MinutesPerHour); if (result < 0) result += (int32)MinutesPerHour; return result; } static int32 SecFromTime(float64 t) { int32 result = (int32)fd::fmod(floor(t / msPerSecond), SecondsPerMinute); if (result < 0) result += (int32)SecondsPerMinute; return result; } static int32 msFromTime(float64 t) { int32 result = (int32)fd::fmod(t, msPerSecond); if (result < 0) result += (int32)msPerSecond; return result; } static js2val Date_makeTime(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc, uint32 maxargs, bool local) { uint32 i; float64 args[4], *argp, *stop; float64 hour, min, sec, msec; float64 lorutime; /* Local or UTC version of *date */ float64 msec_time; float64 result; float64 *date = Date_getProlog(meta, thisValue); result = *date; /* just return NaN if the date is already NaN */ if (JSDOUBLE_IS_NaN(result)) return meta->engine->nanValue; if (argc == 0) { *date = nan; return meta->engine->nanValue; } else if (argc > maxargs) argc = maxargs; /* clamp argc */ for (i = 0; i < argc; i++) { float64 f = meta->toFloat64(argv[i]); if (JSDOUBLE_IS_NaN(f)) { *date = nan; return meta->engine->nanValue; } args[i] = JS2Engine::truncateFloat64(f); } if (local) lorutime = LocalTime(result); else lorutime = result; argp = args; stop = argp + argc; if ((maxargs >= 4) && (argp < stop)) hour = *argp++; else hour = HourFromTime(lorutime); if ((maxargs >= 3) && (argp < stop)) min = *argp++; else min = MinFromTime(lorutime); if ((maxargs >= 2) && (argp < stop)) sec = *argp++; else sec = SecFromTime(lorutime); if ((maxargs >= 1) && (argp < stop)) msec = *argp; else msec = msFromTime(lorutime); msec_time = MakeTime(hour, min, sec, msec); result = MakeDate(Day(lorutime), msec_time); if (local) result = UTC(result); *date = TIMECLIP(result); return meta->engine->allocNumber(*date); } static js2val Date_makeDate(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc, uint32 maxargs, bool local) { uint32 i; float64 lorutime; /* local or UTC version of *date */ float64 args[3], *argp, *stop; float64 year, month, day; float64 result; float64 *date = Date_getProlog(meta, thisValue); result = *date; /* see complaint about ECMA in date_MakeTime */ if (argc == 0) { *date = nan; return meta->engine->nanValue; } else if (argc > maxargs) argc = maxargs; /* clamp argc */ for (i = 0; i < argc; i++) { float64 f = meta->toFloat64(argv[i]); if (JSDOUBLE_IS_NaN(f)) { *date = nan; return meta->engine->nanValue; } args[i] = (float64)(JS2Engine::float64toInt32(f)); } /* return NaN if date is NaN and we're not setting the year, * If we are, use 0 as the time. */ if (!(JSDOUBLE_IS_FINITE(result))) { if (argc < 3) return meta->engine->nanValue; else lorutime = +0.; } else { if (local) lorutime = LocalTime(result); else lorutime = result; } argp = args; stop = argp + argc; if (maxargs >= 3 && argp < stop) year = *argp++; else year = YearFromTime(lorutime); if (maxargs >= 2 && argp < stop) month = *argp++; else month = MonthFromTime(lorutime); if (maxargs >= 1 && argp < stop) day = *argp++; else day = DateFromTime(lorutime); day = MakeDay(year, month, day); /* day within year */ result = MakeDate(day, TimeWithinDay(lorutime)); if (local) result = UTC(result); *date = TIMECLIP(result); return meta->engine->allocNumber(*date); } /* find UTC time from given date... no 1900 correction! */ static float64 date_msecFromDate(float64 year, float64 mon, float64 mday, float64 hour, float64 min, float64 sec, float64 msec) { float64 day; float64 msec_time; float64 result; day = MakeDay(year, mon, mday); msec_time = MakeTime(hour, min, sec, msec); result = MakeDate(day, msec_time); return result; } static float64 date_parseString(const String &s) { float64 msec; int year = -1; int mon = -1; int mday = -1; int hour = -1; int min = -1; int sec = -1; int c = -1; uint32 i = 0; int n = -1; float64 tzoffset = -1; /* was an int, overflowed on win16!!! */ int prevc = 0; uint32 limit = 0; bool seenplusminus = false; limit = s.length(); if (limit == 0) goto syntax; while (i < limit) { c = s[i]; i++; if (c <= ' ' || c == ',' || c == '-') { if (c == '-' && '0' <= s[i] && s[i] <= '9') { prevc = c; } continue; } if (c == '(') { /* comments) */ int depth = 1; while (i < limit) { c = s[i]; i++; if (c == '(') depth++; else if (c == ')') if (--depth <= 0) break; } continue; } if ('0' <= c && c <= '9') { n = c - '0'; while (i < limit && '0' <= (c = s[i]) && c <= '9') { n = n * 10 + c - '0'; i++; } /* allow TZA before the year, so * 'Wed Nov 05 21:49:11 GMT-0800 1997' * works */ /* uses of seenplusminus allow : in TZA, so Java * no-timezone style of GMT+4:30 works */ if ((prevc == '+' || prevc == '-')/* && year>=0 */) { /* make ':' case below change tzoffset */ seenplusminus = true; /* offset */ if (n < 24) n = n * 60; /* EG. "GMT-3" */ else n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ if (prevc == '+') /* plus means east of GMT */ n = -n; if (tzoffset != 0 && tzoffset != -1) goto syntax; tzoffset = n; } else if (n >= 70 || (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) { if (year >= 0) goto syntax; else if (c <= ' ' || c == ',' || c == '/' || i >= limit) year = n < 100 ? n + 1900 : n; else goto syntax; } else if (c == ':') { if (hour < 0) hour = /*byte*/ n; else if (min < 0) min = /*byte*/ n; else goto syntax; } else if (c == '/') { if (mon < 0) mon = /*byte*/ n-1; else if (mday < 0) mday = /*byte*/ n; else goto syntax; } else if (i < limit && c != ',' && c > ' ' && c != '-') { goto syntax; } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */ if (tzoffset < 0) tzoffset -= n; else tzoffset += n; } else if (hour >= 0 && min < 0) { min = /*byte*/ n; } else if (min >= 0 && sec < 0) { sec = /*byte*/ n; } else if (mday < 0) { mday = /*byte*/ n; } else { goto syntax; } prevc = 0; } else if (c == '/' || c == ':' || c == '+' || c == '-') { prevc = c; } else { uint32 st = i - 1; int k; while (i < limit) { c = s[i]; if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) break; i++; } if (i <= st + 1) goto syntax; for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;) if (regionMatches(wtb[k], s, st, i-st, 1)) { int action = ttb[k]; if (action != 0) { if (action < 0) { /* * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as * 12:30, instead of blindly adding 12 if PM. */ ASSERT(action == -1 || action == -2); if (hour > 12 || hour < 0) { goto syntax; } else { if (action == -1 && hour == 12) { /* am */ hour = 0; } else if (action == -2 && hour != 12) { /* pm */ hour += 12; } } } else if (action <= 13) { /* month! */ if (mon < 0) { mon = /*byte*/ (action - 2); } else { goto syntax; } } else { tzoffset = action - 10000; } } break; } if (k < 0) goto syntax; prevc = 0; } } // while (i < limit) if (year < 0 || mon < 0 || mday < 0) goto syntax; if (sec < 0) sec = 0; if (min < 0) min = 0; if (hour < 0) hour = 0; if (tzoffset == -1) { /* no time zone specified, have to use local */ float64 msec_time; msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); return UTC(msec_time); } msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); msec += tzoffset * msPerMinute; return msec; syntax: /* syntax error */ return nan; } /* for Date.toLocaleString; interface to PRMJTime date struct. * If findEquivalent is true, then try to map the year to an equivalent year * that's in range. */ static void new_explode(float64 timeval, PRMJTime *split, bool findEquivalent) { int32 year = YearFromTime(timeval); int16 adjustedYear; /* If the year doesn't fit in a PRMJTime, find something to do about it. */ if (year > 32767 || year < -32768) { if (findEquivalent) { /* We're really just trying to get a timezone string; map the year * to some equivalent year in the range 0 to 2800. Borrowed from * A. D. Olsen. */ int32 cycles; #define CYCLE_YEARS 2800L cycles = (year >= 0) ? year / CYCLE_YEARS : -1 - (-1 - year) / CYCLE_YEARS; adjustedYear = (int16)(year - cycles * CYCLE_YEARS); } else { /* Clamp it to the nearest representable year. */ adjustedYear = (int16)((year > 0) ? 32767 : - 32768); } } else { adjustedYear = (int16)year; } split->tm_usec = (uint32) msFromTime(timeval) * 1000; split->tm_sec = (uint8) SecFromTime(timeval); split->tm_min = (uint8) MinFromTime(timeval); split->tm_hour = (uint8) HourFromTime(timeval); split->tm_mday = (uint8) DateFromTime(timeval); split->tm_mon = (uint8) MonthFromTime(timeval); split->tm_wday = (uint8) WeekDay(timeval); split->tm_year = (uint16) adjustedYear; split->tm_yday = (uint16) DayWithinYear(timeval, year); /* not sure how this affects things, but it doesn't seem to matter. */ split->tm_isdst = (DaylightSavingTA(timeval) != 0); } /* helper function */ static js2val Date_format(JS2Metadata *meta, float64 date, formatspec format) { StringFormatter outf; char tzbuf[100]; bool usetz; size_t i, tzlen, bytesStored; PRMJTime split; if (!JSDOUBLE_IS_FINITE(date)) { outf << js_NaN_date_str; } else { float64 local = LocalTime(date); /* offset from GMT in minutes. The offset includes daylight savings, if it applies. */ int32 minutes = (int32)fd::floor((LocalTZA + DaylightSavingTA(date)) / msPerMinute); /* map 510 minutes to 0830 hours */ int32 offset = (minutes / 60) * 100 + minutes % 60; /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is * printed as 'GMT-0800' rather than as 'PST' to avoid * operating-system dependence on strftime (which * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints * PST as 'Pacific Standard Time.' This way we always know * what we're getting, and can parse it if we produce it. * The OS TZA string is included as a comment. */ /* get a timezone string from the OS to include as a comment. */ new_explode(date, &split, true); bytesStored = PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split); /* Decide whether to use the resulting timezone string. * * Reject it if it contains any non-ASCII, non-alphanumeric characters. * It's then likely in some other character encoding, and we probably * won't display it correctly. */ if (bytesStored > 0) { usetz = true; tzlen = strlen(tzbuf); if (tzlen > 100) { usetz = false; } else { for (i = 0; i < tzlen; i++) { int16 c = tzbuf[i]; if (c > 127 || !(isalpha(c) || isdigit(c) || c == ' ' || c == '(' || c == ')')) { usetz = false; } } } /* Also reject it if it's not parenthesized or if it's '()'. */ if (tzbuf[0] != '(' || tzbuf[1] == ')') usetz = false; } else usetz = false; switch (format) { case FORMATSPEC_FULL: /* * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. */ /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */ printFormat(outf, "%s %s %.2d %.2d:%.2d:%.2d GMT%+.4d %s%s%.4d", days[WeekDay(local)], months[MonthFromTime(local)], DateFromTime(local), HourFromTime(local), MinFromTime(local), SecFromTime(local), offset, usetz ? tzbuf : "", usetz ? " " : "", YearFromTime(local)); break; case FORMATSPEC_DATE: /* Tue Oct 31 2000 */ printFormat(outf, "%s %s %.2d %.4d", days[WeekDay(local)], months[MonthFromTime(local)], DateFromTime(local), YearFromTime(local)); break; case FORMATSPEC_TIME: /* 09:41:40 GMT-0800 (PST) */ printFormat(outf, "%.2d:%.2d:%.2d GMT%+.4d%s%s", HourFromTime(local), MinFromTime(local), SecFromTime(local), offset, usetz ? " " : "", usetz ? tzbuf : ""); break; } } return meta->engine->allocString(outf.getString()); } js2val Date_Call(JS2Metadata *meta, const js2val /* thisValue */, js2val *argv, uint32 argc) { int64 us, ms, us2ms; float64 msec_time; us = PRMJ_Now(); JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); JSLL_DIV(ms, us, us2ms); JSLL_L2D(msec_time, ms); return Date_format(meta, msec_time, FORMATSPEC_FULL); } #define MAXARGS 7 js2val Date_Constructor(JS2Metadata *meta, const js2val /* thisValue */, js2val *argv, uint32 argc) { js2val thatValue = OBJECT_TO_JS2VAL(new DateInstance(meta, meta->dateClass->prototype, meta->dateClass)); DateInstance *thisInst = checked_cast(JS2VAL_TO_OBJECT(thatValue)); RootKeeper rk(&thisInst); /* Date called as constructor */ if (argc == 0) { int64 us, ms, us2ms; float64 msec_time; us = PRMJ_Now(); JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); JSLL_DIV(ms, us, us2ms); JSLL_L2D(msec_time, ms); thisInst->ms = msec_time; } else { if (argc == 1) { if (!JS2VAL_IS_STRING(argv[0])) { /* the argument is a millisecond number */ float64 d = meta->toFloat64(argv[0]); thisInst->ms = TIMECLIP(d); } else { /* the argument is a string; parse it. */ const String *str = meta->toString(argv[0]); float64 d = date_parseString(*str); thisInst->ms = TIMECLIP(d); } } else { float64 array[MAXARGS]; uint32 loop; float64 day; float64 msec_time; for (loop = 0; loop < MAXARGS; loop++) { if (loop < argc) { float64 double_arg = meta->toFloat64(argv[loop]); /* if any arg is NaN, make a NaN date object and return */ if (!JSDOUBLE_IS_FINITE(double_arg)) { thisInst->ms = nan; return thatValue; } array[loop] = JS2Engine::float64toInt32(double_arg); } else { if (loop == 2) { array[loop] = 1; /* Default the date argument to 1. */ } else { array[loop] = 0; } } } /* adjust 2-digit years into the 20th century */ if (array[0] >= 0 && array[0] <= 99) array[0] += 1900; day = MakeDay(array[0], array[1], array[2]); msec_time = MakeTime(array[3], array[4], array[5], array[6]); msec_time = MakeDate(day, msec_time); msec_time = UTC(msec_time); thisInst->ms = TIMECLIP(msec_time); } } return thatValue; } js2val Date_parse(JS2Metadata *meta, const js2val /*thisValue*/, js2val *argv, uint32 /*argc*/) { const String *str = meta->toString(argv[0]); float64 d = date_parseString(*str); d = TIMECLIP(d); return meta->engine->allocNumber(d); } js2val Date_UTC(JS2Metadata *meta, const js2val /*thisValue*/, js2val *argv, uint32 argc) { float64 array[MAXARGS]; uint32 loop; float64 d; for (loop = 0; loop < MAXARGS; loop++) { if (loop < argc) { d = meta->toFloat64(argv[loop]); if (!JSDOUBLE_IS_FINITE(d)) return meta->engine->allocNumber(d); array[loop] = floor(d); } else array[loop] = 0; } /* adjust 2-digit years into the 20th century */ if ((array[0] >= 0) && (array[0] <= 99)) array[0] += 1900; /* if we got a 0 for 'date' (which is out of range) * pretend it's a 1. (So Date.UTC(1972, 5) works) */ if (array[2] < 1) array[2] = 1; d = date_msecFromDate(array[0], array[1], array[2], array[3], array[4], array[5], array[6]); d = TIMECLIP(d); return meta->engine->allocNumber(d); } static js2val Date_toGMTString(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { StringFormatter buf; float64 *date = Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(*date)) { buf << js_NaN_date_str; } else { float64 temp = *date; /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. */ printFormat(buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", days[WeekDay(temp)], DateFromTime(temp), months[MonthFromTime(temp)], YearFromTime(temp), HourFromTime(temp), MinFromTime(temp), SecFromTime(temp)); } return meta->engine->allocString(buf.getString()); } static js2val Date_toLocaleHelper(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/, char *format) { StringFormatter outf; char buf[100]; PRMJTime split; float64 *date = Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(*date)) { outf << js_NaN_date_str; } else { uint32 result_len; float64 local = LocalTime(*date); new_explode(local, &split, false); /* let PRMJTime format it. */ result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split); /* If it failed, default to toString. */ if (result_len == 0) return Date_format(meta, *date, FORMATSPEC_FULL); /* Hacked check against undesired 2-digit year 00/00/00 form. */ if ((buf[result_len - 3] == '/') && isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) { printString(outf, buf + (result_len - 2), buf + ((sizeof buf) - (result_len - 2)) ); printFormat(outf, "%d", (int32)YearFromTime(LocalTime(*date)) ); } else outf << buf; } return meta->engine->allocString(outf.getString()); } static js2val Date_toLocaleString(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc) { /* Use '%#c' for windows, because '%c' is * backward-compatible and non-y2k with msvc; '%#c' requests that a * full year be used in the result string. */ return Date_toLocaleHelper(meta, thisValue, argv, argc, #if defined(_WIN32) && !defined(__MWERKS__) "%#c" #else "%c" #endif ); } static js2val Date_toLocaleDateString(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc) { /* Use '%#x' for windows, because '%x' is * backward-compatible and non-y2k with msvc; '%#x' requests that a * full year be used in the result string. */ return Date_toLocaleHelper(meta, thisValue, argv, argc, #if defined(_WIN32) && !defined(__MWERKS__) "%#x" #else "%x" #endif ); } static js2val Date_toLocaleTimeString(JS2Metadata *meta, const js2val thisValue, js2val *argv, uint32 argc) { return Date_toLocaleHelper(meta, thisValue, argv, argc, "%X"); } static js2val Date_toTimeString(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { float64 *date = Date_getProlog(meta, thisValue); return Date_format(meta, *date, FORMATSPEC_TIME); } static js2val Date_toDateString(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { float64 *date = Date_getProlog(meta, thisValue); return Date_format(meta, *date, FORMATSPEC_DATE); } static js2val Date_toString(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { float64 *date = Date_getProlog(meta, thisValue); return Date_format(meta, *date, FORMATSPEC_FULL); } static js2val Date_toSource(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { StringFormatter buf; float64 *date = Date_getProlog(meta, thisValue); buf << "(new Date(" << *meta->engine->numberToString(date) << "))"; return meta->engine->allocString(buf.getString()); } static js2val Date_getTime(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { float64 *date = Date_getProlog(meta, thisValue); return meta->engine->allocNumber(*date); } static js2val Date_getTimezoneOffset(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { float64 result; float64 *date = Date_getProlog(meta, thisValue); result = *date; /* * Return the time zone offset in minutes for the current locale * that is appropriate for this time. This value would be a * constant except for daylight savings time. */ result = (result - LocalTime(result)) / msPerMinute; return meta->engine->allocNumber(result); } static js2val Date_getYear(JS2Metadata *meta, const js2val thisValue, js2val * /*argv*/, uint32 /*argc*/) { float64 result; float64 *date = Date_getProlog(meta, thisValue); result = *date; if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = YearFromTime(LocalTime(result)); #if 0 XXX /* * During the great date rewrite of 1.3, we tried to track the evolving ECMA * standard, which then had a definition of getYear which always subtracted * 1900. Which we implemented, not realizing that it was incompatible with * the old behavior... now, rather than thrash the behavior yet again, * we've decided to leave it with the - 1900 behavior and point people to * the getFullYear method. But we try to protect existing scripts that * have specified a version... */ if (cx->version == JSVERSION_1_0 || cx->version == JSVERSION_1_1 || cx->version == JSVERSION_1_2) { if (result >= 1900 && result < 2000) result -= 1900; } else { result -= 1900; } #else result -= 1900; #endif return meta->engine->allocNumber(result); } static js2val Date_getFullYear(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = YearFromTime(LocalTime(result)); return meta->engine->allocNumber(result); } static js2val Date_getUTCFullYear(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = YearFromTime(result); return meta->engine->allocNumber(result); } static js2val Date_getMonth(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = MonthFromTime(LocalTime(result)); return meta->engine->allocNumber(result); } static js2val Date_getUTCMonth(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = MonthFromTime(result); return meta->engine->allocNumber(result); } static js2val Date_getDate(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = DateFromTime(LocalTime(result)); return meta->engine->allocNumber(result); } static js2val Date_getUTCDate(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = DateFromTime(result); return meta->engine->allocNumber(result); } static js2val Date_getDay(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = WeekDay(LocalTime(result)); return meta->engine->allocNumber(result); } static js2val Date_getUTCDay(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = WeekDay(result); return meta->engine->allocNumber(result); } static js2val Date_getHours(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = HourFromTime(LocalTime(result)); return meta->engine->allocNumber(result); } static js2val Date_getUTCHours(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = HourFromTime(result); return meta->engine->allocNumber(result); } static js2val Date_getMinutes(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = MinFromTime(LocalTime(result)); return meta->engine->allocNumber(result); } static js2val Date_getUTCMinutes(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = MinFromTime(result); return meta->engine->allocNumber(result); } /* Date.getSeconds is mapped to getUTCSeconds */ static js2val Date_getUTCSeconds(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = SecFromTime(result); return meta->engine->allocNumber(result); } /* Date.getMilliseconds is mapped to getUTCMilliseconds */ static js2val Date_getUTCMilliseconds(JS2Metadata *meta, const js2val thisValue, js2val * /* argv */, uint32 /*argc*/) { float64 result = *Date_getProlog(meta, thisValue); if (!JSDOUBLE_IS_FINITE(result)) return meta->engine->allocNumber(result); result = msFromTime(result); return meta->engine->allocNumber(result); } static js2val Date_setTime(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 /*argc*/) { float64 *date = Date_getProlog(meta, thisValue); float64 result = meta->toFloat64(argv[0]); *date = TIMECLIP(result); return meta->engine->allocNumber(*date); } static js2val Date_setYear(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 /*argc*/) { float64 t; float64 year; float64 day; float64 result; float64 *date = Date_getProlog(meta, thisValue); result = *date; year = meta->toFloat64(argv[0]); if (!JSDOUBLE_IS_FINITE(year)) { *date = nan; return meta->engine->allocNumber(*date); } year = JS2Engine::float64toInt32(year); if (!JSDOUBLE_IS_FINITE(result)) { t = +0.0; } else { t = LocalTime(result); } if (year >= 0 && year <= 99) year += 1900; day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); result = MakeDate(day, TimeWithinDay(t)); result = UTC(result); *date = TIMECLIP(result); return meta->engine->allocNumber(*date); } static js2val Date_setFullYear(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeDate(meta, thisValue, argv, argc, 3, true); } static js2val Date_setUTCFullYear(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeDate(meta, thisValue, argv, argc, 3, false); } static js2val Date_setMonth(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeDate(meta, thisValue, argv, argc, 2, true); } static js2val Date_setUTCMonth(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeDate(meta, thisValue, argv, argc, 2, false); } static js2val Date_setDate(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeDate(meta, thisValue, argv, argc, 1, true); } static js2val Date_setUTCDate(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeDate(meta, thisValue, argv, argc, 1, false); } static js2val Date_setHours(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeTime(meta, thisValue, argv, argc, 4, true); } static js2val Date_setUTCHours(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeTime(meta, thisValue, argv, argc, 4, false); } static js2val Date_setMinutes(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeTime(meta, thisValue, argv, argc, 3, true); } static js2val Date_setUTCMinutes(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeTime(meta, thisValue, argv, argc, 3, false); } static js2val Date_setSeconds(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeTime(meta, thisValue, argv, argc, 2, true); } static js2val Date_setUTCSeconds(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeTime(meta, thisValue, argv, argc, 2, true); } static js2val Date_setMilliseconds(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeTime(meta, thisValue, argv, argc, 1, true); } static js2val Date_setUTCMilliseconds(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { return Date_makeTime(meta, thisValue, argv, argc, 1, true); } // SpiderMonkey has a 'hinted' version: #if JS_HAS_VALUEOF_HINT static js2val Date_valueOf(JS2Metadata *meta, const js2val thisValue, js2val argv[], uint32 argc) { /* If called directly with no arguments, convert to a time number. */ if (argc == 0) return Date_getTime(meta, thisValue, argv, argc); /* Convert to number only if the hint was given, otherwise favor string. */ if (argc == 1) { const String *str = JSValue::string(JSValue::toString(meta, argv[0])); if (str->compare(&cx->Number_StringAtom) == 0) return Date_getTime(meta, thisValue, argv, argc); } return Date_toString(meta, thisValue, argv, argc); } #endif void initDateObject(JS2Metadata *meta) { FunctionData prototypeFunctions[] = { { "getTime", 0, Date_getTime }, { "getTimezoneOffset", 0, Date_getTimezoneOffset }, { "getYear", 0, Date_getYear }, { "getFullYear", 0, Date_getFullYear }, { "getUTCFullYear", 0, Date_getUTCFullYear }, { "getMonth", 0, Date_getMonth }, { "getUTCMonth", 0, Date_getUTCMonth }, { "getDate", 0, Date_getDate }, { "getUTCDate", 0, Date_getUTCDate }, { "getDay", 0, Date_getDay }, { "getUTCDay", 0, Date_getUTCDay }, { "getHours", 0, Date_getHours }, { "getUTCHours", 0, Date_getUTCHours }, { "getMinutes", 0, Date_getMinutes }, { "getUTCMinutes", 0, Date_getUTCMinutes }, { "getSeconds", 0, Date_getUTCSeconds }, { "getUTCSeconds", 0, Date_getUTCSeconds }, { "getMilliseconds", 0, Date_getUTCMilliseconds }, { "getUTCMilliseconds", 0, Date_getUTCMilliseconds }, { "setTime", 1, Date_setTime }, { "setYear", 1, Date_setYear }, { "setFullYear", 1, Date_setFullYear }, { "setUTCFullYear", 3, Date_setUTCFullYear }, { "setMonth", 2, Date_setMonth }, { "setUTCMonth", 2, Date_setUTCMonth }, { "setDate", 1, Date_setDate }, { "setUTCDate", 1, Date_setUTCDate }, { "setHours", 4, Date_setHours }, { "setUTCHours", 4, Date_setUTCHours }, { "setMinutes", 3, Date_setMinutes }, { "setUTCMinutes", 3, Date_setUTCMinutes }, { "setSeconds", 2, Date_setSeconds }, { "setUTCSeconds", 2, Date_setUTCSeconds }, { "setMilliseconds", 1, Date_setMilliseconds }, { "setUTCMilliseconds", 1, Date_setUTCMilliseconds }, { "toUTCString", 0, Date_toGMTString }, { "toGMTString", 0, Date_toGMTString }, // XXX this is a SpiderMonkey extension? { "toLocaleString", 0, Date_toLocaleString }, { "toLocaleDateString", 0, Date_toLocaleDateString }, { "toLocaleTimeString", 0, Date_toLocaleTimeString }, { "toDateString", 0, Date_toDateString }, { "toTimeString", 0, Date_toTimeString }, { "toSource", 0, Date_toSource }, { "toString", 0, Date_toString }, #if JS_HAS_VALUEOF_HINT { "valueOf", 0, Date_valueOf }, #else { "valueOf", 0, Date_getTime }, #endif { NULL } }; FunctionData staticFunctions[] = { { "parse", 0, Date_parse }, { "UTC", 0, Date_UTC }, { NULL } }; LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); meta->dateClass->prototype = new DateInstance(meta, meta->objectClass->prototype, meta->dateClass); meta->initBuiltinClass(meta->dateClass, &prototypeFunctions[0], &staticFunctions[0], Date_Constructor, Date_Call); } } }