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 JS {
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) throws JSExn {
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) throws JSExn {
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(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
909 if (a0 instanceof JS)
910 a0 = ((JS) a0).toString();
911 if (!(a0 instanceof String)) {
912 // if it's not a string, use it as a millisecond date
913 date = _toNumber(a0);
915 // it's a string; parse it.
916 String str = (String) a0;
917 date = date_parseString(str);
919 obj.date = TimeClip(date);
923 // multiple arguments; year, month, day etc.
924 double array[] = new double[MAXARGS];
925 array[0] = toDouble(a0);
926 array[1] = toDouble(a1);
927 if (nargs >= 2) array[2] = toDouble(a2);
928 for(int i=0; i<nargs; i++) {
929 double d = _toNumber(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
930 if (d != d || Double.isInfinite(d)) {
931 obj.date = Double.NaN;
937 /* adjust 2-digit years into the 20th century */
938 if (array[0] >= 0 && array[0] <= 99)
941 /* if we got a 0 for 'date' (which is out of range)
942 * pretend it's a 1 */
946 double day = MakeDay(array[0], array[1], array[2]);
947 double time = MakeTime(array[3], array[4], array[5], array[6]);
948 time = MakeDate(day, time);
949 time = internalUTC(time);
950 obj.date = TimeClip(time);
957 /* constants for toString, toUTCString */
958 private static String NaN_date_str = "Invalid Date";
960 private static String[] days = {
961 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
964 private static String[] months = {
965 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
966 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
969 private static String toLocale_helper(double t,
970 java.text.DateFormat formatter)
975 java.util.Date tempdate = new java.util.Date((long) t);
976 return formatter.format(tempdate);
979 private static String toLocaleString(double date) {
980 if (localeDateTimeFormatter == null)
981 localeDateTimeFormatter =
982 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
984 return toLocale_helper(date, localeDateTimeFormatter);
987 private static String toLocaleTimeString(double date) {
988 if (localeTimeFormatter == null)
989 localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
991 return toLocale_helper(date, localeTimeFormatter);
994 private static String toLocaleDateString(double date) {
995 if (localeDateFormatter == null)
996 localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
998 return toLocale_helper(date, localeDateFormatter);
1001 private static String toUTCString(double date) {
1002 StringBuffer result = new StringBuffer(60);
1004 String dateStr = Integer.toString(DateFromTime(date));
1005 String hourStr = Integer.toString(HourFromTime(date));
1006 String minStr = Integer.toString(MinFromTime(date));
1007 String secStr = Integer.toString(SecFromTime(date));
1008 int year = YearFromTime(date);
1009 String yearStr = Integer.toString(year > 0 ? year : -year);
1011 result.append(days[WeekDay(date)]);
1012 result.append(", ");
1013 if (dateStr.length() == 1)
1015 result.append(dateStr);
1017 result.append(months[MonthFromTime(date)]);
1019 result.append(" -");
1023 for (i = yearStr.length(); i < 4; i++)
1025 result.append(yearStr);
1027 if (hourStr.length() == 1)
1028 result.append(" 0");
1031 result.append(hourStr);
1032 if (minStr.length() == 1)
1033 result.append(":0");
1036 result.append(minStr);
1037 if (secStr.length() == 1)
1038 result.append(":0");
1041 result.append(secStr);
1043 result.append(" GMT");
1044 return result.toString();
1047 private static double getYear(double date) {
1048 int result = YearFromTime(LocalTime(date));
1053 private static double getTimezoneOffset(double date) {
1054 return (date - LocalTime(date)) / msPerMinute;
1057 public double setTime(double time) {
1058 this.date = TimeClip(time);
1062 private double makeTime(Object[] args, int maxargs, boolean local) {
1064 double conv[] = new double[4];
1065 double hour, min, sec, msec;
1066 double lorutime; /* Local or UTC version of date */
1071 double date = this.date;
1073 /* just return NaN if the date is already NaN */
1077 /* Satisfy the ECMA rule that if a function is called with
1078 * fewer arguments than the specified formal arguments, the
1079 * remaining arguments are set to undefined. Seems like all
1080 * the Date.setWhatever functions in ECMA are only varargs
1081 * beyond the first argument; this should be set to undefined
1082 * if it's not given. This means that "d = new Date();
1083 * d.setMilliseconds()" returns NaN. Blech.
1085 if (args.length == 0)
1086 args = new Object[] { null };
1088 for (i = 0; i < args.length && i < maxargs; i++) {
1089 conv[i] = _toNumber(args[i]);
1091 // limit checks that happen in MakeTime in ECMA.
1092 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1093 this.date = Double.NaN;
1096 conv[i] = toDouble(conv[i]);
1100 lorutime = LocalTime(date);
1105 int stop = args.length;
1107 if (maxargs >= 4 && i < stop)
1110 hour = HourFromTime(lorutime);
1112 if (maxargs >= 3 && i < stop)
1115 min = MinFromTime(lorutime);
1117 if (maxargs >= 2 && i < stop)
1120 sec = SecFromTime(lorutime);
1122 if (maxargs >= 1 && i < stop)
1125 msec = msFromTime(lorutime);
1127 time = MakeTime(hour, min, sec, msec);
1128 result = MakeDate(Day(lorutime), time);
1131 result = internalUTC(result);
1132 date = TimeClip(result);
1138 private double setHours(Object[] args) {
1139 return makeTime(args, 4, true);
1142 private double setUTCHours(Object[] args) {
1143 return makeTime(args, 4, false);
1146 private double makeDate(Object[] args, int maxargs, boolean local) {
1148 double conv[] = new double[3];
1149 double year, month, day;
1150 double lorutime; /* local or UTC version of date */
1153 double date = this.date;
1155 /* See arg padding comment in makeTime.*/
1156 if (args.length == 0)
1157 args = new Object[] { null };
1159 for (i = 0; i < args.length && i < maxargs; i++) {
1160 conv[i] = _toNumber(args[i]);
1162 // limit checks that happen in MakeDate in ECMA.
1163 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1164 this.date = Double.NaN;
1167 conv[i] = toDouble(conv[i]);
1170 /* return NaN if date is NaN and we're not setting the year,
1171 * If we are, use 0 as the time. */
1173 if (args.length < 3) {
1180 lorutime = LocalTime(date);
1186 int stop = args.length;
1188 if (maxargs >= 3 && i < stop)
1191 year = YearFromTime(lorutime);
1193 if (maxargs >= 2 && i < stop)
1196 month = MonthFromTime(lorutime);
1198 if (maxargs >= 1 && i < stop)
1201 day = DateFromTime(lorutime);
1203 day = MakeDay(year, month, day); /* day within year */
1204 result = MakeDate(day, TimeWithinDay(lorutime));
1207 result = internalUTC(result);
1209 date = TimeClip(result);
1215 private double setYear(double year) {
1217 if (year != year || Double.isInfinite(year)) {
1218 this.date = Double.NaN;
1222 if (this.date != this.date) {
1225 this.date = LocalTime(this.date);
1228 if (year >= 0 && year <= 99)
1231 day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
1232 result = MakeDate(day, TimeWithinDay(this.date));
1233 result = internalUTC(result);
1235 this.date = TimeClip(result);
1240 // private static final int
1241 // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
1245 private static java.util.TimeZone thisTimeZone;
1246 private static double LocalTZA;
1247 private static java.text.DateFormat timeZoneFormatter;
1248 private static java.text.DateFormat localeDateTimeFormatter;
1249 private static java.text.DateFormat localeDateFormatter;
1250 private static java.text.DateFormat localeTimeFormatter;
1252 private double date;
1254 public long getRawTime() { return (long)this.date; }