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