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 {
50 if (thisTimeZone == null) {
51 // j.u.TimeZone is synchronized, so setting class statics from it
53 thisTimeZone = java.util.TimeZone.getDefault();
54 LocalTZA = thisTimeZone.getRawOffset();
58 public JSDate(long now) {
59 if (thisTimeZone == null) {
60 // j.u.TimeZone is synchronized, so setting class statics from it
62 thisTimeZone = java.util.TimeZone.getDefault();
63 LocalTZA = thisTimeZone.getRawOffset();
68 public String toString() { return date_format(date, FORMATSPEC_FULL); }
70 public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
74 case "toString": return date_format(date, FORMATSPEC_FULL);
75 case "toTimeString": return date_format(date, FORMATSPEC_TIME);
76 case "toDateString": return date_format(date, FORMATSPEC_DATE);
77 case "toLocaleString": return toLocaleString(date);
78 case "toLocaleTimeString": return toLocaleTimeString(date);
79 case "toLocaleDateString": return toLocaleDateString(date);
80 case "toUTCString": return toUTCString(date);
81 case "valueOf": return N(this.date);
82 case "getTime": return N(this.date);
83 case "getYear": return N(getYear(date));
84 case "getFullYear": return N(YearFromTime(LocalTime(date)));
85 case "getUTCFullYear": return N(YearFromTime(date));
86 case "getMonth": return N(MonthFromTime(LocalTime(date)));
87 case "getUTCMonth": return N(MonthFromTime(date));
88 case "getDate": return N(DateFromTime(LocalTime(date)));
89 case "getUTCDate": return N(DateFromTime(date));
90 case "getDay": return N(WeekDay(LocalTime(date)));
91 case "getUTCDay": return N(WeekDay(date));
92 case "getHours": return N(HourFromTime(LocalTime(date)));
93 case "getUTCHours": return N(HourFromTime(date));
94 case "getMinutes": return N(MinFromTime(LocalTime(date)));
95 case "getUTCMinutes": return N(MinFromTime(date));
96 case "getSeconds": return N(SecFromTime(LocalTime(date)));
97 case "getUTCSeconds": return N(SecFromTime(date));
98 case "getMilliseconds": return N(msFromTime(LocalTime(date)));
99 case "getUTCMilliseconds": return N(msFromTime(date));
100 case "getTimezoneOffset": return N(getTimezoneOffset(date));
102 return super.callMethod(method, a0, a1, a2, rest, nargs);
106 case "setTime": return N(this.setTime(toDouble(a0)));
107 case "setYear": return N(this.setYear(toDouble(a0)));
112 Object[] args = new Object[nargs];
113 for(int i=0; i<nargs; i++) args[i] = i==0 ? a0 : i==1 ? a1 : i==2 ? a2 : rest[i-3];
115 case "setMilliseconds": return N(this.makeTime(args, 1, true));
116 case "setUTCMilliseconds": return N(this.makeTime(args, 1, false));
117 case "setSeconds": return N(this.makeTime(args, 2, true));
118 case "setUTCSeconds": return N(this.makeTime(args, 2, false));
119 case "setMinutes": return N(this.makeTime(args, 3, true));
120 case "setUTCMinutes": return N(this.makeTime(args, 3, false));
121 case "setHours": return N(this.makeTime(args, 4, true));
122 case "setUTCHours": return N(this.makeTime(args, 4, false));
123 case "setDate": return N(this.makeDate(args, 1, true));
124 case "setUTCDate": return N(this.makeDate(args, 1, false));
125 case "setMonth": return N(this.makeDate(args, 2, true));
126 case "setUTCMonth": return N(this.makeDate(args, 2, false));
127 case "setFullYear": return N(this.makeDate(args, 3, true));
128 case "setUTCFullYear": return N(this.makeDate(args, 3, false));
132 return super.callMethod(method, a0, a1, a2, rest, nargs);
135 public Object get(Object key) throws JSExn {
137 case "toString": return METHOD;
138 case "toTimeString": return METHOD;
139 case "toDateString": return METHOD;
140 case "toLocaleString": return METHOD;
141 case "toLocaleTimeString": return METHOD;
142 case "toLocaleDateString": return METHOD;
143 case "toUTCString": return METHOD;
144 case "valueOf": return METHOD;
145 case "getTime": return METHOD;
146 case "getYear": return METHOD;
147 case "getFullYear": return METHOD;
148 case "getUTCFullYear": return METHOD;
149 case "getMonth": return METHOD;
150 case "getUTCMonth": return METHOD;
151 case "getDate": return METHOD;
152 case "getUTCDate": return METHOD;
153 case "getDay": return METHOD;
154 case "getUTCDay": return METHOD;
155 case "getHours": return METHOD;
156 case "getUTCHours": return METHOD;
157 case "getMinutes": return METHOD;
158 case "getUTCMinutes": return METHOD;
159 case "getSeconds": return METHOD;
160 case "getUTCSeconds": return METHOD;
161 case "getMilliseconds": return METHOD;
162 case "getUTCMilliseconds": return METHOD;
163 case "getTimezoneOffset": return METHOD;
164 case "setTime": return METHOD;
165 case "setYear": return METHOD;
166 case "setMilliseconds": return METHOD;
167 case "setUTCMilliseconds": return METHOD;
168 case "setSeconds": return METHOD;
169 case "setUTCSeconds": return METHOD;
170 case "setMinutes": return METHOD;
171 case "setUTCMinutes": return METHOD;
172 case "setHours": return METHOD;
173 case "setUTCHours": return METHOD;
174 case "setDate": return METHOD;
175 case "setUTCDate": return METHOD;
176 case "setMonth": return METHOD;
177 case "setUTCMonth": return METHOD;
178 case "setFullYear": return METHOD;
179 case "setUTCFullYear": return METHOD;
181 return super.get(key);
184 /* ECMA helper functions */
186 private static final double HalfTimeDomain = 8.64e15;
187 private static final double HoursPerDay = 24.0;
188 private static final double MinutesPerHour = 60.0;
189 private static final double SecondsPerMinute = 60.0;
190 private static final double msPerSecond = 1000.0;
191 private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour);
192 private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute);
193 private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute);
194 private static final double msPerDay = (SecondsPerDay * msPerSecond);
195 private static final double msPerHour = (SecondsPerHour * msPerSecond);
196 private static final double msPerMinute = (SecondsPerMinute * msPerSecond);
198 private static double Day(double t) {
199 return java.lang.Math.floor(t / msPerDay);
202 private static double TimeWithinDay(double t) {
204 result = t % msPerDay;
210 private static int DaysInYear(int y) {
211 if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
218 /* math here has to be f.p, because we need
219 * floor((1968 - 1969) / 4) == -1
221 private static double DayFromYear(double y) {
222 return ((365 * ((y)-1970) + java.lang.Math.floor(((y)-1969)/4.0)
223 - java.lang.Math.floor(((y)-1901)/100.0) + java.lang.Math.floor(((y)-1601)/400.0)));
226 private static double TimeFromYear(double y) {
227 return DayFromYear(y) * msPerDay;
230 private static int YearFromTime(double t) {
231 int lo = (int) java.lang.Math.floor((t / msPerDay) / 366) + 1970;
232 int hi = (int) java.lang.Math.floor((t / msPerDay) / 365) + 1970;
235 /* above doesn't work for negative dates... */
242 /* Use a simple binary search algorithm to find the right
243 year. This seems like brute force... but the computation
244 of hi and lo years above lands within one year of the
245 correct answer for years within a thousand years of
246 1970; the loop below only requires six iterations
250 if (TimeFromYear(mid) > t) {
253 if (TimeFromYear(mid) <= t) {
255 if (TimeFromYear(temp) > t) {
265 private static boolean InLeapYear(double t) {
266 return DaysInYear(YearFromTime(t)) == 366;
269 private static int DayWithinYear(double t) {
270 int year = YearFromTime(t);
271 return (int) (Day(t) - DayFromYear(year));
274 * The following array contains the day of year for the first day of
275 * each month, where index 0 is January, and day 0 is January 1.
278 private static double DayFromMonth(int m, boolean leap) {
281 if (m >= 7) { day += m / 2 - 1; }
282 else if (m >= 2) { day += (m - 1) / 2 - 1; }
285 if (leap && m >= 2) { ++day; }
290 private static int MonthFromTime(double t) {
293 d = DayWithinYear(t);
298 // Originally coded as step += (InLeapYear(t) ? 29 : 28);
299 // but some jits always returned 28!
307 if (d < (step += 31))
309 if (d < (step += 30))
311 if (d < (step += 31))
313 if (d < (step += 30))
315 if (d < (step += 31))
317 if (d < (step += 31))
319 if (d < (step += 30))
321 if (d < (step += 31))
323 if (d < (step += 30))
328 private static int DateFromTime(double t) {
331 d = DayWithinYear(t);
332 if (d <= (next = 30))
336 // Originally coded as next += (InLeapYear(t) ? 29 : 28);
337 // but some jits always returned 28!
346 if (d <= (next += 31))
349 if (d <= (next += 30))
352 if (d <= (next += 31))
355 if (d <= (next += 30))
358 if (d <= (next += 31))
361 if (d <= (next += 31))
364 if (d <= (next += 30))
367 if (d <= (next += 31))
370 if (d <= (next += 30))
377 private static int WeekDay(double t) {
386 private static double Now() {
387 return (double) System.currentTimeMillis();
390 /* Should be possible to determine the need for this dynamically
391 * if we go with the workaround... I'm not using it now, because I
392 * can't think of any clean way to make toLocaleString() and the
393 * time zone (comment) in toString match the generated string
394 * values. Currently it's wrong-but-consistent in all but the
395 * most recent betas of the JRE - seems to work in 1.1.7.
397 private final static boolean TZO_WORKAROUND = false;
398 private static double DaylightSavingTA(double t) {
399 if (!TZO_WORKAROUND) {
400 java.util.Date date = new java.util.Date((long) t);
401 if (thisTimeZone.inDaylightTime(date))
406 /* Use getOffset if inDaylightTime() is broken, because it
407 * seems to work acceptably. We don't switch over to it
408 * entirely, because it requires (expensive) exploded date arguments,
409 * and the api makes it impossible to handle dst
410 * changeovers cleanly.
413 // Hardcode the assumption that the changeover always
414 // happens at 2:00 AM:
415 t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);
417 int year = YearFromTime(t);
418 double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,
423 (int)TimeWithinDay(t));
425 if ((offset - LocalTZA) != 0)
429 // return offset - LocalTZA;
433 private static double LocalTime(double t) {
434 return t + LocalTZA + DaylightSavingTA(t);
437 public static double internalUTC(double t) {
438 return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
441 private static int HourFromTime(double t) {
443 result = java.lang.Math.floor(t / msPerHour) % HoursPerDay;
445 result += HoursPerDay;
449 private static int MinFromTime(double t) {
451 result = java.lang.Math.floor(t / msPerMinute) % MinutesPerHour;
453 result += MinutesPerHour;
457 private static int SecFromTime(double t) {
459 result = java.lang.Math.floor(t / msPerSecond) % SecondsPerMinute;
461 result += SecondsPerMinute;
465 private static int msFromTime(double t) {
467 result = t % msPerSecond;
469 result += msPerSecond;
473 private static double MakeTime(double hour, double min,
474 double sec, double ms)
476 return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
480 private static double MakeDay(double year, double month, double date) {
486 year += java.lang.Math.floor(month / 12);
492 leap = (DaysInYear((int) year) == 366);
494 yearday = java.lang.Math.floor(TimeFromYear(year) / msPerDay);
495 monthday = DayFromMonth((int) month, leap);
503 private static double MakeDate(double day, double time) {
504 return day * msPerDay + time;
507 private static double TimeClip(double d) {
509 d == Double.POSITIVE_INFINITY ||
510 d == Double.NEGATIVE_INFINITY ||
511 java.lang.Math.abs(d) > HalfTimeDomain)
516 return java.lang.Math.floor(d + 0.);
518 return java.lang.Math.ceil(d + 0.);
521 /* end of ECMA helper functions */
523 /* find UTC time from given date... no 1900 correction! */
524 public static double date_msecFromDate(double year, double mon,
525 double mday, double hour,
526 double min, double sec,
533 day = MakeDay(year, mon, mday);
534 time = MakeTime(hour, min, sec, msec);
535 result = MakeDate(day, time);
540 private static final int MAXARGS = 7;
541 private static double jsStaticJSFunction_UTC(Object[] args) {
542 double array[] = new double[MAXARGS];
546 for (loop = 0; loop < MAXARGS; loop++) {
547 if (loop < args.length) {
548 d = _toNumber(args[loop]);
549 if (d != d || Double.isInfinite(d)) {
552 array[loop] = toDouble(args[loop]);
558 /* adjust 2-digit years into the 20th century */
559 if (array[0] >= 0 && array[0] <= 99)
562 /* if we got a 0 for 'date' (which is out of range)
563 * pretend it's a 1. (So Date.UTC(1972, 5) works) */
567 d = date_msecFromDate(array[0], array[1], array[2],
568 array[3], array[4], array[5], array[6]);
575 * Use ported code from jsdate.c rather than the locale-specific
576 * date-parsing code from Java, to keep js and rhino consistent.
577 * Is this the right strategy?
580 /* for use by date_parse */
582 /* replace this with byte arrays? Cheaper? */
583 private static String wtb[] = {
585 "monday", "tuesday", "wednesday", "thursday", "friday",
586 "saturday", "sunday",
587 "january", "february", "march", "april", "may", "june",
588 "july", "august", "september", "october", "november", "december",
589 "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
590 "mst", "mdt", "pst", "pdt"
591 /* time zone table needs to be expanded */
594 private static int ttb[] = {
595 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
596 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
597 10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */
598 10000 + 5 * 60, 10000 + 4 * 60, /* EDT */
599 10000 + 6 * 60, 10000 + 5 * 60,
600 10000 + 7 * 60, 10000 + 6 * 60,
601 10000 + 8 * 60, 10000 + 7 * 60
604 /* helper for date_parse */
605 private static boolean date_regionMatches(String s1, int s1off,
606 String s2, int s2off,
609 boolean result = false;
610 /* return true if matches, otherwise, false */
611 int s1len = s1.length();
612 int s2len = s2.length();
614 while (count > 0 && s1off < s1len && s2off < s2len) {
615 if (Character.toLowerCase(s1.charAt(s1off)) !=
616 Character.toLowerCase(s2.charAt(s2off)))
629 private static double date_parseString(String s) {
642 double tzoffset = -1;
645 boolean seenplusminus = false;
647 if (s == null) // ??? Will s be null?
653 if (c <= ' ' || c == ',' || c == '-') {
656 if (c == '-' && '0' <= si && si <= '9') {
662 if (c == '(') { /* comments) */
675 if ('0' <= c && c <= '9') {
677 while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {
678 n = n * 10 + c - '0';
682 /* allow TZA before the year, so
683 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
686 /* uses of seenplusminus allow : in TZA, so Java
687 * no-timezone style of GMT+4:30 works
689 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
690 /* make ':' case below change tzoffset */
691 seenplusminus = true;
695 n = n * 60; /* EG. "GMT-3" */
697 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
698 if (prevc == '+') /* plus means east of GMT */
700 if (tzoffset != 0 && tzoffset != -1)
703 } else if (n >= 70 ||
704 (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
707 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
708 year = n < 100 ? n + 1900 : n;
711 } else if (c == ':') {
718 } else if (c == '/') {
725 } else if (i < limit && c != ',' && c > ' ' && c != '-') {
727 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
732 } else if (hour >= 0 && min < 0) {
734 } else if (min >= 0 && sec < 0) {
736 } else if (mday < 0) {
742 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
749 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
755 for (k = wtb.length; --k >= 0;)
756 if (date_regionMatches(wtb[k], 0, s, st, i-st)) {
761 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
762 * 12:30, instead of blindly adding 12 if PM.
764 if (hour > 12 || hour < 0) {
767 if (action == -1 && hour == 12) { // am
769 } else if (action == -2 && hour != 12) {// pm
773 } else if (action <= 13) { /* month! */
775 mon = /*byte*/ (action - 2);
780 tzoffset = action - 10000;
790 if (year < 0 || mon < 0 || mday < 0)
798 if (tzoffset == -1) { /* no time zone specified, have to use local */
800 time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
801 return internalUTC(time);
804 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
805 msec += tzoffset * msPerMinute;
809 private static double jsStaticJSFunction_parse(String s) {
810 return date_parseString(s);
813 private static final int FORMATSPEC_FULL = 0;
814 private static final int FORMATSPEC_DATE = 1;
815 private static final int FORMATSPEC_TIME = 2;
817 private static String date_format(double t, int format) {
821 StringBuffer result = new StringBuffer(60);
822 double local = LocalTime(t);
824 /* offset from GMT in minutes. The offset includes daylight savings,
826 int minutes = (int) java.lang.Math.floor((LocalTZA + DaylightSavingTA(t))
828 /* map 510 minutes to 0830 hours */
829 int offset = (minutes / 60) * 100 + minutes % 60;
831 String dateStr = Integer.toString(DateFromTime(local));
832 String hourStr = Integer.toString(HourFromTime(local));
833 String minStr = Integer.toString(MinFromTime(local));
834 String secStr = Integer.toString(SecFromTime(local));
835 String offsetStr = Integer.toString(offset > 0 ? offset : -offset);
836 int year = YearFromTime(local);
837 String yearStr = Integer.toString(year > 0 ? year : -year);
839 /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */
840 /* Tue Oct 31 2000 */
841 /* 09:41:40 GMT-0800 (PST) */
843 if (format != FORMATSPEC_TIME) {
844 result.append(days[WeekDay(local)]);
846 result.append(months[MonthFromTime(local)]);
847 if (dateStr.length() == 1)
851 result.append(dateStr);
855 if (format != FORMATSPEC_DATE) {
856 if (hourStr.length() == 1)
858 result.append(hourStr);
859 if (minStr.length() == 1)
863 result.append(minStr);
864 if (secStr.length() == 1)
868 result.append(secStr);
870 result.append(" GMT+");
872 result.append(" GMT-");
873 for (int i = offsetStr.length(); i < 4; i++)
875 result.append(offsetStr);
877 if (timeZoneFormatter == null)
878 timeZoneFormatter = new java.text.SimpleDateFormat("zzz");
880 if (timeZoneFormatter != null) {
882 java.util.Date date = new java.util.Date((long) t);
883 result.append(timeZoneFormatter.format(date));
886 if (format != FORMATSPEC_TIME)
890 if (format != FORMATSPEC_TIME) {
893 for (int i = yearStr.length(); i < 4; i++)
895 result.append(yearStr);
898 return result.toString();
901 private static double _toNumber(Object o) { return JS.toDouble(o); }
902 private static double _toNumber(Object[] o, int index) { return JS.toDouble(o[index]); }
903 private static double toDouble(double d) { return d; }
905 public JSDate(Object a0, Object a1, Object a2, Object[] rest, int nargs) {
915 if (a0 instanceof JS)
916 a0 = ((JS) a0).toString();
917 if (!(a0 instanceof String)) {
918 // if it's not a string, use it as a millisecond date
919 date = _toNumber(a0);
921 // it's a string; parse it.
922 String str = (String) a0;
923 date = date_parseString(str);
925 obj.date = TimeClip(date);
929 // multiple arguments; year, month, day etc.
930 double array[] = new double[MAXARGS];
931 array[0] = toDouble(a0);
932 array[1] = toDouble(a1);
933 if (nargs >= 2) array[2] = toDouble(a2);
934 for(int i=0; i<nargs; i++) {
935 double d = _toNumber(i==0?a0:i==1?a1:i==2?a2:rest[i-3]);
936 if (d != d || Double.isInfinite(d)) {
937 obj.date = Double.NaN;
943 /* adjust 2-digit years into the 20th century */
944 if (array[0] >= 0 && array[0] <= 99)
947 /* if we got a 0 for 'date' (which is out of range)
948 * pretend it's a 1 */
952 double day = MakeDay(array[0], array[1], array[2]);
953 double time = MakeTime(array[3], array[4], array[5], array[6]);
954 time = MakeDate(day, time);
955 time = internalUTC(time);
956 obj.date = TimeClip(time);
963 /* constants for toString, toUTCString */
964 private static String NaN_date_str = "Invalid Date";
966 private static String[] days = {
967 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
970 private static String[] months = {
971 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
972 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
975 private static String toLocale_helper(double t,
976 java.text.DateFormat formatter)
981 java.util.Date tempdate = new java.util.Date((long) t);
982 return formatter.format(tempdate);
985 private static String toLocaleString(double date) {
986 if (localeDateTimeFormatter == null)
987 localeDateTimeFormatter =
988 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
990 return toLocale_helper(date, localeDateTimeFormatter);
993 private static String toLocaleTimeString(double date) {
994 if (localeTimeFormatter == null)
995 localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
997 return toLocale_helper(date, localeTimeFormatter);
1000 private static String toLocaleDateString(double date) {
1001 if (localeDateFormatter == null)
1002 localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
1004 return toLocale_helper(date, localeDateFormatter);
1007 private static String toUTCString(double date) {
1008 StringBuffer result = new StringBuffer(60);
1010 String dateStr = Integer.toString(DateFromTime(date));
1011 String hourStr = Integer.toString(HourFromTime(date));
1012 String minStr = Integer.toString(MinFromTime(date));
1013 String secStr = Integer.toString(SecFromTime(date));
1014 int year = YearFromTime(date);
1015 String yearStr = Integer.toString(year > 0 ? year : -year);
1017 result.append(days[WeekDay(date)]);
1018 result.append(", ");
1019 if (dateStr.length() == 1)
1021 result.append(dateStr);
1023 result.append(months[MonthFromTime(date)]);
1025 result.append(" -");
1029 for (i = yearStr.length(); i < 4; i++)
1031 result.append(yearStr);
1033 if (hourStr.length() == 1)
1034 result.append(" 0");
1037 result.append(hourStr);
1038 if (minStr.length() == 1)
1039 result.append(":0");
1042 result.append(minStr);
1043 if (secStr.length() == 1)
1044 result.append(":0");
1047 result.append(secStr);
1049 result.append(" GMT");
1050 return result.toString();
1053 private static double getYear(double date) {
1054 int result = YearFromTime(LocalTime(date));
1059 private static double getTimezoneOffset(double date) {
1060 return (date - LocalTime(date)) / msPerMinute;
1063 public double setTime(double time) {
1064 this.date = TimeClip(time);
1068 private double makeTime(Object[] args, int maxargs, boolean local) {
1070 double conv[] = new double[4];
1071 double hour, min, sec, msec;
1072 double lorutime; /* Local or UTC version of date */
1077 double date = this.date;
1079 /* just return NaN if the date is already NaN */
1083 /* Satisfy the ECMA rule that if a function is called with
1084 * fewer arguments than the specified formal arguments, the
1085 * remaining arguments are set to undefined. Seems like all
1086 * the Date.setWhatever functions in ECMA are only varargs
1087 * beyond the first argument; this should be set to undefined
1088 * if it's not given. This means that "d = new Date();
1089 * d.setMilliseconds()" returns NaN. Blech.
1091 if (args.length == 0)
1092 args = new Object[] { null };
1094 for (i = 0; i < args.length && i < maxargs; i++) {
1095 conv[i] = _toNumber(args[i]);
1097 // limit checks that happen in MakeTime in ECMA.
1098 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1099 this.date = Double.NaN;
1102 conv[i] = toDouble(conv[i]);
1106 lorutime = LocalTime(date);
1111 int stop = args.length;
1113 if (maxargs >= 4 && i < stop)
1116 hour = HourFromTime(lorutime);
1118 if (maxargs >= 3 && i < stop)
1121 min = MinFromTime(lorutime);
1123 if (maxargs >= 2 && i < stop)
1126 sec = SecFromTime(lorutime);
1128 if (maxargs >= 1 && i < stop)
1131 msec = msFromTime(lorutime);
1133 time = MakeTime(hour, min, sec, msec);
1134 result = MakeDate(Day(lorutime), time);
1137 result = internalUTC(result);
1138 date = TimeClip(result);
1144 private double setHours(Object[] args) {
1145 return makeTime(args, 4, true);
1148 private double setUTCHours(Object[] args) {
1149 return makeTime(args, 4, false);
1152 private double makeDate(Object[] args, int maxargs, boolean local) {
1154 double conv[] = new double[3];
1155 double year, month, day;
1156 double lorutime; /* local or UTC version of date */
1159 double date = this.date;
1161 /* See arg padding comment in makeTime.*/
1162 if (args.length == 0)
1163 args = new Object[] { null };
1165 for (i = 0; i < args.length && i < maxargs; i++) {
1166 conv[i] = _toNumber(args[i]);
1168 // limit checks that happen in MakeDate in ECMA.
1169 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1170 this.date = Double.NaN;
1173 conv[i] = toDouble(conv[i]);
1176 /* return NaN if date is NaN and we're not setting the year,
1177 * If we are, use 0 as the time. */
1179 if (args.length < 3) {
1186 lorutime = LocalTime(date);
1192 int stop = args.length;
1194 if (maxargs >= 3 && i < stop)
1197 year = YearFromTime(lorutime);
1199 if (maxargs >= 2 && i < stop)
1202 month = MonthFromTime(lorutime);
1204 if (maxargs >= 1 && i < stop)
1207 day = DateFromTime(lorutime);
1209 day = MakeDay(year, month, day); /* day within year */
1210 result = MakeDate(day, TimeWithinDay(lorutime));
1213 result = internalUTC(result);
1215 date = TimeClip(result);
1221 private double setYear(double year) {
1223 if (year != year || Double.isInfinite(year)) {
1224 this.date = Double.NaN;
1228 if (this.date != this.date) {
1231 this.date = LocalTime(this.date);
1234 if (year >= 0 && year <= 99)
1237 day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
1238 result = MakeDate(day, TimeWithinDay(this.date));
1239 result = internalUTC(result);
1241 this.date = TimeClip(result);
1246 // private static final int
1247 // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
1251 private static java.util.TimeZone thisTimeZone;
1252 private static double LocalTZA;
1253 private static java.text.DateFormat timeZoneFormatter;
1254 private static java.text.DateFormat localeDateTimeFormatter;
1255 private static java.text.DateFormat localeDateFormatter;
1256 private static java.text.DateFormat localeTimeFormatter;
1258 private double date;
1260 public long getRawTime() { return (long)this.date; }