X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Fjs%2FJSDate.java;fp=src%2Forg%2Fibex%2Fjs%2FJSDate.java;h=694122c693daeb2dc278f04a29b2fdeadd320048;hb=3591b88b94a6bb378af3d4abe6eb5233ce583104;hp=0000000000000000000000000000000000000000;hpb=de378041d5ca2aca1a2b5a31ef15ae90a86c977f;p=org.ibex.core.git diff --git a/src/org/ibex/js/JSDate.java b/src/org/ibex/js/JSDate.java new file mode 100644 index 0000000..694122c --- /dev/null +++ b/src/org/ibex/js/JSDate.java @@ -0,0 +1,1253 @@ +/* -*- 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 { + + 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 toString() { return date_format(date, FORMATSPEC_FULL); } + + public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { + switch(nargs) { + case 0: { + //#switch(method) + case "toString": return date_format(date, FORMATSPEC_FULL); + case "toTimeString": return date_format(date, FORMATSPEC_TIME); + case "toDateString": return date_format(date, FORMATSPEC_DATE); + case "toLocaleString": return toLocaleString(date); + case "toLocaleTimeString": return toLocaleTimeString(date); + case "toLocaleDateString": return toLocaleDateString(date); + case "toUTCString": return toUTCString(date); + case "valueOf": return N(this.date); + case "getTime": return N(this.date); + case "getYear": return N(getYear(date)); + case "getFullYear": return N(YearFromTime(LocalTime(date))); + case "getUTCFullYear": return N(YearFromTime(date)); + case "getMonth": return N(MonthFromTime(LocalTime(date))); + case "getUTCMonth": return N(MonthFromTime(date)); + case "getDate": return N(DateFromTime(LocalTime(date))); + case "getUTCDate": return N(DateFromTime(date)); + case "getDay": return N(WeekDay(LocalTime(date))); + case "getUTCDay": return N(WeekDay(date)); + case "getHours": return N(HourFromTime(LocalTime(date))); + case "getUTCHours": return N(HourFromTime(date)); + case "getMinutes": return N(MinFromTime(LocalTime(date))); + case "getUTCMinutes": return N(MinFromTime(date)); + case "getSeconds": return N(SecFromTime(LocalTime(date))); + case "getUTCSeconds": return N(SecFromTime(date)); + case "getMilliseconds": return N(msFromTime(LocalTime(date))); + case "getUTCMilliseconds": return N(msFromTime(date)); + case "getTimezoneOffset": return N(getTimezoneOffset(date)); + //#end + return super.callMethod(method, a0, a1, a2, rest, nargs); + } + case 1: { + //#switch(method) + case "setTime": return N(this.setTime(toDouble(a0))); + case "setYear": return N(this.setYear(toDouble(a0))); + //#end + // fall through + } + default: { + Object[] args = new Object[nargs]; + for(int i=0; i 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(Object[] args) { + 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] = 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 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(Object o) { return JS.toDouble(o); } + private static double _toNumber(Object[] o, int index) { return JS.toDouble(o[index]); } + private static double toDouble(double d) { return d; } + + public JSDate(Object a0, Object a1, Object a2, Object[] rest, int nargs) { + + JSDate obj = this; + switch (nargs) { + case 0: { + obj.date = Now(); + return; + } + case 1: { + double date; + if (a0 instanceof JS) + a0 = ((JS) a0).toString(); + if (!(a0 instanceof String)) { + // if it's not a string, use it as a millisecond date + date = _toNumber(a0); + } else { + // it's a string; parse it. + String str = (String) a0; + date = date_parseString(str); + } + obj.date = TimeClip(date); + return; + } + default: { + // multiple arguments; year, month, day etc. + double array[] = new double[MAXARGS]; + array[0] = toDouble(a0); + array[1] = toDouble(a1); + if (nargs >= 2) array[2] = toDouble(a2); + for(int i=0; i= 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(Object[] args, int maxargs, boolean local) { + 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 Object[] { 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; + } + conv[i] = toDouble(conv[i]); + } + + 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(Object[] args) { + return makeTime(args, 4, true); + } + + private double setUTCHours(Object[] args) { + return makeTime(args, 4, false); + } + + private double makeDate(Object[] args, int maxargs, boolean local) { + 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 Object[] { 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; + } + conv[i] = toDouble(conv[i]); + } + + /* 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; } +} + +