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(long l) throws JSExn { this.date = (double)l; }
895 public JSDate(JS[] args) throws JSExn {
898 switch (args.length) {
905 if(JSU.isString(args[0]))
906 date = date_parseString(JSU.toString(args[0]));
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] = JSU.toDouble(args[0]);
916 array[1] = JSU.toDouble(args[1]);
917 if (args.length >= 2) array[2] = JSU.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; }