1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * The contents of this file are subject to the Netscape Public
4 * License Version 1.1 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of
6 * the License at http://www.mozilla.org/NPL/
8 * Software distributed under the License is distributed on an "AS
9 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
10 * implied. See the License for the specific language governing
11 * rights and limitations under the License.
13 * The Original Code is Rhino code, released
16 * The Initial Developer of the Original Code is Netscape
17 * Communications Corporation. Portions created by Netscape are
18 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
25 * Alternatively, the contents of this file may be used under the
26 * terms of the GNU Public License (the "GPL"), in which case the
27 * provisions of the GPL are applicable instead of those above.
28 * If you wish to allow use of your version of this file only
29 * under the terms of the GPL and not to allow others to use your
30 * version of this file under the NPL, indicate your decision by
31 * deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL. If you do not delete
33 * the provisions above, a recipient may use your version of this
34 * file under either the NPL or the GPL.
39 import java.text.DateFormat;
42 * This class implements the Date native object.
45 * @author Adam Megacz (many modifications
47 public class JSDate extends JS.Immutable {
48 private static final JS.Method METHOD = new JS.Method();
51 if (thisTimeZone == null) {
52 // j.u.TimeZone is synchronized, so setting class statics from it
54 thisTimeZone = java.util.TimeZone.getDefault();
55 LocalTZA = thisTimeZone.getRawOffset();
59 public String coerceToString() { return date_format(date, FORMATSPEC_FULL); }
61 public JS call(JS method, JS[] args) throws JSExn {
64 //#switch(JS.toString(method))
65 case "toString": return JS.S(date_format(date, FORMATSPEC_FULL));
66 case "toTimeString": return JS.S(date_format(date, FORMATSPEC_TIME));
67 case "toDateString": return JS.S(date_format(date, FORMATSPEC_DATE));
68 case "toLocaleString": return JS.S(toLocaleString(date));
69 case "toLocaleTimeString": return JS.S(toLocaleTimeString(date));
70 case "toLocaleDateString": return JS.S(toLocaleDateString(date));
71 case "toUTCString": return JS.S(toUTCString(date));
72 case "valueOf": return N(this.date);
73 case "getTime": return N(this.date);
74 case "getYear": return N(getYear(date));
75 case "getFullYear": return N(YearFromTime(LocalTime(date)));
76 case "getUTCFullYear": return N(YearFromTime(date));
77 case "getMonth": return N(MonthFromTime(LocalTime(date)));
78 case "getUTCMonth": return N(MonthFromTime(date));
79 case "getDate": return N(DateFromTime(LocalTime(date)));
80 case "getUTCDate": return N(DateFromTime(date));
81 case "getDay": return N(WeekDay(LocalTime(date)));
82 case "getUTCDay": return N(WeekDay(date));
83 case "getHours": return N(HourFromTime(LocalTime(date)));
84 case "getUTCHours": return N(HourFromTime(date));
85 case "getMinutes": return N(MinFromTime(LocalTime(date)));
86 case "getUTCMinutes": return N(MinFromTime(date));
87 case "getSeconds": return N(SecFromTime(LocalTime(date)));
88 case "getUTCSeconds": return N(SecFromTime(date));
89 case "getMilliseconds": return N(msFromTime(LocalTime(date)));
90 case "getUTCMilliseconds": return N(msFromTime(date));
91 case "getTimezoneOffset": return N(getTimezoneOffset(date));
93 return super.call(method, args);
96 //#switch(JS.toString(method))
97 case "setTime": return N(this.setTime(toDouble(a0)));
98 case "setYear": return N(this.setYear(toDouble(a0)));
103 JS[] args = new JS[nargs];
104 for(int i=0; i<nargs; i++) args[i] = i==0 ? a0 : i==1 ? a1 : i==2 ? a2 : rest[i-3];
105 //#switch(JS.toString(method))
106 case "setMilliseconds": return N(this.makeTime(args, 1, true));
107 case "setUTCMilliseconds": return N(this.makeTime(args, 1, false));
108 case "setSeconds": return N(this.makeTime(args, 2, true));
109 case "setUTCSeconds": return N(this.makeTime(args, 2, false));
110 case "setMinutes": return N(this.makeTime(args, 3, true));
111 case "setUTCMinutes": return N(this.makeTime(args, 3, false));
112 case "setHours": return N(this.makeTime(args, 4, true));
113 case "setUTCHours": return N(this.makeTime(args, 4, false));
114 case "setDate": return N(this.makeDate(args, 1, true));
115 case "setUTCDate": return N(this.makeDate(args, 1, false));
116 case "setMonth": return N(this.makeDate(args, 2, true));
117 case "setUTCMonth": return N(this.makeDate(args, 2, false));
118 case "setFullYear": return N(this.makeDate(args, 3, true));
119 case "setUTCFullYear": return N(this.makeDate(args, 3, false));
123 return super.call(method, args);
126 public JS get(JS key) throws JSExn {
127 //#switch(JS.toString(key))
128 case "toString": return METHOD;
129 case "toTimeString": return METHOD;
130 case "toDateString": return METHOD;
131 case "toLocaleString": return METHOD;
132 case "toLocaleTimeString": return METHOD;
133 case "toLocaleDateString": return METHOD;
134 case "toUTCString": return METHOD;
135 case "valueOf": return METHOD;
136 case "getTime": return METHOD;
137 case "getYear": return METHOD;
138 case "getFullYear": return METHOD;
139 case "getUTCFullYear": return METHOD;
140 case "getMonth": return METHOD;
141 case "getUTCMonth": return METHOD;
142 case "getDate": return METHOD;
143 case "getUTCDate": return METHOD;
144 case "getDay": return METHOD;
145 case "getUTCDay": return METHOD;
146 case "getHours": return METHOD;
147 case "getUTCHours": return METHOD;
148 case "getMinutes": return METHOD;
149 case "getUTCMinutes": return METHOD;
150 case "getSeconds": return METHOD;
151 case "getUTCSeconds": return METHOD;
152 case "getMilliseconds": return METHOD;
153 case "getUTCMilliseconds": return METHOD;
154 case "getTimezoneOffset": return METHOD;
155 case "setTime": return METHOD;
156 case "setYear": return METHOD;
157 case "setMilliseconds": return METHOD;
158 case "setUTCMilliseconds": return METHOD;
159 case "setSeconds": return METHOD;
160 case "setUTCSeconds": return METHOD;
161 case "setMinutes": return METHOD;
162 case "setUTCMinutes": return METHOD;
163 case "setHours": return METHOD;
164 case "setUTCHours": return METHOD;
165 case "setDate": return METHOD;
166 case "setUTCDate": return METHOD;
167 case "setMonth": return METHOD;
168 case "setUTCMonth": return METHOD;
169 case "setFullYear": return METHOD;
170 case "setUTCFullYear": return METHOD;
172 return super.get(key);
175 /* ECMA helper functions */
177 private static final double HalfTimeDomain = 8.64e15;
178 private static final double HoursPerDay = 24.0;
179 private static final double MinutesPerHour = 60.0;
180 private static final double SecondsPerMinute = 60.0;
181 private static final double msPerSecond = 1000.0;
182 private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour);
183 private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute);
184 private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute);
185 private static final double msPerDay = (SecondsPerDay * msPerSecond);
186 private static final double msPerHour = (SecondsPerHour * msPerSecond);
187 private static final double msPerMinute = (SecondsPerMinute * msPerSecond);
189 private static double Day(double t) {
190 return java.lang.Math.floor(t / msPerDay);
193 private static double TimeWithinDay(double t) {
195 result = t % msPerDay;
201 private static int DaysInYear(int y) {
202 if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
209 /* math here has to be f.p, because we need
210 * floor((1968 - 1969) / 4) == -1
212 private static double DayFromYear(double y) {
213 return ((365 * ((y)-1970) + java.lang.Math.floor(((y)-1969)/4.0)
214 - java.lang.Math.floor(((y)-1901)/100.0) + java.lang.Math.floor(((y)-1601)/400.0)));
217 private static double TimeFromYear(double y) {
218 return DayFromYear(y) * msPerDay;
221 private static int YearFromTime(double t) {
222 int lo = (int) java.lang.Math.floor((t / msPerDay) / 366) + 1970;
223 int hi = (int) java.lang.Math.floor((t / msPerDay) / 365) + 1970;
226 /* above doesn't work for negative dates... */
233 /* Use a simple binary search algorithm to find the right
234 year. This seems like brute force... but the computation
235 of hi and lo years above lands within one year of the
236 correct answer for years within a thousand years of
237 1970; the loop below only requires six iterations
241 if (TimeFromYear(mid) > t) {
244 if (TimeFromYear(mid) <= t) {
246 if (TimeFromYear(temp) > t) {
256 private static boolean InLeapYear(double t) {
257 return DaysInYear(YearFromTime(t)) == 366;
260 private static int DayWithinYear(double t) {
261 int year = YearFromTime(t);
262 return (int) (Day(t) - DayFromYear(year));
265 * The following array contains the day of year for the first day of
266 * each month, where index 0 is January, and day 0 is January 1.
269 private static double DayFromMonth(int m, boolean leap) {
272 if (m >= 7) { day += m / 2 - 1; }
273 else if (m >= 2) { day += (m - 1) / 2 - 1; }
276 if (leap && m >= 2) { ++day; }
281 private static int MonthFromTime(double t) {
284 d = DayWithinYear(t);
289 // Originally coded as step += (InLeapYear(t) ? 29 : 28);
290 // but some jits always returned 28!
298 if (d < (step += 31))
300 if (d < (step += 30))
302 if (d < (step += 31))
304 if (d < (step += 30))
306 if (d < (step += 31))
308 if (d < (step += 31))
310 if (d < (step += 30))
312 if (d < (step += 31))
314 if (d < (step += 30))
319 private static int DateFromTime(double t) {
322 d = DayWithinYear(t);
323 if (d <= (next = 30))
327 // Originally coded as next += (InLeapYear(t) ? 29 : 28);
328 // but some jits always returned 28!
337 if (d <= (next += 31))
340 if (d <= (next += 30))
343 if (d <= (next += 31))
346 if (d <= (next += 30))
349 if (d <= (next += 31))
352 if (d <= (next += 31))
355 if (d <= (next += 30))
358 if (d <= (next += 31))
361 if (d <= (next += 30))
368 private static int WeekDay(double t) {
377 private static double Now() {
378 return (double) System.currentTimeMillis();
381 /* Should be possible to determine the need for this dynamically
382 * if we go with the workaround... I'm not using it now, because I
383 * can't think of any clean way to make toLocaleString() and the
384 * time zone (comment) in toString match the generated string
385 * values. Currently it's wrong-but-consistent in all but the
386 * most recent betas of the JRE - seems to work in 1.1.7.
388 private final static boolean TZO_WORKAROUND = false;
389 private static double DaylightSavingTA(double t) {
390 if (!TZO_WORKAROUND) {
391 java.util.Date date = new java.util.Date((long) t);
392 if (thisTimeZone.inDaylightTime(date))
397 /* Use getOffset if inDaylightTime() is broken, because it
398 * seems to work acceptably. We don't switch over to it
399 * entirely, because it requires (expensive) exploded date arguments,
400 * and the api makes it impossible to handle dst
401 * changeovers cleanly.
404 // Hardcode the assumption that the changeover always
405 // happens at 2:00 AM:
406 t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);
408 int year = YearFromTime(t);
409 double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,
414 (int)TimeWithinDay(t));
416 if ((offset - LocalTZA) != 0)
420 // return offset - LocalTZA;
424 private static double LocalTime(double t) {
425 return t + LocalTZA + DaylightSavingTA(t);
428 public static double internalUTC(double t) {
429 return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
432 private static int HourFromTime(double t) {
434 result = java.lang.Math.floor(t / msPerHour) % HoursPerDay;
436 result += HoursPerDay;
440 private static int MinFromTime(double t) {
442 result = java.lang.Math.floor(t / msPerMinute) % MinutesPerHour;
444 result += MinutesPerHour;
448 private static int SecFromTime(double t) {
450 result = java.lang.Math.floor(t / msPerSecond) % SecondsPerMinute;
452 result += SecondsPerMinute;
456 private static int msFromTime(double t) {
458 result = t % msPerSecond;
460 result += msPerSecond;
464 private static double MakeTime(double hour, double min,
465 double sec, double ms)
467 return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
471 private static double MakeDay(double year, double month, double date) {
477 year += java.lang.Math.floor(month / 12);
483 leap = (DaysInYear((int) year) == 366);
485 yearday = java.lang.Math.floor(TimeFromYear(year) / msPerDay);
486 monthday = DayFromMonth((int) month, leap);
494 private static double MakeDate(double day, double time) {
495 return day * msPerDay + time;
498 private static double TimeClip(double d) {
500 d == Double.POSITIVE_INFINITY ||
501 d == Double.NEGATIVE_INFINITY ||
502 java.lang.Math.abs(d) > HalfTimeDomain)
507 return java.lang.Math.floor(d + 0.);
509 return java.lang.Math.ceil(d + 0.);
512 /* end of ECMA helper functions */
514 /* find UTC time from given date... no 1900 correction! */
515 public static double date_msecFromDate(double year, double mon,
516 double mday, double hour,
517 double min, double sec,
524 day = MakeDay(year, mon, mday);
525 time = MakeTime(hour, min, sec, msec);
526 result = MakeDate(day, time);
531 private static final int MAXARGS = 7;
532 private static double jsStaticJSFunction_UTC(JS[] args) throws JSExn {
533 double array[] = new double[MAXARGS];
537 for (loop = 0; loop < MAXARGS; loop++) {
538 if (loop < args.length) {
539 d = _toNumber(args[loop]);
540 if (d != d || Double.isInfinite(d)) {
543 array[loop] = toDouble(args[loop]);
549 /* adjust 2-digit years into the 20th century */
550 if (array[0] >= 0 && array[0] <= 99)
553 /* if we got a 0 for 'date' (which is out of range)
554 * pretend it's a 1. (So Date.UTC(1972, 5) works) */
558 d = date_msecFromDate(array[0], array[1], array[2],
559 array[3], array[4], array[5], array[6]);
566 * Use ported code from jsdate.c rather than the locale-specific
567 * date-parsing code from Java, to keep js and rhino consistent.
568 * Is this the right strategy?
571 /* for use by date_parse */
573 /* replace this with byte arrays? Cheaper? */
574 private static String wtb[] = {
576 "monday", "tuesday", "wednesday", "thursday", "friday",
577 "saturday", "sunday",
578 "january", "february", "march", "april", "may", "june",
579 "july", "august", "september", "october", "november", "december",
580 "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
581 "mst", "mdt", "pst", "pdt"
582 /* time zone table needs to be expanded */
585 private static int ttb[] = {
586 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
587 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
588 10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */
589 10000 + 5 * 60, 10000 + 4 * 60, /* EDT */
590 10000 + 6 * 60, 10000 + 5 * 60,
591 10000 + 7 * 60, 10000 + 6 * 60,
592 10000 + 8 * 60, 10000 + 7 * 60
595 /* helper for date_parse */
596 private static boolean date_regionMatches(String s1, int s1off,
597 String s2, int s2off,
600 boolean result = false;
601 /* return true if matches, otherwise, false */
602 int s1len = s1.length();
603 int s2len = s2.length();
605 while (count > 0 && s1off < s1len && s2off < s2len) {
606 if (Character.toLowerCase(s1.charAt(s1off)) !=
607 Character.toLowerCase(s2.charAt(s2off)))
620 private static double date_parseString(String s) {
633 double tzoffset = -1;
636 boolean seenplusminus = false;
638 if (s == null) // ??? Will s be null?
644 if (c <= ' ' || c == ',' || c == '-') {
647 if (c == '-' && '0' <= si && si <= '9') {
653 if (c == '(') { /* comments) */
666 if ('0' <= c && c <= '9') {
668 while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {
669 n = n * 10 + c - '0';
673 /* allow TZA before the year, so
674 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
677 /* uses of seenplusminus allow : in TZA, so Java
678 * no-timezone style of GMT+4:30 works
680 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
681 /* make ':' case below change tzoffset */
682 seenplusminus = true;
686 n = n * 60; /* EG. "GMT-3" */
688 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
689 if (prevc == '+') /* plus means east of GMT */
691 if (tzoffset != 0 && tzoffset != -1)
694 } else if (n >= 70 ||
695 (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
698 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
699 year = n < 100 ? n + 1900 : n;
702 } else if (c == ':') {
709 } else if (c == '/') {
716 } else if (i < limit && c != ',' && c > ' ' && c != '-') {
718 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
723 } else if (hour >= 0 && min < 0) {
725 } else if (min >= 0 && sec < 0) {
727 } else if (mday < 0) {
733 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
740 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
746 for (k = wtb.length; --k >= 0;)
747 if (date_regionMatches(wtb[k], 0, s, st, i-st)) {
752 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
753 * 12:30, instead of blindly adding 12 if PM.
755 if (hour > 12 || hour < 0) {
758 if (action == -1 && hour == 12) { // am
760 } else if (action == -2 && hour != 12) {// pm
764 } else if (action <= 13) { /* month! */
766 mon = /*byte*/ (action - 2);
771 tzoffset = action - 10000;
781 if (year < 0 || mon < 0 || mday < 0)
789 if (tzoffset == -1) { /* no time zone specified, have to use local */
791 time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
792 return internalUTC(time);
795 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
796 msec += tzoffset * msPerMinute;
800 private static double jsStaticJSFunction_parse(String s) {
801 return date_parseString(s);
804 private static final int FORMATSPEC_FULL = 0;
805 private static final int FORMATSPEC_DATE = 1;
806 private static final int FORMATSPEC_TIME = 2;
808 private static String date_format(double t, int format) {
812 StringBuffer result = new StringBuffer(60);
813 double local = LocalTime(t);
815 /* offset from GMT in minutes. The offset includes daylight savings,
817 int minutes = (int) java.lang.Math.floor((LocalTZA + DaylightSavingTA(t))
819 /* map 510 minutes to 0830 hours */
820 int offset = (minutes / 60) * 100 + minutes % 60;
822 String dateStr = Integer.toString(DateFromTime(local));
823 String hourStr = Integer.toString(HourFromTime(local));
824 String minStr = Integer.toString(MinFromTime(local));
825 String secStr = Integer.toString(SecFromTime(local));
826 String offsetStr = Integer.toString(offset > 0 ? offset : -offset);
827 int year = YearFromTime(local);
828 String yearStr = Integer.toString(year > 0 ? year : -year);
830 /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */
831 /* Tue Oct 31 2000 */
832 /* 09:41:40 GMT-0800 (PST) */
834 if (format != FORMATSPEC_TIME) {
835 result.append(days[WeekDay(local)]);
837 result.append(months[MonthFromTime(local)]);
838 if (dateStr.length() == 1)
842 result.append(dateStr);
846 if (format != FORMATSPEC_DATE) {
847 if (hourStr.length() == 1)
849 result.append(hourStr);
850 if (minStr.length() == 1)
854 result.append(minStr);
855 if (secStr.length() == 1)
859 result.append(secStr);
861 result.append(" GMT+");
863 result.append(" GMT-");
864 for (int i = offsetStr.length(); i < 4; i++)
866 result.append(offsetStr);
868 if (timeZoneFormatter == null)
869 timeZoneFormatter = new java.text.SimpleDateFormat("zzz");
871 if (timeZoneFormatter != null) {
873 java.util.Date date = new java.util.Date((long) t);
874 result.append(timeZoneFormatter.format(date));
877 if (format != FORMATSPEC_TIME)
881 if (format != FORMATSPEC_TIME) {
884 for (int i = yearStr.length(); i < 4; i++)
886 result.append(yearStr);
889 return result.toString();
892 private static double _toNumber(JS o) throws JSExn { return JS.toDouble(o); }
893 private static double _toNumber(JS[] o, int index) throws JSExn { return JS.toDouble(o[index]); }
894 private static double toDouble(double d) { return d; }
896 public JSDate(JS[] args) throws JSExn {
899 switch (args.length) {
907 date = date_parseString(JS.toString(a0));
909 date = _toNumber(args[0]);
910 obj.date = TimeClip(date);
914 // multiple arguments; year, month, day etc.
915 double array[] = new double[MAXARGS];
916 array[0] = Script.toDouble(args[0]);
917 array[1] = Script.toDouble(args[1]);
918 if (args.length >= 2) array[2] = Script.toDouble(args[2]);
919 for (int i=0; i < args.length; i++) {
920 double d = _toNumber(args[i]);
921 if (d != d || Double.isInfinite(d)) {
922 obj.date = Double.NaN;
928 /* adjust 2-digit years into the 20th century */
929 if (array[0] >= 0 && array[0] <= 99)
932 /* if we got a 0 for 'date' (which is out of range)
933 * pretend it's a 1 */
937 double day = MakeDay(array[0], array[1], array[2]);
938 double time = MakeTime(array[3], array[4], array[5], array[6]);
939 time = MakeDate(day, time);
940 time = internalUTC(time);
941 obj.date = TimeClip(time);
948 /* constants for toString, toUTCString */
949 private static String NaN_date_str = "Invalid Date";
951 private static String[] days = {
952 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
955 private static String[] months = {
956 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
957 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
960 private static String toLocale_helper(double t,
961 java.text.DateFormat formatter)
966 java.util.Date tempdate = new java.util.Date((long) t);
967 return formatter.format(tempdate);
970 private static String toLocaleString(double date) {
971 if (localeDateTimeFormatter == null)
972 localeDateTimeFormatter =
973 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
975 return toLocale_helper(date, localeDateTimeFormatter);
978 private static String toLocaleTimeString(double date) {
979 if (localeTimeFormatter == null)
980 localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
982 return toLocale_helper(date, localeTimeFormatter);
985 private static String toLocaleDateString(double date) {
986 if (localeDateFormatter == null)
987 localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
989 return toLocale_helper(date, localeDateFormatter);
992 private static String toUTCString(double date) {
993 StringBuffer result = new StringBuffer(60);
995 String dateStr = Integer.toString(DateFromTime(date));
996 String hourStr = Integer.toString(HourFromTime(date));
997 String minStr = Integer.toString(MinFromTime(date));
998 String secStr = Integer.toString(SecFromTime(date));
999 int year = YearFromTime(date);
1000 String yearStr = Integer.toString(year > 0 ? year : -year);
1002 result.append(days[WeekDay(date)]);
1003 result.append(", ");
1004 if (dateStr.length() == 1)
1006 result.append(dateStr);
1008 result.append(months[MonthFromTime(date)]);
1010 result.append(" -");
1014 for (i = yearStr.length(); i < 4; i++)
1016 result.append(yearStr);
1018 if (hourStr.length() == 1)
1019 result.append(" 0");
1022 result.append(hourStr);
1023 if (minStr.length() == 1)
1024 result.append(":0");
1027 result.append(minStr);
1028 if (secStr.length() == 1)
1029 result.append(":0");
1032 result.append(secStr);
1034 result.append(" GMT");
1035 return result.toString();
1038 private static double getYear(double date) {
1039 int result = YearFromTime(LocalTime(date));
1044 private static double getTimezoneOffset(double date) {
1045 return (date - LocalTime(date)) / msPerMinute;
1048 public double setTime(double time) {
1049 this.date = TimeClip(time);
1053 private double makeTime(JS[] args, int maxargs, boolean local) throws JSExn {
1055 double conv[] = new double[4];
1056 double hour, min, sec, msec;
1057 double lorutime; /* Local or UTC version of date */
1062 double date = this.date;
1064 /* just return NaN if the date is already NaN */
1068 /* Satisfy the ECMA rule that if a function is called with
1069 * fewer arguments than the specified formal arguments, the
1070 * remaining arguments are set to undefined. Seems like all
1071 * the Date.setWhatever functions in ECMA are only varargs
1072 * beyond the first argument; this should be set to undefined
1073 * if it's not given. This means that "d = new Date();
1074 * d.setMilliseconds()" returns NaN. Blech.
1076 if (args.length == 0)
1077 args = new JS[] { null };
1079 for (i = 0; i < args.length && i < maxargs; i++) {
1080 conv[i] = _toNumber(args[i]);
1082 // limit checks that happen in MakeTime in ECMA.
1083 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1084 this.date = Double.NaN;
1087 conv[i] = toDouble(conv[i]);
1091 lorutime = LocalTime(date);
1096 int stop = args.length;
1098 if (maxargs >= 4 && i < stop)
1101 hour = HourFromTime(lorutime);
1103 if (maxargs >= 3 && i < stop)
1106 min = MinFromTime(lorutime);
1108 if (maxargs >= 2 && i < stop)
1111 sec = SecFromTime(lorutime);
1113 if (maxargs >= 1 && i < stop)
1116 msec = msFromTime(lorutime);
1118 time = MakeTime(hour, min, sec, msec);
1119 result = MakeDate(Day(lorutime), time);
1122 result = internalUTC(result);
1123 date = TimeClip(result);
1129 private double setHours(JS[] args) throws JSExn {
1130 return makeTime(args, 4, true);
1133 private double setUTCHours(JS[] args) throws JSExn {
1134 return makeTime(args, 4, false);
1137 private double makeDate(JS[] args, int maxargs, boolean local) throws JSExn {
1139 double conv[] = new double[3];
1140 double year, month, day;
1141 double lorutime; /* local or UTC version of date */
1144 double date = this.date;
1146 /* See arg padding comment in makeTime.*/
1147 if (args.length == 0)
1148 args = new JS[] { null };
1150 for (i = 0; i < args.length && i < maxargs; i++) {
1151 conv[i] = _toNumber(args[i]);
1153 // limit checks that happen in MakeDate in ECMA.
1154 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1155 this.date = Double.NaN;
1158 conv[i] = toDouble(conv[i]);
1161 /* return NaN if date is NaN and we're not setting the year,
1162 * If we are, use 0 as the time. */
1164 if (args.length < 3) {
1171 lorutime = LocalTime(date);
1177 int stop = args.length;
1179 if (maxargs >= 3 && i < stop)
1182 year = YearFromTime(lorutime);
1184 if (maxargs >= 2 && i < stop)
1187 month = MonthFromTime(lorutime);
1189 if (maxargs >= 1 && i < stop)
1192 day = DateFromTime(lorutime);
1194 day = MakeDay(year, month, day); /* day within year */
1195 result = MakeDate(day, TimeWithinDay(lorutime));
1198 result = internalUTC(result);
1200 date = TimeClip(result);
1206 private double setYear(double year) {
1208 if (year != year || Double.isInfinite(year)) {
1209 this.date = Double.NaN;
1213 if (this.date != this.date) {
1216 this.date = LocalTime(this.date);
1219 if (year >= 0 && year <= 99)
1222 day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
1223 result = MakeDate(day, TimeWithinDay(this.date));
1224 result = internalUTC(result);
1226 this.date = TimeClip(result);
1231 // private static final int
1232 // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
1236 private static java.util.TimeZone thisTimeZone;
1237 private static double LocalTZA;
1238 private static java.text.DateFormat timeZoneFormatter;
1239 private static java.text.DateFormat localeDateTimeFormatter;
1240 private static java.text.DateFormat localeDateFormatter;
1241 private static java.text.DateFormat localeTimeFormatter;
1243 private double date;
1245 public long getRawTime() { return (long)this.date; }