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(JSU.toString(method))
65 case "toString": return JSU.S(date_format(date, FORMATSPEC_FULL));
66 case "toTimeString": return JSU.S(date_format(date, FORMATSPEC_TIME));
67 case "toDateString": return JSU.S(date_format(date, FORMATSPEC_DATE));
68 case "toLocaleString": return JSU.S(toLocaleString(date));
69 case "toLocaleTimeString": return JSU.S(toLocaleTimeString(date));
70 case "toLocaleDateString": return JSU.S(toLocaleDateString(date));
71 case "toUTCString": return JSU.S(toUTCString(date));
72 case "valueOf": return JSU.N(this.date);
73 case "getTime": return JSU.N(this.date);
74 case "getYear": return JSU.N(getYear(date));
75 case "getFullYear": return JSU.N(YearFromTime(LocalTime(date)));
76 case "getUTCFullYear": return JSU.N(YearFromTime(date));
77 case "getMonth": return JSU.N(MonthFromTime(LocalTime(date)));
78 case "getUTCMonth": return JSU.N(MonthFromTime(date));
79 case "getDate": return JSU.N(DateFromTime(LocalTime(date)));
80 case "getUTCDate": return JSU.N(DateFromTime(date));
81 case "getDay": return JSU.N(WeekDay(LocalTime(date)));
82 case "getUTCDay": return JSU.N(WeekDay(date));
83 case "getHours": return JSU.N(HourFromTime(LocalTime(date)));
84 case "getUTCHours": return JSU.N(HourFromTime(date));
85 case "getMinutes": return JSU.N(MinFromTime(LocalTime(date)));
86 case "getUTCMinutes": return JSU.N(MinFromTime(date));
87 case "getSeconds": return JSU.N(SecFromTime(LocalTime(date)));
88 case "getUTCSeconds": return JSU.N(SecFromTime(date));
89 case "getMilliseconds": return JSU.N(msFromTime(LocalTime(date)));
90 case "getUTCMilliseconds": return JSU.N(msFromTime(date));
91 case "getTimezoneOffset": return JSU.N(getTimezoneOffset(date));
93 return super.call(method, args);
96 //#switch(JSU.toString(method))
97 case "setTime": return JSU.N(this.setTime(JSU.toDouble(args[0])));
98 case "setYear": return JSU.N(this.setYear(JSU.toDouble(args[0])));
103 //#switch(JSU.toString(method))
104 case "setMilliseconds": return JSU.N(this.makeTime(args, 1, true));
105 case "setUTCMilliseconds": return JSU.N(this.makeTime(args, 1, false));
106 case "setSeconds": return JSU.N(this.makeTime(args, 2, true));
107 case "setUTCSeconds": return JSU.N(this.makeTime(args, 2, false));
108 case "setMinutes": return JSU.N(this.makeTime(args, 3, true));
109 case "setUTCMinutes": return JSU.N(this.makeTime(args, 3, false));
110 case "setHours": return JSU.N(this.makeTime(args, 4, true));
111 case "setUTCHours": return JSU.N(this.makeTime(args, 4, false));
112 case "setDate": return JSU.N(this.makeDate(args, 1, true));
113 case "setUTCDate": return JSU.N(this.makeDate(args, 1, false));
114 case "setMonth": return JSU.N(this.makeDate(args, 2, true));
115 case "setUTCMonth": return JSU.N(this.makeDate(args, 2, false));
116 case "setFullYear": return JSU.N(this.makeDate(args, 3, true));
117 case "setUTCFullYear": return JSU.N(this.makeDate(args, 3, false));
121 return super.call(method, args);
124 public JS get(JS key) throws JSExn {
125 //#switch(JSU.toString(key))
126 case "toString": return METHOD;
127 case "toTimeString": return METHOD;
128 case "toDateString": return METHOD;
129 case "toLocaleString": return METHOD;
130 case "toLocaleTimeString": return METHOD;
131 case "toLocaleDateString": return METHOD;
132 case "toUTCString": return METHOD;
133 case "valueOf": return METHOD;
134 case "getTime": return METHOD;
135 case "getYear": return METHOD;
136 case "getFullYear": return METHOD;
137 case "getUTCFullYear": return METHOD;
138 case "getMonth": return METHOD;
139 case "getUTCMonth": return METHOD;
140 case "getDate": return METHOD;
141 case "getUTCDate": return METHOD;
142 case "getDay": return METHOD;
143 case "getUTCDay": return METHOD;
144 case "getHours": return METHOD;
145 case "getUTCHours": return METHOD;
146 case "getMinutes": return METHOD;
147 case "getUTCMinutes": return METHOD;
148 case "getSeconds": return METHOD;
149 case "getUTCSeconds": return METHOD;
150 case "getMilliseconds": return METHOD;
151 case "getUTCMilliseconds": return METHOD;
152 case "getTimezoneOffset": return METHOD;
153 case "setTime": return METHOD;
154 case "setYear": return METHOD;
155 case "setMilliseconds": return METHOD;
156 case "setUTCMilliseconds": return METHOD;
157 case "setSeconds": return METHOD;
158 case "setUTCSeconds": return METHOD;
159 case "setMinutes": return METHOD;
160 case "setUTCMinutes": return METHOD;
161 case "setHours": return METHOD;
162 case "setUTCHours": return METHOD;
163 case "setDate": return METHOD;
164 case "setUTCDate": return METHOD;
165 case "setMonth": return METHOD;
166 case "setUTCMonth": return METHOD;
167 case "setFullYear": return METHOD;
168 case "setUTCFullYear": return METHOD;
170 return super.get(key);
173 /* ECMA helper functions */
175 private static final double HalfTimeDomain = 8.64e15;
176 private static final double HoursPerDay = 24.0;
177 private static final double MinutesPerHour = 60.0;
178 private static final double SecondsPerMinute = 60.0;
179 private static final double msPerSecond = 1000.0;
180 private static final double MinutesPerDay = (HoursPerDay * MinutesPerHour);
181 private static final double SecondsPerDay = (MinutesPerDay * SecondsPerMinute);
182 private static final double SecondsPerHour = (MinutesPerHour * SecondsPerMinute);
183 private static final double msPerDay = (SecondsPerDay * msPerSecond);
184 private static final double msPerHour = (SecondsPerHour * msPerSecond);
185 private static final double msPerMinute = (SecondsPerMinute * msPerSecond);
187 private static double Day(double t) {
188 return java.lang.Math.floor(t / msPerDay);
191 private static double TimeWithinDay(double t) {
193 result = t % msPerDay;
199 private static int DaysInYear(int y) {
200 if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
207 /* math here has to be f.p, because we need
208 * floor((1968 - 1969) / 4) == -1
210 private static double DayFromYear(double y) {
211 return ((365 * ((y)-1970) + java.lang.Math.floor(((y)-1969)/4.0)
212 - java.lang.Math.floor(((y)-1901)/100.0) + java.lang.Math.floor(((y)-1601)/400.0)));
215 private static double TimeFromYear(double y) {
216 return DayFromYear(y) * msPerDay;
219 private static int YearFromTime(double t) {
220 int lo = (int) java.lang.Math.floor((t / msPerDay) / 366) + 1970;
221 int hi = (int) java.lang.Math.floor((t / msPerDay) / 365) + 1970;
224 /* above doesn't work for negative dates... */
231 /* Use a simple binary search algorithm to find the right
232 year. This seems like brute force... but the computation
233 of hi and lo years above lands within one year of the
234 correct answer for years within a thousand years of
235 1970; the loop below only requires six iterations
239 if (TimeFromYear(mid) > t) {
242 if (TimeFromYear(mid) <= t) {
244 if (TimeFromYear(temp) > t) {
254 private static boolean InLeapYear(double t) {
255 return DaysInYear(YearFromTime(t)) == 366;
258 private static int DayWithinYear(double t) {
259 int year = YearFromTime(t);
260 return (int) (Day(t) - DayFromYear(year));
263 * The following array contains the day of year for the first day of
264 * each month, where index 0 is January, and day 0 is January 1.
267 private static double DayFromMonth(int m, boolean leap) {
270 if (m >= 7) { day += m / 2 - 1; }
271 else if (m >= 2) { day += (m - 1) / 2 - 1; }
274 if (leap && m >= 2) { ++day; }
279 private static int MonthFromTime(double t) {
282 d = DayWithinYear(t);
287 // Originally coded as step += (InLeapYear(t) ? 29 : 28);
288 // but some jits always returned 28!
296 if (d < (step += 31))
298 if (d < (step += 30))
300 if (d < (step += 31))
302 if (d < (step += 30))
304 if (d < (step += 31))
306 if (d < (step += 31))
308 if (d < (step += 30))
310 if (d < (step += 31))
312 if (d < (step += 30))
317 private static int DateFromTime(double t) {
320 d = DayWithinYear(t);
321 if (d <= (next = 30))
325 // Originally coded as next += (InLeapYear(t) ? 29 : 28);
326 // but some jits always returned 28!
335 if (d <= (next += 31))
338 if (d <= (next += 30))
341 if (d <= (next += 31))
344 if (d <= (next += 30))
347 if (d <= (next += 31))
350 if (d <= (next += 31))
353 if (d <= (next += 30))
356 if (d <= (next += 31))
359 if (d <= (next += 30))
366 private static int WeekDay(double t) {
375 private static double Now() {
376 return (double) System.currentTimeMillis();
379 /* Should be possible to determine the need for this dynamically
380 * if we go with the workaround... I'm not using it now, because I
381 * can't think of any clean way to make toLocaleString() and the
382 * time zone (comment) in toString match the generated string
383 * values. Currently it's wrong-but-consistent in all but the
384 * most recent betas of the JRE - seems to work in 1.1.7.
386 private final static boolean TZO_WORKAROUND = false;
387 private static double DaylightSavingTA(double t) {
388 if (!TZO_WORKAROUND) {
389 java.util.Date date = new java.util.Date((long) t);
390 if (thisTimeZone.inDaylightTime(date))
395 /* Use getOffset if inDaylightTime() is broken, because it
396 * seems to work acceptably. We don't switch over to it
397 * entirely, because it requires (expensive) exploded date arguments,
398 * and the api makes it impossible to handle dst
399 * changeovers cleanly.
402 // Hardcode the assumption that the changeover always
403 // happens at 2:00 AM:
404 t += LocalTZA + (HourFromTime(t) <= 2 ? msPerHour : 0);
406 int year = YearFromTime(t);
407 double offset = thisTimeZone.getOffset(year > 0 ? 1 : 0,
412 (int)TimeWithinDay(t));
414 if ((offset - LocalTZA) != 0)
418 // return offset - LocalTZA;
422 private static double LocalTime(double t) {
423 return t + LocalTZA + DaylightSavingTA(t);
426 public static double internalUTC(double t) {
427 return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
430 private static int HourFromTime(double t) {
432 result = java.lang.Math.floor(t / msPerHour) % HoursPerDay;
434 result += HoursPerDay;
438 private static int MinFromTime(double t) {
440 result = java.lang.Math.floor(t / msPerMinute) % MinutesPerHour;
442 result += MinutesPerHour;
446 private static int SecFromTime(double t) {
448 result = java.lang.Math.floor(t / msPerSecond) % SecondsPerMinute;
450 result += SecondsPerMinute;
454 private static int msFromTime(double t) {
456 result = t % msPerSecond;
458 result += msPerSecond;
462 private static double MakeTime(double hour, double min,
463 double sec, double ms)
465 return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec)
469 private static double MakeDay(double year, double month, double date) {
475 year += java.lang.Math.floor(month / 12);
481 leap = (DaysInYear((int) year) == 366);
483 yearday = java.lang.Math.floor(TimeFromYear(year) / msPerDay);
484 monthday = DayFromMonth((int) month, leap);
492 private static double MakeDate(double day, double time) {
493 return day * msPerDay + time;
496 private static double TimeClip(double d) {
498 d == Double.POSITIVE_INFINITY ||
499 d == Double.NEGATIVE_INFINITY ||
500 java.lang.Math.abs(d) > HalfTimeDomain)
505 return java.lang.Math.floor(d + 0.);
507 return java.lang.Math.ceil(d + 0.);
510 /* end of ECMA helper functions */
512 /* find UTC time from given date... no 1900 correction! */
513 public static double date_msecFromDate(double year, double mon,
514 double mday, double hour,
515 double min, double sec,
522 day = MakeDay(year, mon, mday);
523 time = MakeTime(hour, min, sec, msec);
524 result = MakeDate(day, time);
529 private static final int MAXARGS = 7;
530 private static double jsStaticJSFunction_UTC(JS[] args) throws JSExn {
531 double array[] = new double[MAXARGS];
535 for (loop = 0; loop < MAXARGS; loop++) {
536 if (loop < args.length) {
537 d = _toNumber(args[loop]);
538 if (d != d || Double.isInfinite(d)) {
541 array[loop] = JSU.toDouble(args[loop]);
547 /* adjust 2-digit years into the 20th century */
548 if (array[0] >= 0 && array[0] <= 99)
551 /* if we got a 0 for 'date' (which is out of range)
552 * pretend it's a 1. (So Date.UTC(1972, 5) works) */
556 d = date_msecFromDate(array[0], array[1], array[2],
557 array[3], array[4], array[5], array[6]);
564 * Use ported code from jsdate.c rather than the locale-specific
565 * date-parsing code from Java, to keep js and rhino consistent.
566 * Is this the right strategy?
569 /* for use by date_parse */
571 /* replace this with byte arrays? Cheaper? */
572 private static String wtb[] = {
574 "monday", "tuesday", "wednesday", "thursday", "friday",
575 "saturday", "sunday",
576 "january", "february", "march", "april", "may", "june",
577 "july", "august", "september", "october", "november", "december",
578 "gmt", "ut", "utc", "est", "edt", "cst", "cdt",
579 "mst", "mdt", "pst", "pdt"
580 /* time zone table needs to be expanded */
583 private static int ttb[] = {
584 -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */
585 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
586 10000 + 0, 10000 + 0, 10000 + 0, /* UT/UTC */
587 10000 + 5 * 60, 10000 + 4 * 60, /* EDT */
588 10000 + 6 * 60, 10000 + 5 * 60,
589 10000 + 7 * 60, 10000 + 6 * 60,
590 10000 + 8 * 60, 10000 + 7 * 60
593 /* helper for date_parse */
594 private static boolean date_regionMatches(String s1, int s1off,
595 String s2, int s2off,
598 boolean result = false;
599 /* return true if matches, otherwise, false */
600 int s1len = s1.length();
601 int s2len = s2.length();
603 while (count > 0 && s1off < s1len && s2off < s2len) {
604 if (Character.toLowerCase(s1.charAt(s1off)) !=
605 Character.toLowerCase(s2.charAt(s2off)))
618 private static double date_parseString(String s) {
631 double tzoffset = -1;
634 boolean seenplusminus = false;
636 if (s == null) // ??? Will s be null?
642 if (c <= ' ' || c == ',' || c == '-') {
645 if (c == '-' && '0' <= si && si <= '9') {
651 if (c == '(') { /* comments) */
664 if ('0' <= c && c <= '9') {
666 while (i < limit && '0' <= (c = s.charAt(i)) && c <= '9') {
667 n = n * 10 + c - '0';
671 /* allow TZA before the year, so
672 * 'Wed Nov 05 21:49:11 GMT-0800 1997'
675 /* uses of seenplusminus allow : in TZA, so Java
676 * no-timezone style of GMT+4:30 works
678 if ((prevc == '+' || prevc == '-')/* && year>=0 */) {
679 /* make ':' case below change tzoffset */
680 seenplusminus = true;
684 n = n * 60; /* EG. "GMT-3" */
686 n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */
687 if (prevc == '+') /* plus means east of GMT */
689 if (tzoffset != 0 && tzoffset != -1)
692 } else if (n >= 70 ||
693 (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {
696 else if (c <= ' ' || c == ',' || c == '/' || i >= limit)
697 year = n < 100 ? n + 1900 : n;
700 } else if (c == ':') {
707 } else if (c == '/') {
714 } else if (i < limit && c != ',' && c > ' ' && c != '-') {
716 } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */
721 } else if (hour >= 0 && min < 0) {
723 } else if (min >= 0 && sec < 0) {
725 } else if (mday < 0) {
731 } else if (c == '/' || c == ':' || c == '+' || c == '-') {
738 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
744 for (k = wtb.length; --k >= 0;)
745 if (date_regionMatches(wtb[k], 0, s, st, i-st)) {
750 * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
751 * 12:30, instead of blindly adding 12 if PM.
753 if (hour > 12 || hour < 0) {
756 if (action == -1 && hour == 12) { // am
758 } else if (action == -2 && hour != 12) {// pm
762 } else if (action <= 13) { /* month! */
764 mon = /*byte*/ (action - 2);
769 tzoffset = action - 10000;
779 if (year < 0 || mon < 0 || mday < 0)
787 if (tzoffset == -1) { /* no time zone specified, have to use local */
789 time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
790 return internalUTC(time);
793 msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);
794 msec += tzoffset * msPerMinute;
798 private static double jsStaticJSFunction_parse(String s) {
799 return date_parseString(s);
802 private static final int FORMATSPEC_FULL = 0;
803 private static final int FORMATSPEC_DATE = 1;
804 private static final int FORMATSPEC_TIME = 2;
806 private static String date_format(double t, int format) {
810 StringBuffer result = new StringBuffer(60);
811 double local = LocalTime(t);
813 /* offset from GMT in minutes. The offset includes daylight savings,
815 int minutes = (int) java.lang.Math.floor((LocalTZA + DaylightSavingTA(t))
817 /* map 510 minutes to 0830 hours */
818 int offset = (minutes / 60) * 100 + minutes % 60;
820 String dateStr = Integer.toString(DateFromTime(local));
821 String hourStr = Integer.toString(HourFromTime(local));
822 String minStr = Integer.toString(MinFromTime(local));
823 String secStr = Integer.toString(SecFromTime(local));
824 String offsetStr = Integer.toString(offset > 0 ? offset : -offset);
825 int year = YearFromTime(local);
826 String yearStr = Integer.toString(year > 0 ? year : -year);
828 /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */
829 /* Tue Oct 31 2000 */
830 /* 09:41:40 GMT-0800 (PST) */
832 if (format != FORMATSPEC_TIME) {
833 result.append(days[WeekDay(local)]);
835 result.append(months[MonthFromTime(local)]);
836 if (dateStr.length() == 1)
840 result.append(dateStr);
844 if (format != FORMATSPEC_DATE) {
845 if (hourStr.length() == 1)
847 result.append(hourStr);
848 if (minStr.length() == 1)
852 result.append(minStr);
853 if (secStr.length() == 1)
857 result.append(secStr);
859 result.append(" GMT+");
861 result.append(" GMT-");
862 for (int i = offsetStr.length(); i < 4; i++)
864 result.append(offsetStr);
866 if (timeZoneFormatter == null)
867 timeZoneFormatter = new java.text.SimpleDateFormat("zzz");
869 if (timeZoneFormatter != null) {
871 java.util.Date date = new java.util.Date((long) t);
872 result.append(timeZoneFormatter.format(date));
875 if (format != FORMATSPEC_TIME)
879 if (format != FORMATSPEC_TIME) {
882 for (int i = yearStr.length(); i < 4; i++)
884 result.append(yearStr);
887 return result.toString();
890 private static double _toNumber(JS o) throws JSExn { return JSU.toDouble(o); }
891 private static double _toNumber(JS[] o, int index) throws JSExn { return JSU.toDouble(o[index]); }
893 public JSDate(JS[] args) throws JSExn {
896 switch (args.length) {
903 if(JSU.isString(args[0]))
904 date = date_parseString(JSU.toString(args[0]));
906 date = _toNumber(args[0]);
907 obj.date = TimeClip(date);
911 // multiple arguments; year, month, day etc.
912 double array[] = new double[MAXARGS];
913 array[0] = JSU.toDouble(args[0]);
914 array[1] = JSU.toDouble(args[1]);
915 if (args.length >= 2) array[2] = JSU.toDouble(args[2]);
916 for (int i=0; i < args.length; i++) {
917 double d = _toNumber(args[i]);
918 if (d != d || Double.isInfinite(d)) {
919 obj.date = Double.NaN;
925 /* adjust 2-digit years into the 20th century */
926 if (array[0] >= 0 && array[0] <= 99)
929 /* if we got a 0 for 'date' (which is out of range)
930 * pretend it's a 1 */
934 double day = MakeDay(array[0], array[1], array[2]);
935 double time = MakeTime(array[3], array[4], array[5], array[6]);
936 time = MakeDate(day, time);
937 time = internalUTC(time);
938 obj.date = TimeClip(time);
945 /* constants for toString, toUTCString */
946 private static String NaN_date_str = "Invalid Date";
948 private static String[] days = {
949 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
952 private static String[] months = {
953 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
954 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
957 private static String toLocale_helper(double t,
958 java.text.DateFormat formatter)
963 java.util.Date tempdate = new java.util.Date((long) t);
964 return formatter.format(tempdate);
967 private static String toLocaleString(double date) {
968 if (localeDateTimeFormatter == null)
969 localeDateTimeFormatter =
970 DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
972 return toLocale_helper(date, localeDateTimeFormatter);
975 private static String toLocaleTimeString(double date) {
976 if (localeTimeFormatter == null)
977 localeTimeFormatter = DateFormat.getTimeInstance(DateFormat.LONG);
979 return toLocale_helper(date, localeTimeFormatter);
982 private static String toLocaleDateString(double date) {
983 if (localeDateFormatter == null)
984 localeDateFormatter = DateFormat.getDateInstance(DateFormat.LONG);
986 return toLocale_helper(date, localeDateFormatter);
989 private static String toUTCString(double date) {
990 StringBuffer result = new StringBuffer(60);
992 String dateStr = Integer.toString(DateFromTime(date));
993 String hourStr = Integer.toString(HourFromTime(date));
994 String minStr = Integer.toString(MinFromTime(date));
995 String secStr = Integer.toString(SecFromTime(date));
996 int year = YearFromTime(date);
997 String yearStr = Integer.toString(year > 0 ? year : -year);
999 result.append(days[WeekDay(date)]);
1000 result.append(", ");
1001 if (dateStr.length() == 1)
1003 result.append(dateStr);
1005 result.append(months[MonthFromTime(date)]);
1007 result.append(" -");
1011 for (i = yearStr.length(); i < 4; i++)
1013 result.append(yearStr);
1015 if (hourStr.length() == 1)
1016 result.append(" 0");
1019 result.append(hourStr);
1020 if (minStr.length() == 1)
1021 result.append(":0");
1024 result.append(minStr);
1025 if (secStr.length() == 1)
1026 result.append(":0");
1029 result.append(secStr);
1031 result.append(" GMT");
1032 return result.toString();
1035 private static double getYear(double date) {
1036 int result = YearFromTime(LocalTime(date));
1041 private static double getTimezoneOffset(double date) {
1042 return (date - LocalTime(date)) / msPerMinute;
1045 public double setTime(double time) {
1046 this.date = TimeClip(time);
1050 private double makeTime(JS[] args, int maxargs, boolean local) throws JSExn {
1052 double conv[] = new double[4];
1053 double hour, min, sec, msec;
1054 double lorutime; /* Local or UTC version of date */
1059 double date = this.date;
1061 /* just return NaN if the date is already NaN */
1065 /* Satisfy the ECMA rule that if a function is called with
1066 * fewer arguments than the specified formal arguments, the
1067 * remaining arguments are set to undefined. Seems like all
1068 * the Date.setWhatever functions in ECMA are only varargs
1069 * beyond the first argument; this should be set to undefined
1070 * if it's not given. This means that "d = new Date();
1071 * d.setMilliseconds()" returns NaN. Blech.
1073 if (args.length == 0)
1074 args = new JS[] { null };
1076 for (i = 0; i < args.length && i < maxargs; i++) {
1077 conv[i] = _toNumber(args[i]);
1079 // limit checks that happen in MakeTime in ECMA.
1080 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1081 this.date = Double.NaN;
1087 lorutime = LocalTime(date);
1092 int stop = args.length;
1094 if (maxargs >= 4 && i < stop)
1097 hour = HourFromTime(lorutime);
1099 if (maxargs >= 3 && i < stop)
1102 min = MinFromTime(lorutime);
1104 if (maxargs >= 2 && i < stop)
1107 sec = SecFromTime(lorutime);
1109 if (maxargs >= 1 && i < stop)
1112 msec = msFromTime(lorutime);
1114 time = MakeTime(hour, min, sec, msec);
1115 result = MakeDate(Day(lorutime), time);
1118 result = internalUTC(result);
1119 date = TimeClip(result);
1125 private double setHours(JS[] args) throws JSExn {
1126 return makeTime(args, 4, true);
1129 private double setUTCHours(JS[] args) throws JSExn {
1130 return makeTime(args, 4, false);
1133 private double makeDate(JS[] args, int maxargs, boolean local) throws JSExn {
1135 double conv[] = new double[3];
1136 double year, month, day;
1137 double lorutime; /* local or UTC version of date */
1140 double date = this.date;
1142 /* See arg padding comment in makeTime.*/
1143 if (args.length == 0)
1144 args = new JS[] { null };
1146 for (i = 0; i < args.length && i < maxargs; i++) {
1147 conv[i] = _toNumber(args[i]);
1149 // limit checks that happen in MakeDate in ECMA.
1150 if (conv[i] != conv[i] || Double.isInfinite(conv[i])) {
1151 this.date = Double.NaN;
1156 /* return NaN if date is NaN and we're not setting the year,
1157 * If we are, use 0 as the time. */
1159 if (args.length < 3) {
1166 lorutime = LocalTime(date);
1172 int stop = args.length;
1174 if (maxargs >= 3 && i < stop)
1177 year = YearFromTime(lorutime);
1179 if (maxargs >= 2 && i < stop)
1182 month = MonthFromTime(lorutime);
1184 if (maxargs >= 1 && i < stop)
1187 day = DateFromTime(lorutime);
1189 day = MakeDay(year, month, day); /* day within year */
1190 result = MakeDate(day, TimeWithinDay(lorutime));
1193 result = internalUTC(result);
1195 date = TimeClip(result);
1201 private double setYear(double year) {
1203 if (year != year || Double.isInfinite(year)) {
1204 this.date = Double.NaN;
1208 if (this.date != this.date) {
1211 this.date = LocalTime(this.date);
1214 if (year >= 0 && year <= 99)
1217 day = MakeDay(year, MonthFromTime(this.date), DateFromTime(this.date));
1218 result = MakeDate(day, TimeWithinDay(this.date));
1219 result = internalUTC(result);
1221 this.date = TimeClip(result);
1226 // private static final int
1227 // Id_toGMTString = Id_toUTCString; // Alias, see Ecma B.2.6
1231 private static java.util.TimeZone thisTimeZone;
1232 private static double LocalTZA;
1233 private static java.text.DateFormat timeZoneFormatter;
1234 private static java.text.DateFormat localeDateTimeFormatter;
1235 private static java.text.DateFormat localeDateFormatter;
1236 private static java.text.DateFormat localeTimeFormatter;
1238 private double date;
1240 public long getRawTime() { return (long)this.date; }