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.util.TimeZone;
40 import java.util.Locale;
41 import java.text.NumberFormat;
42 import java.text.DateFormat;
43 import java.text.SimpleDateFormat;
46 * This class implements the Date native object.
49 * @author Adam Megacz (many modifications
51 public class JSDate extends JSCallable {
54 if (thisTimeZone == null) {
55 // j.u.TimeZone is synchronized, so setting class statics from it
57 thisTimeZone = java.util.TimeZone.getDefault();
58 LocalTZA = thisTimeZone.getRawOffset();
62 public String coerceToString() { return date_format(date, FORMATSPEC_FULL); }
64 public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) {
68 case "toString": return date_format(date, FORMATSPEC_FULL);
69 case "toTimeString": return date_format(date, FORMATSPEC_TIME);
70 case "toDateString": return date_format(date, FORMATSPEC_DATE);
71 case "toLocaleString": return toLocaleString(date);
72 case "toLocaleTimeString": return toLocaleTimeString(date);
73 case "toLocaleDateString": return toLocaleDateString(date);
74 case "toUTCString": return toUTCString(date);
75 case "valueOf": return N(this.date);
76 case "getTime": return N(this.date);
77 case "getYear": return N(getYear(date));
78 case "getFullYear": return N(YearFromTime(LocalTime(date)));
79 case "getUTCFullYear": return N(YearFromTime(date));
80 case "getMonth": return N(MonthFromTime(LocalTime(date)));
81 case "getUTCMonth": return N(MonthFromTime(date));
82 case "getDate": return N(DateFromTime(LocalTime(date)));
83 case "getUTCDate": return N(DateFromTime(date));
84 case "getDay": return N(WeekDay(LocalTime(date)));
85 case "getUTCDay": return N(WeekDay(date));
86 case "getHours": return N(HourFromTime(LocalTime(date)));
87 case "getUTCHours": return N(HourFromTime(date));
88 case "getMinutes": return N(MinFromTime(LocalTime(date)));
89 case "getUTCMinutes": return N(MinFromTime(date));
90 case "getSeconds": return N(SecFromTime(LocalTime(date)));
91 case "getUTCSeconds": return N(SecFromTime(date));
92 case "getMilliseconds": return N(msFromTime(LocalTime(date)));
93 case "getUTCMilliseconds": return N(msFromTime(date));
94 case "getTimezoneOffset": return N(getTimezoneOffset(date));
96 return super.callMethod(method, a0, a1, a2, rest, nargs);
100 case "setTime": return N(this.setTime(toDouble(a0)));
101 case "setYear": return N(this.setYear(toDouble(a0)));
106 Object[] args = new Object[nargs];
107 for(int i=0; i<nargs; i++) args[i] = i==0 ? a0 : i==1 ? a1 : i==2 ? a2 : rest[i-3];
109 case "setMilliseconds": return N(this.makeTime(args, 1, true));
110 case "setUTCMilliseconds": return N(this.makeTime(args, 1, false));
111 case "setSeconds": return N(this.makeTime(args, 2, true));
112 case "setUTCSeconds": return N(this.makeTime(args, 2, false));
113 case "setMinutes": return N(this.makeTime(args, 3, true));
114 case "setUTCMinutes": return N(this.makeTime(args, 3, false));
115 case "setHours": return N(this.makeTime(args, 4, true));
116 case "setUTCHours": return N(this.makeTime(args, 4, false));
117 case "setDate": return N(this.makeDate(args, 1, true));
118 case "setUTCDate": return N(this.makeDate(args, 1, false));
119 case "setMonth": return N(this.makeDate(args, 2, true));
120 case "setUTCMonth": return N(this.makeDate(args, 2, false));
121 case "setFullYear": return N(this.makeDate(args, 3, true));
122 case "setUTCFullYear": return N(this.makeDate(args, 3, false));
126 return super.callMethod(method, a0, a1, a2, rest, nargs);
129 public Object get(Object key) {
131 case "toString": return METHOD;
132 case "toTimeString": return METHOD;
133 case "toDateString": return METHOD;
134 case "toLocaleString": return METHOD;
135 case "toLocaleTimeString": return METHOD;
136 case "toLocaleDateString": return METHOD;
137 case "toUTCString": return METHOD;
138 case "valueOf": return METHOD;
139 case "getTime": return METHOD;
140 case "getYear": return METHOD;
141 case "getFullYear": return METHOD;
142 case "getUTCFullYear": return METHOD;
143 case "getMonth": return METHOD;
144 case "getUTCMonth": return METHOD;
145 case "getDate": return METHOD;
146 case "getUTCDate": return METHOD;
147 case "getDay": return METHOD;
148 case "getUTCDay": return METHOD;
149 case "getHours": return METHOD;
150 case "getUTCHours": return METHOD;
151 case "getMinutes": return METHOD;
152 case "getUTCMinutes": return METHOD;
153 case "getSeconds": return METHOD;
154 case "getUTCSeconds": return METHOD;
155 case "getMilliseconds": return METHOD;
156 case "getUTCMilliseconds": return METHOD;
157 case "getTimezoneOffset": return METHOD;
158 case "setTime": return METHOD;
159 case "setYear": return METHOD;
160 case "setMilliseconds": return METHOD;
161 case "setUTCMilliseconds": return METHOD;
162 case "setSeconds": return METHOD;
163 case "setUTCSeconds": return METHOD;
164 case "setMinutes": return METHOD;
165 case "setUTCMinutes": return METHOD;
166 case "setHours": return METHOD;
167 case "setUTCHours": return METHOD;
168 case "setDate": return METHOD;
169 case "setUTCDate": return METHOD;
170 case "setMonth": return METHOD;
171 case "setUTCMonth": return METHOD;
172 case "setFullYear": return METHOD;
173 case "setUTCFullYear": return METHOD;
175 return super.get(key);
178 /* ECMA helper functions */
180 private static final double HalfTimeDomain = 8.64e15;
181 private static final double HoursPerDay = 24.0;
182 private static final double MinutesPerHour = 60.0;
183 private static final double SecondsPerMinute = 60.0;
184 private static final double msPerSecond = 1000.0;
185 private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour);
186 private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute);
187 private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute);
188 private static final double msPerDay = (SecondsPerDay * msPerSecond);
189 private static final double msPerHour = (SecondsPerHour * msPerSecond);
190 private static final double msPerMinute = (SecondsPerMinute * msPerSecond);
192 private static double Day(double t) {
193 return java.lang.Math.floor(t / msPerDay);
196 private static double TimeWithinDay(double t) {
198 result = t % msPerDay;
204 private static int DaysInYear(int y) {
205 if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
212 /* math here has to be f.p, because we need
213 * floor((1968 - 1969) / 4) == -1
215 private static double DayFromYear(double y) {
216 return ((365 * ((y)-1970) + java.lang.Math.floor(((y)-1969)/4.0)
217 - java.lang.Math.floor(((y)-1901)/100.0) + java.lang.Math.floor(((y)-1601)/400.0)));
220 private static double TimeFromYear(double y) {
221 return DayFromYear(y) * msPerDay;
224 private static int YearFromTime(double t) {
225 int lo = (int) java.lang.Math.floor((t / msPerDay) / 366) + 1970;
226 int hi = (int) java.lang.Math.floor((t / msPerDay) / 365) + 1970;
229 /* above doesn't work for negative dates... */
236 /* Use a simple binary search algorithm to find the right
237 year. This seems like brute force... but the computation
238 of hi and lo years above lands within one year of the
239 correct answer for years within a thousand years of
240 1970; the loop below only requires six iterations
244 if (TimeFromYear(mid) > t) {
247 if (TimeFromYear(mid) <= t) {
249 if (TimeFromYear(temp) > t) {
259 private static boolean InLeapYear(double t) {
260 return DaysInYear(YearFromTime(t)) == 366;
263 private static int DayWithinYear(double t) {
264 int year = YearFromTime(t);
265 return (int) (Day(t) - DayFromYear(year));
268 * The following array contains the day of year for the first day of
269 * each month, where index 0 is January, and day 0 is January 1.
272 private static double DayFromMonth(int m, boolean leap) {
275 if (m >= 7) { day += m / 2 - 1; }
276 else if (m >= 2) { day += (m - 1) / 2 - 1; }
279 if (leap && m >= 2) { ++day; }
284 private static int MonthFromTime(double t) {
287 d = DayWithinYear(t);
292 // Originally coded as step += (InLeapYear(t) ? 29 : 28);
293 // but some jits always returned 28!
301 if (d < (step += 31))
303 if (d < (step += 30))
305 if (d < (step += 31))
307 if (d < (step += 30))
309 if (d < (step += 31))
311 if (d < (step += 31))
313 if (d < (step += 30))
315 if (d < (step += 31))
317 if (d < (step += 30))
322 private static int DateFromTime(double t) {
325 d = DayWithinYear(t);
326 if (d <= (next = 30))
330 // Originally coded as next += (InLeapYear(t) ? 29 : 28);
331 // but some jits always returned 28!
340 if (d <= (next += 31))
343 if (d <= (next += 30))
346 if (d <= (next += 31))
349 if (d <= (next += 30))
352 if (d <= (next += 31))
355 if (d <= (next += 31))
358 if (d <= (next += 30))
361 if (d <= (next += 31))
364 if (d <= (next += 30))
371 private static int WeekDay(double t) {
380 private static double Now() {
381 return (double) System.currentTimeMillis();
384 /* Should be possible to determine the need for this dynamically
385 * if we go with the workaround... I'm not using it now, because I
386 * can't think of any clean way to make toLocaleString() and the
387 * time zone (comment) in toString match the generated string
388 * values. Currently it's wrong-but-consistent in all but the
389 * most recent betas of the JRE - seems to work in 1.1.7.
391 private final static boolean TZO_WORKAROUND = false;
392 private static double DaylightSavingTA(double t) {
393 if (!TZO_WORKAROUND) {
394 java.util.Date date = new java.util.Date((long) t);
395 if (thisTimeZone.inDaylightTime(date))
400 /* Use getOffset if inDaylightTime() is broken, because it
401 * seems to work acceptably. We don't switch over to it
402 * entirely, because it requires (expensive) exploded date arguments,
403 * and the api makes it impossible to handle dst
404 * changeovers cleanly.
407 // Hardcode the assumption that the changeover always
408 // happens at 2:00 AM:
409 t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);
411 int year = YearFromTime(t);
412 double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,
417 (int)TimeWithinDay(t));
419 if ((offset - LocalTZA) != 0)
423 // return offset - LocalTZA;
427 private static double LocalTime(double t) {
428 return t + LocalTZA + DaylightSavingTA(t);
431 public static double internalUTC(double t) {
432 return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
435 private static int HourFromTime(double t) {
437 result = java.lang.Math.floor(t / msPerHour) % HoursPerDay;
439 result += HoursPerDay;
443 private static int MinFromTime(double t) {
445 result = java.lang.Math.floor(t / msPerMinute) % MinutesPerHour;
447 result += MinutesPerHour;
451 private static int SecFromTime(double t) {
453 result = java.lang.Math.floor(t / msPerSecond) % SecondsPerMinute;
455 result += SecondsPerMinute;
459 private static int msFromTime(double t) {
461 result = t % msPerSecond;
463 result += msPerSecond;
467 private static double MakeTime(double hour, double min,
468 double sec, double ms)
470 return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
474 private static double MakeDay(double year, double month, double date) {
480 year += java.lang.Math.floor(month / 12);
486 leap = (DaysInYear((int) year) == 366);
488 yearday = java.lang.Math.floor(TimeFromYear(year) / msPerDay);
489 monthday = DayFromMonth((int) month, leap);
497 private static double MakeDate(double day, double time) {
498 return day * msPerDay + time;
501 private static double TimeClip(double d) {
503 d == Double.POSITIVE_INFINITY ||
504 d == Double.NEGATIVE_INFINITY ||
505 java.lang.Math.abs(d) > HalfTimeDomain)
510 return java.lang.Math.floor(d + 0.);
512 return java.lang.Math.ceil(d + 0.);
515 /* end of ECMA helper functions */
517 /* find UTC time from given date... no 1900 correction! */
518 public static double date_msecFromDate(double year, double mon,
519 double mday, double hour,
520 double min, double sec,
527 day = MakeDay(year, mon, mday);
528 time = MakeTime(hour, min, sec, msec);
529 result = MakeDate(day, time);
534 private static final int MAXARGS = 7;
535 private static double jsStaticJSFunction_UTC(Object[] args) {
536 double array[] = new double[MAXARGS];
540 for (loop = 0; loop < MAXARGS; loop++) {
541 if (loop < args.length) {
542 d = _toNumber(args[loop]);
543 if (d != d || Double.isInfinite(d)) {
546 array[loop] = toDouble(args[loop]);
552 /* adjust 2-digit years into the 20th century */
553 if (array[0] >= 0 && array[0] <= 99)
556 /* if we got a 0 for 'date' (which is out of range)
557 * pretend it's a 1. (So Date.UTC(1972, 5) works) */
561 d = date_msecFromDate(array[0], array[1], array[2],
562 array[3], array[4], array[5], array[6]);
569 * Use ported code from jsdate.c rather than the locale-specific
570 * date-parsing code from Java, to keep js and rhino consistent.
571 * Is this the right strategy?
574 /* for use by date_parse */
576 /* replace this with byte arrays? Cheaper? */
577 private static String wtb[] = {
579 "monday", "tuesday", "wednesday", "thursday", "friday",
580 "saturday", "sunday",
581 "january", "february", "march", "april", "may", "june",
582 "july", "august", "september", "october", "november", "december",
583 "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
584 "mst", "mdt", "pst", "pdt"
585 /* time zone table needs to be expanded */
588 private static int ttb[] = {
589 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
590 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
591 10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */
592 10000 + 5 * 60, 10000 + 4 * 60, /* EDT */
593 10000 + 6 * 60, 10000 + 5 * 60,
594 10000 + 7 * 60, 10000 + 6 * 60,
595 10000 + 8 * 60, 10000 + 7 * 60
598 /* helper for date_parse */
599 private static boolean date_regionMatches(String s1, int s1off,
600 String s2, int s2off,
603 boolean result = false;
604 /* return true if matches, otherwise, false */
605 int s1len = s1.length();
606 int s2len = s2.length();
608 while (count > 0 && s1off < s1len && s2off < s2len) {
609 if (Character.toLowerCase(s1.charAt(s1off)) !=
610 Character.toLowerCase(s2.charAt(s2off)))
623 private static double date_parseString(String s) {
636 double tzoffset = -1;
639 boolean seenplusminus = false;
641 if (s == null) // ??? Will s be null?
647 if (c <= ' ' || c == ',' || c == '-') {
650 if (c == '-' && '0' <= si && si <= '9') {
656 if (c == '(') { /* comments) */
669 if ('0' <= c && c <= '9') {
671 while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {
672 n = n * 10 + c - '0';
676 /* allow TZA before the year, so
677 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
680 /* uses of seenplusminus allow : in TZA, so Java
681 * no-timezone style of GMT+4:30 works
683 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
684 /* make ':' case below change tzoffset */
685 seenplusminus = true;
689 n = n * 60; /* EG. "GMT-3" */
691 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
692 if (prevc == '+') /* plus means east of GMT */
694 if (tzoffset != 0 && tzoffset != -1)
697 } else if (n >= 70 ||
698 (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
701 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
702 year = n < 100 ? n + 1900 : n;
705 } else if (c == ':') {
712 } else if (c == '/') {
719 } else if (i < limit && c != ',' && c > ' ' && c != '-') {
721 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
726 } else if (hour >= 0 && min < 0) {
728 } else if (min >= 0 && sec < 0) {
730 } else if (mday < 0) {
736 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
743 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
749 for (k = wtb.length; --k >= 0;)
750 if (date_regionMatches(wtb[k], 0, s, st, i-st)) {
755 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
756 * 12:30, instead of blindly adding 12 if PM.
758 if (hour > 12 || hour < 0) {
761 if (action == -1 && hour == 12) { // am
763 } else if (action == -2 && hour != 12) {// pm
767 } else if (action <= 13) { /* month! */
769 mon = /*byte*/ (action - 2);
774 tzoffset = action - 10000;
784 if (year < 0 || mon < 0 || mday < 0)
792 if (tzoffset == -1) { /* no time zone specified, have to use local */
794 time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
795 return internalUTC(time);
798 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
799 msec += tzoffset * msPerMinute;
803 private static double jsStaticJSFunction_parse(String s) {
804 return date_parseString(s);
807 private static final int FORMATSPEC_FULL = 0;
808 private static final int FORMATSPEC_DATE = 1;
809 private static final int FORMATSPEC_TIME = 2;
811 private static String date_format(double t, int format) {
815 StringBuffer result = new StringBuffer(60);
816 double local = LocalTime(t);
818 /* offset from GMT in minutes. The offset includes daylight savings,
820 int minutes = (int) java.lang.Math.floor((LocalTZA + DaylightSavingTA(t))
822 /* map 510 minutes to 0830 hours */
823 int offset = (minutes / 60) * 100 + minutes % 60;
825 String dateStr = Integer.toString(DateFromTime(local));
826 String hourStr = Integer.toString(HourFromTime(local));
827 String minStr = Integer.toString(MinFromTime(local));
828 String secStr = Integer.toString(SecFromTime(local));
829 String offsetStr = Integer.toString(offset > 0 ? offset : -offset);
830 int year = YearFromTime(local);
831 String yearStr = Integer.toString(year > 0 ? year : -year);
833 /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */
834 /* Tue Oct 31 2000 */
835 /* 09:41:40 GMT-0800 (PST) */
837 if (format != FORMATSPEC_TIME) {
838 result.append(days[WeekDay(local)]);
840 result.append(months[MonthFromTime(local)]);
841 if (dateStr.length() == 1)
845 result.append(dateStr);
849 if (format != FORMATSPEC_DATE) {
850 if (hourStr.length() == 1)
852 result.append(hourStr);
853 if (minStr.length() == 1)
857 result.append(minStr);
858 if (secStr.length() == 1)
862 result.append(secStr);
864 result.append(" GMT+");
866 result.append(" GMT-");
867 for (int i = offsetStr.length(); i < 4; i++)
869 result.append(offsetStr);
871 if (timeZoneFormatter == null)
872 timeZoneFormatter = new java.text.SimpleDateFormat("zzz");
874 if (timeZoneFormatter != null) {
876 java.util.Date date = new java.util.Date((long) t);
877 result.append(timeZoneFormatter.format(date));
880 if (format != FORMATSPEC_TIME)
884 if (format != FORMATSPEC_TIME) {
887 for (int i = yearStr.length(); i < 4; i++)
889 result.append(yearStr);
892 return result.toString();
895 private static double _toNumber(Object o) { return JS.toDouble(o); }
896 private static double _toNumber(Object[] o, int index) { return JS.toDouble(o[index]); }
897 private static double toDouble(double d) { return d; }
899 public JSDate(JSArray args_) {
900 Object[] args = new Object[args_.length()];
901 for(int i=0; i<args.length; i++) args[i] = args_.elementAt(i);
904 // if called as a constructor with no args,
905 // return a new Date with the current time.
906 if (args.length == 0) {
911 // if called with just one arg -
912 if (args.length == 1) {
914 if (args[0] instanceof JS)
915 args[0] = ((JS) args[0]).toString();
916 if (!(args[0] instanceof String)) {
917 // if it's not a string, use it as a millisecond date
918 date = _toNumber(args[0]);
920 // it's a string; parse it.
921 String str = (String) args[0];
922 date = date_parseString(str);
924 obj.date = TimeClip(date);
928 // multiple arguments; year, month, day etc.
929 double array[] = new double[MAXARGS];
933 for (loop = 0; loop < MAXARGS; loop++) {
934 if (loop < args.length) {
935 d = _toNumber(args[loop]);
937 if (d != d || Double.isInfinite(d)) {
938 obj.date = Double.NaN;
941 array[loop] = toDouble(args[loop]);
947 /* adjust 2-digit years into the 20th century */
948 if (array[0] >= 0 && array[0] <= 99)
951 /* if we got a 0 for 'date' (which is out of range)
952 * pretend it's a 1 */
956 double day = MakeDay(array[0], array[1], array[2]);
957 double time = MakeTime(array[3], array[4], array[5], array[6]);
958 time = MakeDate(day, time);
959 time = internalUTC(time);
960 obj.date = TimeClip(time);
965 /* constants for toString, toUTCString */
966 private static String NaN_date_str = "Invalid Date";
968 private static String[] days = {
969 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
972 private static String[] months = {
973 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
974 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
977 private static String toLocale_helper(double t,
978 java.text.DateFormat formatter)
983 java.util.Date tempdate = new java.util.Date((long) t);
984 return formatter.format(tempdate);
987 private static String toLocaleString(double date) {
988 if (localeDateTimeFormatter == null)
989 localeDateTimeFormatter =
990 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
992 return toLocale_helper(date, localeDateTimeFormatter);
995 private static String toLocaleTimeString(double date) {
996 if (localeTimeFormatter == null)
997 localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
999 return toLocale_helper(date, localeTimeFormatter);
1002 private static String toLocaleDateString(double date) {
1003 if (localeDateFormatter == null)
1004 localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
1006 return toLocale_helper(date, localeDateFormatter);
1009 private static String toUTCString(double date) {
1010 StringBuffer result = new StringBuffer(60);
1012 String dateStr = Integer.toString(DateFromTime(date));
1013 String hourStr = Integer.toString(HourFromTime(date));
1014 String minStr = Integer.toString(MinFromTime(date));
1015 String secStr = Integer.toString(SecFromTime(date));
1016 int year = YearFromTime(date);
1017 String yearStr = Integer.toString(year > 0 ? year : -year);
1019 result.append(days[WeekDay(date)]);
1020 result.append(", ");
1021 if (dateStr.length() == 1)
1023 result.append(dateStr);
1025 result.append(months[MonthFromTime(date)]);
1027 result.append(" -");
1031 for (i = yearStr.length(); i < 4; i++)
1033 result.append(yearStr);
1035 if (hourStr.length() == 1)
1036 result.append(" 0");
1039 result.append(hourStr);
1040 if (minStr.length() == 1)
1041 result.append(":0");
1044 result.append(minStr);
1045 if (secStr.length() == 1)
1046 result.append(":0");
1049 result.append(secStr);
1051 result.append(" GMT");
1052 return result.toString();
1055 private static double getYear(double date) {
1056 int result = YearFromTime(LocalTime(date));
1061 private static double getTimezoneOffset(double date) {
1062 return (date - LocalTime(date)) / msPerMinute;
1065 public double setTime(double time) {
1066 this.date = TimeClip(time);
1070 private double makeTime(Object[] args, int maxargs, boolean local) {
1072 double conv[] = new double[4];
1073 double hour, min, sec, msec;
1074 double lorutime; /* Local or UTC version of date */
1079 double date = this.date;
1081 /* just return NaN if the date is already NaN */
1085 /* Satisfy the ECMA rule that if a function is called with
1086 * fewer arguments than the specified formal arguments, the
1087 * remaining arguments are set to undefined. Seems like all
1088 * the Date.setWhatever functions in ECMA are only varargs
1089 * beyond the first argument; this should be set to undefined
1090 * if it's not given. This means that "d = new Date();
1091 * d.setMilliseconds()" returns NaN. Blech.
1093 if (args.length == 0)
1094 args = new Object[] { null };
1096 for (i = 0; i < args.length && i < maxargs; i++) {
1097 conv[i] = _toNumber(args[i]);
1099 // limit checks that happen in MakeTime in ECMA.
1100 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1101 this.date = Double.NaN;
1104 conv[i] = toDouble(conv[i]);
1108 lorutime = LocalTime(date);
1113 int stop = args.length;
1115 if (maxargs >= 4 && i < stop)
1118 hour = HourFromTime(lorutime);
1120 if (maxargs >= 3 && i < stop)
1123 min = MinFromTime(lorutime);
1125 if (maxargs >= 2 && i < stop)
1128 sec = SecFromTime(lorutime);
1130 if (maxargs >= 1 && i < stop)
1133 msec = msFromTime(lorutime);
1135 time = MakeTime(hour, min, sec, msec);
1136 result = MakeDate(Day(lorutime), time);
1139 result = internalUTC(result);
1140 date = TimeClip(result);
1146 private double setHours(Object[] args) {
1147 return makeTime(args, 4, true);
1150 private double setUTCHours(Object[] args) {
1151 return makeTime(args, 4, false);
1154 private double makeDate(Object[] args, int maxargs, boolean local) {
1156 double conv[] = new double[3];
1157 double year, month, day;
1158 double lorutime; /* local or UTC version of date */
1161 double date = this.date;
1163 /* See arg padding comment in makeTime.*/
1164 if (args.length == 0)
1165 args = new Object[] { null };
1167 for (i = 0; i < args.length && i < maxargs; i++) {
1168 conv[i] = _toNumber(args[i]);
1170 // limit checks that happen in MakeDate in ECMA.
1171 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1172 this.date = Double.NaN;
1175 conv[i] = toDouble(conv[i]);
1178 /* return NaN if date is NaN and we're not setting the year,
1179 * If we are, use 0 as the time. */
1181 if (args.length < 3) {
1188 lorutime = LocalTime(date);
1194 int stop = args.length;
1196 if (maxargs >= 3 && i < stop)
1199 year = YearFromTime(lorutime);
1201 if (maxargs >= 2 && i < stop)
1204 month = MonthFromTime(lorutime);
1206 if (maxargs >= 1 && i < stop)
1209 day = DateFromTime(lorutime);
1211 day = MakeDay(year, month, day); /* day within year */
1212 result = MakeDate(day, TimeWithinDay(lorutime));
1215 result = internalUTC(result);
1217 date = TimeClip(result);
1223 private double setYear(double year) {
1225 if (year != year || Double.isInfinite(year)) {
1226 this.date = Double.NaN;
1230 if (this.date != this.date) {
1233 this.date = LocalTime(this.date);
1236 if (year >= 0 && year <= 99)
1239 day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
1240 result = MakeDate(day, TimeWithinDay(this.date));
1241 result = internalUTC(result);
1243 this.date = TimeClip(result);
1248 // private static final int
1249 // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
1253 private static java.util.TimeZone thisTimeZone;
1254 private static double LocalTZA;
1255 private static java.text.DateFormat timeZoneFormatter;
1256 private static java.text.DateFormat localeDateTimeFormatter;
1257 private static java.text.DateFormat localeDateFormatter;
1258 private static java.text.DateFormat localeTimeFormatter;
1260 private double date;
1262 public long getRawTime() { return (long)this.date; }