/* -*- Mode: java; tab-width: 8; 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 oqr * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1997-1999 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Norris Boyd * Mike McCabe * * 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. */ package org.ibex.js; import java.text.DateFormat; /** * This class implements the Date native object. * See ECMA 15.9. * @author Mike McCabe * @author Adam Megacz (many modifications */ public class JSDate extends JS.Immutable { private static final JS.Method METHOD = new JS.Method(); public JSDate() { if (thisTimeZone == null) { // j.u.TimeZone is synchronized, so setting class statics from it // should be OK. thisTimeZone = java.util.TimeZone.getDefault(); LocalTZA = thisTimeZone.getRawOffset(); } } public String coerceToString() { return date_format(date, FORMATSPEC_FULL); } public JS call(JS method, JS[] args) throws JSExn { switch(args.length) { case 0: { //#switch(JSU.toString(method)) case "toString": return JSU.S(date_format(date, FORMATSPEC_FULL)); case "toTimeString": return JSU.S(date_format(date, FORMATSPEC_TIME)); case "toDateString": return JSU.S(date_format(date, FORMATSPEC_DATE)); case "toLocaleString": return JSU.S(toLocaleString(date)); case "toLocaleTimeString": return JSU.S(toLocaleTimeString(date)); case "toLocaleDateString": return JSU.S(toLocaleDateString(date)); case "toUTCString": return JSU.S(toUTCString(date)); case "valueOf": return JSU.N(this.date); case "getTime": return JSU.N(this.date); case "getYear": return JSU.N(getYear(date)); case "getFullYear": return JSU.N(YearFromTime(LocalTime(date))); case "getUTCFullYear": return JSU.N(YearFromTime(date)); case "getMonth": return JSU.N(MonthFromTime(LocalTime(date))); case "getUTCMonth": return JSU.N(MonthFromTime(date)); case "getDate": return JSU.N(DateFromTime(LocalTime(date))); case "getUTCDate": return JSU.N(DateFromTime(date)); case "getDay": return JSU.N(WeekDay(LocalTime(date))); case "getUTCDay": return JSU.N(WeekDay(date)); case "getHours": return JSU.N(HourFromTime(LocalTime(date))); case "getUTCHours": return JSU.N(HourFromTime(date)); case "getMinutes": return JSU.N(MinFromTime(LocalTime(date))); case "getUTCMinutes": return JSU.N(MinFromTime(date)); case "getSeconds": return JSU.N(SecFromTime(LocalTime(date))); case "getUTCSeconds": return JSU.N(SecFromTime(date)); case "getMilliseconds": return JSU.N(msFromTime(LocalTime(date))); case "getUTCMilliseconds": return JSU.N(msFromTime(date)); case "getTimezoneOffset": return JSU.N(getTimezoneOffset(date)); //#end return super.call(method, args); } case 1: { //#switch(JSU.toString(method)) case "setTime": return JSU.N(this.setTime(JSU.toDouble(args[0]))); case "setYear": return JSU.N(this.setYear(JSU.toDouble(args[0]))); //#end // fall through } default: { //#switch(JSU.toString(method)) case "setMilliseconds": return JSU.N(this.makeTime(args, 1, true)); case "setUTCMilliseconds": return JSU.N(this.makeTime(args, 1, false)); case "setSeconds": return JSU.N(this.makeTime(args, 2, true)); case "setUTCSeconds": return JSU.N(this.makeTime(args, 2, false)); case "setMinutes": return JSU.N(this.makeTime(args, 3, true)); case "setUTCMinutes": return JSU.N(this.makeTime(args, 3, false)); case "setHours": return JSU.N(this.makeTime(args, 4, true)); case "setUTCHours": return JSU.N(this.makeTime(args, 4, false)); case "setDate": return JSU.N(this.makeDate(args, 1, true)); case "setUTCDate": return JSU.N(this.makeDate(args, 1, false)); case "setMonth": return JSU.N(this.makeDate(args, 2, true)); case "setUTCMonth": return JSU.N(this.makeDate(args, 2, false)); case "setFullYear": return JSU.N(this.makeDate(args, 3, true)); case "setUTCFullYear": return JSU.N(this.makeDate(args, 3, false)); //#end } } return super.call(method, args); } public JS get(JS key) throws JSExn { //#switch(JSU.toString(key)) case "toString": return METHOD; case "toTimeString": return METHOD; case "toDateString": return METHOD; case "toLocaleString": return METHOD; case "toLocaleTimeString": return METHOD; case "toLocaleDateString": return METHOD; case "toUTCString": return METHOD; case "valueOf": return METHOD; case "getTime": return METHOD; case "getYear": return METHOD; case "getFullYear": return METHOD; case "getUTCFullYear": return METHOD; case "getMonth": return METHOD; case "getUTCMonth": return METHOD; case "getDate": return METHOD; case "getUTCDate": return METHOD; case "getDay": return METHOD; case "getUTCDay": return METHOD; case "getHours": return METHOD; case "getUTCHours": return METHOD; case "getMinutes": return METHOD; case "getUTCMinutes": return METHOD; case "getSeconds": return METHOD; case "getUTCSeconds": return METHOD; case "getMilliseconds": return METHOD; case "getUTCMilliseconds": return METHOD; case "getTimezoneOffset": return METHOD; case "setTime": return METHOD; case "setYear": return METHOD; case "setMilliseconds": return METHOD; case "setUTCMilliseconds": return METHOD; case "setSeconds": return METHOD; case "setUTCSeconds": return METHOD; case "setMinutes": return METHOD; case "setUTCMinutes": return METHOD; case "setHours": return METHOD; case "setUTCHours": return METHOD; case "setDate": return METHOD; case "setUTCDate": return METHOD; case "setMonth": return METHOD; case "setUTCMonth": return METHOD; case "setFullYear": return METHOD; case "setUTCFullYear": return METHOD; //#end return super.get(key); } /* ECMA helper functions */ private static final double HalfTimeDomain = 8.64e15; private static final double HoursPerDay = 24.0; private static final double MinutesPerHour = 60.0; private static final double SecondsPerMinute = 60.0; private static final double msPerSecond = 1000.0; private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour); private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute); private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute); private static final double msPerDay = (SecondsPerDay * msPerSecond); private static final double msPerHour = (SecondsPerHour * msPerSecond); private static final double msPerMinute = (SecondsPerMinute * msPerSecond); private static double Day(double t) { return java.lang.Math.floor(t / msPerDay); } private static double TimeWithinDay(double t) { double result; result = t % msPerDay; if (result < 0) result += msPerDay; return result; } private static int DaysInYear(int y) { if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) return 366; else return 365; } /* math here has to be f.p, because we need * floor((1968 - 1969) / 4) == -1 */ private static double DayFromYear(double y) { return ((365 * ((y)-1970) + java.lang.Math.floor(((y)-1969)/4.0) - java.lang.Math.floor(((y)-1901)/100.0) + java.lang.Math.floor(((y)-1601)/400.0))); } private static double TimeFromYear(double y) { return DayFromYear(y) * msPerDay; } private static int YearFromTime(double t) { int lo = (int) java.lang.Math.floor((t / msPerDay) / 366) + 1970; int hi = (int) java.lang.Math.floor((t / msPerDay) / 365) + 1970; int mid; /* above doesn't work for negative dates... */ if (hi < lo) { int temp = lo; lo = hi; hi = temp; } /* Use a simple binary search algorithm to find the right year. This seems like brute force... but the computation of hi and lo years above lands within one year of the correct answer for years within a thousand years of 1970; the loop below only requires six iterations for year 270000. */ while (hi > lo) { mid = (hi + lo) / 2; if (TimeFromYear(mid) > t) { hi = mid - 1; } else { if (TimeFromYear(mid) <= t) { int temp = mid + 1; if (TimeFromYear(temp) > t) { return mid; } lo = mid + 1; } } } return lo; } private static boolean InLeapYear(double t) { return DaysInYear(YearFromTime(t)) == 366; } private static int DayWithinYear(double t) { int year = YearFromTime(t); return (int) (Day(t) - DayFromYear(year)); } /* * 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. */ private static double DayFromMonth(int m, boolean leap) { int day = m * 30; if (m >= 7) { day += m / 2 - 1; } else if (m >= 2) { day += (m - 1) / 2 - 1; } else { day += m; } if (leap && m >= 2) { ++day; } return day; } private static int MonthFromTime(double t) { int d, step; d = DayWithinYear(t); if (d < (step = 31)) return 0; // Originally coded as step += (InLeapYear(t) ? 29 : 28); // but some jits always returned 28! if (InLeapYear(t)) step += 29; else step += 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; } private static int DateFromTime(double t) { int d, step, next; d = DayWithinYear(t); if (d <= (next = 30)) return d + 1; step = next; // Originally coded as next += (InLeapYear(t) ? 29 : 28); // but some jits always returned 28! if (InLeapYear(t)) next += 29; else next += 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; } private static int WeekDay(double t) { double result; result = Day(t) + 4; result = result % 7; if (result < 0) result += 7; return (int) result; } private static double Now() { return (double) System.currentTimeMillis(); } /* Should be possible to determine the need for this dynamically * if we go with the workaround... I'm not using it now, because I * can't think of any clean way to make toLocaleString() and the * time zone (comment) in toString match the generated string * values. Currently it's wrong-but-consistent in all but the * most recent betas of the JRE - seems to work in 1.1.7. */ private final static boolean TZO_WORKAROUND = false; private static double DaylightSavingTA(double t) { if (!TZO_WORKAROUND) { java.util.Date date = new java.util.Date((long) t); if (thisTimeZone.inDaylightTime(date)) return msPerHour; else return 0; } else { /* Use getOffset if inDaylightTime() is broken, because it * seems to work acceptably. We don't switch over to it * entirely, because it requires (expensive) exploded date arguments, * and the api makes it impossible to handle dst * changeovers cleanly. */ // Hardcode the assumption that the changeover always // happens at 2:00 AM: t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0); int year = YearFromTime(t); double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0, year, MonthFromTime(t), DateFromTime(t), WeekDay(t), (int)TimeWithinDay(t)); if ((offset - LocalTZA) != 0) return msPerHour; else return 0; // return offset - LocalTZA; } } private static double LocalTime(double t) { return t + LocalTZA + DaylightSavingTA(t); } public static double internalUTC(double t) { return t - LocalTZA - DaylightSavingTA(t - LocalTZA); } private static int HourFromTime(double t) { double result; result = java.lang.Math.floor(t / msPerHour) % HoursPerDay; if (result < 0) result += HoursPerDay; return (int) result; } private static int MinFromTime(double t) { double result; result = java.lang.Math.floor(t / msPerMinute) % MinutesPerHour; if (result < 0) result += MinutesPerHour; return (int) result; } private static int SecFromTime(double t) { double result; result = java.lang.Math.floor(t / msPerSecond) % SecondsPerMinute; if (result < 0) result += SecondsPerMinute; return (int) result; } private static int msFromTime(double t) { double result; result = t % msPerSecond; if (result < 0) result += msPerSecond; return (int) result; } private static double MakeTime(double hour, double min, double sec, double ms) { return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms; } private static double MakeDay(double year, double month, double date) { double result; boolean leap; double yearday; double monthday; year += java.lang.Math.floor(month / 12); month = month % 12; if (month < 0) month += 12; leap = (DaysInYear((int) year) == 366); yearday = java.lang.Math.floor(TimeFromYear(year) / msPerDay); monthday = DayFromMonth((int) month, leap); result = yearday + monthday + date - 1; return result; } private static double MakeDate(double day, double time) { return day * msPerDay + time; } private static double TimeClip(double d) { if (d != d || d == Double.POSITIVE_INFINITY || d == Double.NEGATIVE_INFINITY || java.lang.Math.abs(d) > HalfTimeDomain) { return Double.NaN; } if (d > 0.0) return java.lang.Math.floor(d + 0.); else return java.lang.Math.ceil(d + 0.); } /* end of ECMA helper functions */ /* find UTC time from given date... no 1900 correction! */ public static double date_msecFromDate(double year, double mon, double mday, double hour, double min, double sec, double msec) { double day; double time; double result; day = MakeDay(year, mon, mday); time = MakeTime(hour, min, sec, msec); result = MakeDate(day, time); return result; } private static final int MAXARGS = 7; private static double jsStaticJSFunction_UTC(JS[] args) throws JSExn { double array[] = new double[MAXARGS]; int loop; double d; for (loop = 0; loop < MAXARGS; loop++) { if (loop < args.length) { d = _toNumber(args[loop]); if (d != d || Double.isInfinite(d)) { return Double.NaN; } array[loop] = JSU.toDouble(args[loop]); } 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 d; // return JSU.N(d); } /* * Use ported code from jsdate.c rather than the locale-specific * date-parsing code from Java, to keep js and rhino consistent. * Is this the right strategy? */ /* for use by date_parse */ /* replace this with byte arrays? Cheaper? */ private static String 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 */ }; private 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, /* UT/UTC */ 10000 + 5 * 60, 10000 + 4 * 60, /* EDT */ 10000 + 6 * 60, 10000 + 5 * 60, 10000 + 7 * 60, 10000 + 6 * 60, 10000 + 8 * 60, 10000 + 7 * 60 }; /* helper for date_parse */ private static boolean date_regionMatches(String s1, int s1off, String s2, int s2off, int count) { boolean result = false; /* return true if matches, otherwise, false */ int s1len = s1.length(); int s2len = s2.length(); while (count > 0 && s1off < s1len && s2off < s2len) { if (Character.toLowerCase(s1.charAt(s1off)) != Character.toLowerCase(s2.charAt(s2off))) break; s1off++; s2off++; count--; } if (count == 0) { result = true; } return result; } private static double date_parseString(String s) { double msec; int year = -1; int mon = -1; int mday = -1; int hour = -1; int min = -1; int sec = -1; char c = 0; char si = 0; int i = 0; int n = -1; double tzoffset = -1; char prevc = 0; int limit = 0; boolean seenplusminus = false; if (s == null) // ??? Will s be null? return Double.NaN; limit = s.length(); while (i < limit) { c = s.charAt(i); i++; if (c <= ' ' || c == ',' || c == '-') { if (i < limit) { si = s.charAt(i); if (c == '-' && '0' <= si && si <= '9') { prevc = c; } } continue; } if (c == '(') { /* comments) */ int depth = 1; while (i < limit) { c = s.charAt(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.charAt(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) return Double.NaN; tzoffset = n; } else if (n >= 70 || (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) { if (year >= 0) return Double.NaN; else if (c <= ' ' || c == ',' || c == '/' || i >= limit) year = n < 100 ? n + 1900 : n; else return Double.NaN; } else if (c == ':') { if (hour < 0) hour = /*byte*/ n; else if (min < 0) min = /*byte*/ n; else return Double.NaN; } else if (c == '/') { if (mon < 0) mon = /*byte*/ n-1; else if (mday < 0) mday = /*byte*/ n; else return Double.NaN; } else if (i < limit && c != ',' && c > ' ' && c != '-') { return Double.NaN; } 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 { return Double.NaN; } prevc = 0; } else if (c == '/' || c == ':' || c == '+' || c == '-') { prevc = c; } else { int st = i - 1; int k; while (i < limit) { c = s.charAt(i); if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) break; i++; } if (i <= st + 1) return Double.NaN; for (k = wtb.length; --k >= 0;) if (date_regionMatches(wtb[k], 0, s, st, i-st)) { 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. */ if (hour > 12 || hour < 0) { return Double.NaN; } 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 { return Double.NaN; } } else { tzoffset = action - 10000; } } break; } if (k < 0) return Double.NaN; prevc = 0; } } if (year < 0 || mon < 0 || mday < 0) return Double.NaN; 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 */ double time; time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); return internalUTC(time); } msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); msec += tzoffset * msPerMinute; return msec; } private static double jsStaticJSFunction_parse(String s) { return date_parseString(s); } private static final int FORMATSPEC_FULL = 0; private static final int FORMATSPEC_DATE = 1; private static final int FORMATSPEC_TIME = 2; private static String date_format(double t, int format) { if (t != t) return NaN_date_str; StringBuffer result = new StringBuffer(60); double local = LocalTime(t); /* offset from GMT in minutes. The offset includes daylight savings, if it applies. */ int minutes = (int) java.lang.Math.floor((LocalTZA + DaylightSavingTA(t)) / msPerMinute); /* map 510 minutes to 0830 hours */ int offset = (minutes / 60) * 100 + minutes % 60; String dateStr = Integer.toString(DateFromTime(local)); String hourStr = Integer.toString(HourFromTime(local)); String minStr = Integer.toString(MinFromTime(local)); String secStr = Integer.toString(SecFromTime(local)); String offsetStr = Integer.toString(offset > 0 ? offset : -offset); int year = YearFromTime(local); String yearStr = Integer.toString(year > 0 ? year : -year); /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */ /* Tue Oct 31 2000 */ /* 09:41:40 GMT-0800 (PST) */ if (format != FORMATSPEC_TIME) { result.append(days[WeekDay(local)]); result.append(' '); result.append(months[MonthFromTime(local)]); if (dateStr.length() == 1) result.append(" 0"); else result.append(' '); result.append(dateStr); result.append(' '); } if (format != FORMATSPEC_DATE) { if (hourStr.length() == 1) result.append('0'); result.append(hourStr); if (minStr.length() == 1) result.append(":0"); else result.append(':'); result.append(minStr); if (secStr.length() == 1) result.append(":0"); else result.append(':'); result.append(secStr); if (offset > 0) result.append(" GMT+"); else result.append(" GMT-"); for (int i = offsetStr.length(); i < 4; i++) result.append('0'); result.append(offsetStr); if (timeZoneFormatter == null) timeZoneFormatter = new java.text.SimpleDateFormat("zzz"); if (timeZoneFormatter != null) { result.append(" ("); java.util.Date date = new java.util.Date((long) t); result.append(timeZoneFormatter.format(date)); result.append(')'); } if (format != FORMATSPEC_TIME) result.append(' '); } if (format != FORMATSPEC_TIME) { if (year < 0) result.append('-'); for (int i = yearStr.length(); i < 4; i++) result.append('0'); result.append(yearStr); } return result.toString(); } private static double _toNumber(JS o) throws JSExn { return JSU.toDouble(o); } private static double _toNumber(JS[] o, int index) throws JSExn { return JSU.toDouble(o[index]); } public JSDate(long l) throws JSExn { this.date = (double)l; thisTimeZone = java.util.TimeZone.getDefault(); LocalTZA = thisTimeZone.getRawOffset(); } public JSDate(JS[] args) throws JSExn { JSDate obj = this; switch (args.length) { case 0: { obj.date = Now(); return; } case 1: { double date; if(JSU.isString(args[0])) date = date_parseString(JSU.toString(args[0])); else date = _toNumber(args[0]); obj.date = TimeClip(date); return; } default: { // multiple arguments; year, month, day etc. double array[] = new double[MAXARGS]; array[0] = JSU.toDouble(args[0]); array[1] = JSU.toDouble(args[1]); if (args.length >= 2) array[2] = JSU.toDouble(args[2]); for (int i=0; i < args.length; i++) { double d = _toNumber(args[i]); if (d != d || Double.isInfinite(d)) { obj.date = Double.NaN; return; } array[i] = d; } /* 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 */ if (array[2] < 1) array[2] = 1; double day = MakeDay(array[0], array[1], array[2]); double time = MakeTime(array[3], array[4], array[5], array[6]); time = MakeDate(day, time); time = internalUTC(time); obj.date = TimeClip(time); return; } } } /* constants for toString, toUTCString */ private static String NaN_date_str = "Invalid Date"; private static String[] days = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; private static String[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; private static String toLocale_helper(double t, java.text.DateFormat formatter) { if (t != t) return NaN_date_str; java.util.Date tempdate = new java.util.Date((long) t); return formatter.format(tempdate); } private static String toLocaleString(double date) { if (localeDateTimeFormatter == null) localeDateTimeFormatter = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); return toLocale_helper(date, localeDateTimeFormatter); } private static String toLocaleTimeString(double date) { if (localeTimeFormatter == null) localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG); return toLocale_helper(date, localeTimeFormatter); } private static String toLocaleDateString(double date) { if (localeDateFormatter == null) localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG); return toLocale_helper(date, localeDateFormatter); } private static String toUTCString(double date) { StringBuffer result = new StringBuffer(60); String dateStr = Integer.toString(DateFromTime(date)); String hourStr = Integer.toString(HourFromTime(date)); String minStr = Integer.toString(MinFromTime(date)); String secStr = Integer.toString(SecFromTime(date)); int year = YearFromTime(date); String yearStr = Integer.toString(year > 0 ? year : -year); result.append(days[WeekDay(date)]); result.append(", "); if (dateStr.length() == 1) result.append('0'); result.append(dateStr); result.append(' '); result.append(months[MonthFromTime(date)]); if (year < 0) result.append(" -"); else result.append(' '); int i; for (i = yearStr.length(); i < 4; i++) result.append('0'); result.append(yearStr); if (hourStr.length() == 1) result.append(" 0"); else result.append(' '); result.append(hourStr); if (minStr.length() == 1) result.append(":0"); else result.append(':'); result.append(minStr); if (secStr.length() == 1) result.append(":0"); else result.append(':'); result.append(secStr); result.append(" GMT"); return result.toString(); } private static double getYear(double date) { int result = YearFromTime(LocalTime(date)); result -= 1900; return result; } private static double getTimezoneOffset(double date) { return (date - LocalTime(date)) / msPerMinute; } public double setTime(double time) { this.date = TimeClip(time); return this.date; } private double makeTime(JS[] args, int maxargs, boolean local) throws JSExn { int i; double conv[] = new double[4]; double hour, min, sec, msec; double lorutime; /* Local or UTC version of date */ double time; double result; double date = this.date; /* just return NaN if the date is already NaN */ if (date != date) return date; /* Satisfy the ECMA rule that if a function is called with * fewer arguments than the specified formal arguments, the * remaining arguments are set to undefined. Seems like all * the Date.setWhatever functions in ECMA are only varargs * beyond the first argument; this should be set to undefined * if it's not given. This means that "d = new Date(); * d.setMilliseconds()" returns NaN. Blech. */ if (args.length == 0) args = new JS[] { null }; for (i = 0; i < args.length && i < maxargs; i++) { conv[i] = _toNumber(args[i]); // limit checks that happen in MakeTime in ECMA. if (conv[i] != conv[i] || Double.isInfinite(conv[i])) { this.date = Double.NaN; return this.date; } } if (local) lorutime = LocalTime(date); else lorutime = date; i = 0; int stop = args.length; if (maxargs >= 4 && i < stop) hour = conv[i++]; else hour = HourFromTime(lorutime); if (maxargs >= 3 && i < stop) min = conv[i++]; else min = MinFromTime(lorutime); if (maxargs >= 2 && i < stop) sec = conv[i++]; else sec = SecFromTime(lorutime); if (maxargs >= 1 && i < stop) msec = conv[i++]; else msec = msFromTime(lorutime); time = MakeTime(hour, min, sec, msec); result = MakeDate(Day(lorutime), time); if (local) result = internalUTC(result); date = TimeClip(result); this.date = date; return date; } private double setHours(JS[] args) throws JSExn { return makeTime(args, 4, true); } private double setUTCHours(JS[] args) throws JSExn { return makeTime(args, 4, false); } private double makeDate(JS[] args, int maxargs, boolean local) throws JSExn { int i; double conv[] = new double[3]; double year, month, day; double lorutime; /* local or UTC version of date */ double result; double date = this.date; /* See arg padding comment in makeTime.*/ if (args.length == 0) args = new JS[] { null }; for (i = 0; i < args.length && i < maxargs; i++) { conv[i] = _toNumber(args[i]); // limit checks that happen in MakeDate in ECMA. if (conv[i] != conv[i] || Double.isInfinite(conv[i])) { this.date = Double.NaN; return this.date; } } /* return NaN if date is NaN and we're not setting the year, * If we are, use 0 as the time. */ if (date != date) { if (args.length < 3) { return Double.NaN; } else { lorutime = 0; } } else { if (local) lorutime = LocalTime(date); else lorutime = date; } i = 0; int stop = args.length; if (maxargs >= 3 && i < stop) year = conv[i++]; else year = YearFromTime(lorutime); if (maxargs >= 2 && i < stop) month = conv[i++]; else month = MonthFromTime(lorutime); if (maxargs >= 1 && i < stop) day = conv[i++]; else day = DateFromTime(lorutime); day = MakeDay(year, month, day); /* day within year */ result = MakeDate(day, TimeWithinDay(lorutime)); if (local) result = internalUTC(result); date = TimeClip(result); this.date = date; return date; } private double setYear(double year) { double day, result; if (year != year || Double.isInfinite(year)) { this.date = Double.NaN; return this.date; } if (this.date != this.date) { this.date = 0; } else { this.date = LocalTime(this.date); } if (year >= 0 && year <= 99) year += 1900; day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date)); result = MakeDate(day, TimeWithinDay(this.date)); result = internalUTC(result); this.date = TimeClip(result); return this.date; } // private static final int // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6 // #/string_id_map# /* cached values */ private static java.util.TimeZone thisTimeZone; private static double LocalTZA; private static java.text.DateFormat timeZoneFormatter; private static java.text.DateFormat localeDateTimeFormatter; private static java.text.DateFormat localeDateFormatter; private static java.text.DateFormat localeTimeFormatter; private double date; public long getRawTime() { return (long)this.date; } }