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) {
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 // FIXME: switch to new calling convention here
900 public JSDate(JSArray args_) {
901 Object[] args = new Object[args_.length()];
902 for(int i=0; i<args.length; i++) args[i] = args_.elementAt(i);
905 // if called as a constructor with no args,
906 // return a new Date with the current time.
907 if (args.length == 0) {
912 // if called with just one arg -
913 if (args.length == 1) {
915 if (args[0] instanceof JS)
916 args[0] = ((JS) args[0]).toString();
917 if (!(args[0] instanceof String)) {
918 // if it's not a string, use it as a millisecond date
919 date = _toNumber(args[0]);
921 // it's a string; parse it.
922 String str = (String) args[0];
923 date = date_parseString(str);
925 obj.date = TimeClip(date);
929 // multiple arguments; year, month, day etc.
930 double array[] = new double[MAXARGS];
934 for (loop = 0; loop < MAXARGS; loop++) {
935 if (loop < args.length) {
936 d = _toNumber(args[loop]);
938 if (d != d || Double.isInfinite(d)) {
939 obj.date = Double.NaN;
942 array[loop] = toDouble(args[loop]);
948 /* adjust 2-digit years into the 20th century */
949 if (array[0] >= 0 && array[0] <= 99)
952 /* if we got a 0 for 'date' (which is out of range)
953 * pretend it's a 1 */
957 double day = MakeDay(array[0], array[1], array[2]);
958 double time = MakeTime(array[3], array[4], array[5], array[6]);
959 time = MakeDate(day, time);
960 time = internalUTC(time);
961 obj.date = TimeClip(time);
966 /* constants for toString, toUTCString */
967 private static String NaN_date_str = "Invalid Date";
969 private static String[] days = {
970 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
973 private static String[] months = {
974 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
975 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
978 private static String toLocale_helper(double t,
979 java.text.DateFormat formatter)
984 java.util.Date tempdate = new java.util.Date((long) t);
985 return formatter.format(tempdate);
988 private static String toLocaleString(double date) {
989 if (localeDateTimeFormatter == null)
990 localeDateTimeFormatter =
991 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
993 return toLocale_helper(date, localeDateTimeFormatter);
996 private static String toLocaleTimeString(double date) {
997 if (localeTimeFormatter == null)
998 localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
1000 return toLocale_helper(date, localeTimeFormatter);
1003 private static String toLocaleDateString(double date) {
1004 if (localeDateFormatter == null)
1005 localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
1007 return toLocale_helper(date, localeDateFormatter);
1010 private static String toUTCString(double date) {
1011 StringBuffer result = new StringBuffer(60);
1013 String dateStr = Integer.toString(DateFromTime(date));
1014 String hourStr = Integer.toString(HourFromTime(date));
1015 String minStr = Integer.toString(MinFromTime(date));
1016 String secStr = Integer.toString(SecFromTime(date));
1017 int year = YearFromTime(date);
1018 String yearStr = Integer.toString(year > 0 ? year : -year);
1020 result.append(days[WeekDay(date)]);
1021 result.append(", ");
1022 if (dateStr.length() == 1)
1024 result.append(dateStr);
1026 result.append(months[MonthFromTime(date)]);
1028 result.append(" -");
1032 for (i = yearStr.length(); i < 4; i++)
1034 result.append(yearStr);
1036 if (hourStr.length() == 1)
1037 result.append(" 0");
1040 result.append(hourStr);
1041 if (minStr.length() == 1)
1042 result.append(":0");
1045 result.append(minStr);
1046 if (secStr.length() == 1)
1047 result.append(":0");
1050 result.append(secStr);
1052 result.append(" GMT");
1053 return result.toString();
1056 private static double getYear(double date) {
1057 int result = YearFromTime(LocalTime(date));
1062 private static double getTimezoneOffset(double date) {
1063 return (date - LocalTime(date)) / msPerMinute;
1066 public double setTime(double time) {
1067 this.date = TimeClip(time);
1071 private double makeTime(Object[] args, int maxargs, boolean local) {
1073 double conv[] = new double[4];
1074 double hour, min, sec, msec;
1075 double lorutime; /* Local or UTC version of date */
1080 double date = this.date;
1082 /* just return NaN if the date is already NaN */
1086 /* Satisfy the ECMA rule that if a function is called with
1087 * fewer arguments than the specified formal arguments, the
1088 * remaining arguments are set to undefined. Seems like all
1089 * the Date.setWhatever functions in ECMA are only varargs
1090 * beyond the first argument; this should be set to undefined
1091 * if it's not given. This means that "d = new Date();
1092 * d.setMilliseconds()" returns NaN. Blech.
1094 if (args.length == 0)
1095 args = new Object[] { null };
1097 for (i = 0; i < args.length && i < maxargs; i++) {
1098 conv[i] = _toNumber(args[i]);
1100 // limit checks that happen in MakeTime in ECMA.
1101 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1102 this.date = Double.NaN;
1105 conv[i] = toDouble(conv[i]);
1109 lorutime = LocalTime(date);
1114 int stop = args.length;
1116 if (maxargs >= 4 && i < stop)
1119 hour = HourFromTime(lorutime);
1121 if (maxargs >= 3 && i < stop)
1124 min = MinFromTime(lorutime);
1126 if (maxargs >= 2 && i < stop)
1129 sec = SecFromTime(lorutime);
1131 if (maxargs >= 1 && i < stop)
1134 msec = msFromTime(lorutime);
1136 time = MakeTime(hour, min, sec, msec);
1137 result = MakeDate(Day(lorutime), time);
1140 result = internalUTC(result);
1141 date = TimeClip(result);
1147 private double setHours(Object[] args) {
1148 return makeTime(args, 4, true);
1151 private double setUTCHours(Object[] args) {
1152 return makeTime(args, 4, false);
1155 private double makeDate(Object[] args, int maxargs, boolean local) {
1157 double conv[] = new double[3];
1158 double year, month, day;
1159 double lorutime; /* local or UTC version of date */
1162 double date = this.date;
1164 /* See arg padding comment in makeTime.*/
1165 if (args.length == 0)
1166 args = new Object[] { null };
1168 for (i = 0; i < args.length && i < maxargs; i++) {
1169 conv[i] = _toNumber(args[i]);
1171 // limit checks that happen in MakeDate in ECMA.
1172 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1173 this.date = Double.NaN;
1176 conv[i] = toDouble(conv[i]);
1179 /* return NaN if date is NaN and we're not setting the year,
1180 * If we are, use 0 as the time. */
1182 if (args.length < 3) {
1189 lorutime = LocalTime(date);
1195 int stop = args.length;
1197 if (maxargs >= 3 && i < stop)
1200 year = YearFromTime(lorutime);
1202 if (maxargs >= 2 && i < stop)
1205 month = MonthFromTime(lorutime);
1207 if (maxargs >= 1 && i < stop)
1210 day = DateFromTime(lorutime);
1212 day = MakeDay(year, month, day); /* day within year */
1213 result = MakeDate(day, TimeWithinDay(lorutime));
1216 result = internalUTC(result);
1218 date = TimeClip(result);
1224 private double setYear(double year) {
1226 if (year != year || Double.isInfinite(year)) {
1227 this.date = Double.NaN;
1231 if (this.date != this.date) {
1234 this.date = LocalTime(this.date);
1237 if (year >= 0 && year <= 99)
1240 day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
1241 result = MakeDate(day, TimeWithinDay(this.date));
1242 result = internalUTC(result);
1244 this.date = TimeClip(result);
1249 // private static final int
1250 // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
1254 private static java.util.TimeZone thisTimeZone;
1255 private static double LocalTZA;
1256 private static java.text.DateFormat timeZoneFormatter;
1257 private static java.text.DateFormat localeDateTimeFormatter;
1258 private static java.text.DateFormat localeDateFormatter;
1259 private static java.text.DateFormat localeTimeFormatter;
1261 private double date;
1263 public long getRawTime() { return (long)this.date; }