Index: java/util/Calendar.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/Calendar.java,v retrieving revision 1.32 diff -u -r1.32 Calendar.java --- java/util/Calendar.java 21 Jan 2005 16:29:01 -0000 1.32 +++ java/util/Calendar.java 21 Jan 2005 16:42:02 -0000 @@ -727,6 +727,7 @@ isSet[WEEK_OF_MONTH] = false; isSet[DAY_OF_WEEK] = false; isSet[DAY_OF_WEEK_IN_MONTH] = false; + isSet[ERA] = false; if (! explicitDSTOffset) isSet[DST_OFFSET] = false; // May have crossed a DST boundary. Index: java/util/GregorianCalendar.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/GregorianCalendar.java,v retrieving revision 1.32 diff -u -r1.32 GregorianCalendar.java --- java/util/GregorianCalendar.java 21 Jan 2005 16:29:01 -0000 1.32 +++ java/util/GregorianCalendar.java 21 Jan 2005 16:42:02 -0000 @@ -165,6 +165,23 @@ private static final String bundleName = "gnu.java.locale.Calendar"; /** + * Days in the epoch. Relative Jan 1, year '0' which is not a leap year. + * (although there is no year zero, this does not matter.) + * This is consistent with the formula: + * = (year-1)*365L + ((year-1) >> 2) + * + * Plus the gregorian correction: + * Math.floor((year-1) / 400.) - Math.floor((year-1) / 100.); + * For a correct julian date, the correction is -2 instead. + * + * The gregorian cutover in 1582 was 10 days, so by calculating the + * correction from year zero, we have 15 non-leap days (even centuries) + * minus 3 leap days (year 400,800,1200) = 12. Subtracting two corrects + * this to the correct number 10. + */ + private static final int EPOCH_DAYS = 719162; + + /** * Retrieves the resource bundle. The resources should be loaded * via this method only. Iff an application uses this method, the * resourcebundle is required. @@ -326,67 +343,19 @@ */ public boolean isLeapYear(int year) { + // Only years divisible by 4 can be leap years if ((year & 3) != 0) - // Only years divisible by 4 can be leap years return false; - // compute the linear day of the 29. February of that year. - // The 13 is the number of days, that were omitted in the Gregorian - // Calender until the epoch. - int julianDay = (((year - 1) * (365 * 4 + 1)) >> 2) - + (31 + 29 - (((1970 - 1) * (365 * 4 + 1)) / 4 + 1 - 13)); - - // If that day is smaller than the gregorianChange the julian - // rule applies: This is a leap year since it is divisible by 4. - if (julianDay * (24 * 60 * 60 * 1000L) < gregorianCutover) + // Is the leap-day a Julian date? Then it's a leap year + if (! isGregorian(year, 31 + 29 - 1)) return true; + // Apply gregorian rules otherwise return ((year % 100) != 0 || (year % 400) == 0); } /** - * Get the linear time in milliseconds since the epoch. If you - * specify a nonpositive year it is interpreted as BC as - * following: 0 is 1 BC, -1 is 2 BC and so on. The date is - * interpreted as gregorian if the change occurred before that date. - * - * @param year the year of the date. - * @param dayOfYear the day of year of the date; 1 based. - * @param millis the millisecond in that day. - * @return the days since the epoch, may be negative. - */ - private long getLinearTime(int year, int dayOfYear, int millis) - { - // The 13 is the number of days, that were omitted in the Gregorian - // Calendar until the epoch. - // We shift right by 2 instead of dividing by 4, to get correct - // results for negative years (and this is even more efficient). - int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear - - ((1970 * (365 * 4 + 1)) / 4 + 1 - 13); - long time = julianDay * (24 * 60 * 60 * 1000L) + millis; - - if (time >= gregorianCutover) - { - // subtract the days that are missing in gregorian calendar - // with respect to julian calendar. - // - // Okay, here we rely on the fact that the gregorian - // calendar was introduced in the AD era. This doesn't work - // with negative years. - // - // The additional leap year factor accounts for the fact that - // a leap day is not seen on Jan 1 of the leap year. - // And on and after the leap day, the leap day has already been - // included in dayOfYear. - int gregOffset = (year / 400) - (year / 100) + 2; - if (isLeapYear(year, true)) - --gregOffset; - time += gregOffset * (24 * 60 * 60 * 1000L); - } - return time; - } - - /** * Retrieves the day of the week corresponding to the specified * day of the specified year. * @@ -396,7 +365,8 @@ */ private int getWeekDay(int year, int dayOfYear) { - int day = (int) (getLinearTime(year, dayOfYear, 0) / (24 * 60 * 60 * 1000L)); + boolean greg = isGregorian(year, dayOfYear); + int day = (int) getLinearDay(year, dayOfYear, greg); // The epoch was a thursday. int weekday = (day + THURSDAY) % 7; @@ -506,6 +476,20 @@ } /** + * Takes a year, and a (zero based) day of year and determines + * if it is gregorian or not. + */ + private boolean isGregorian(int year, int dayOfYear) + { + int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear + - EPOCH_DAYS; // gregorian days from 1 to epoch. + int gregFactor = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover); + } + + /** * Converts the time field values (fields) to * milliseconds since the epoch UTC (time). * @@ -514,29 +498,23 @@ */ protected synchronized void computeTime() { + int millisInDay = 0; int era = isSet[ERA] ? fields[ERA] : AD; int year = isSet[YEAR] ? fields[YEAR] : 1970; - if (isLenient() && isSet[MONTH]) - { - int month = fields[MONTH]; - year += month / 12; - month %= 12; - if (month < 0) - { - month += 12; - year--; - } - fields[MONTH] = month; - isSet[YEAR] = true; - fields[YEAR] = year; - } + int month = isSet[MONTH] ? fields[MONTH] : 0; + int day = isSet[DAY_OF_MONTH] ? fields[DAY_OF_MONTH] : 1; + int minute = isSet[MINUTE] ? fields[MINUTE] : 0; + int second = isSet[SECOND] ? fields[SECOND] : 0; + int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0; + int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + int hour = 0; - if (era == BC) + if (era == BC && year > 0) year = 1 - year; - int[] daysOfYear = getDayOfYear(year); - - int hour = 0; + // should negative BC years be AD? + // get the hour (but no check for validity) if (isSet[HOUR_OF_DAY]) hour = fields[HOUR_OF_DAY]; else if (isSet[HOUR]) @@ -550,89 +528,112 @@ hour = 0; } - int minute = isSet[MINUTE] ? fields[MINUTE] : 0; - int second = isSet[SECOND] ? fields[SECOND] : 0; - int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0; - int millisInDay; - if (isLenient()) { - // prevent overflow + // Read the era,year,month,day fields and convert as appropriate. + // Calculate number of milliseconds into the day + // This takes care of both h, m, s, ms over/underflows. long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis; - daysOfYear[1] += allMillis / (24 * 60 * 60 * 1000L); + day += allMillis / (24 * 60 * 60 * 1000L); millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L)); + + if (isSet[MONTH]) + { + if (month < 0) + { + year += (int) month / 12; + month = month % 12; + if (month < 0) + { + month += 12; + year--; + } + } + if (month > 11) + { + year += (month / 12); + month = month % 12; + } + } + + if (isSet[DAY_OF_MONTH]) + { + month_days[1] = isLeapYear(year) ? 29 : 28; + + while (day <= 0) + { + if (month == 0) + { + year--; + month_days[1] = isLeapYear(year) ? 29 : 28; + } + month = (month + 11) % 12; + day += month_days[month]; + } + while (day > month_days[month]) + { + day -= (month_days[month]); + month = (month + 1) % 12; + if (month == 0) + { + year++; + month_days[1] = isLeapYear(year) ? 29 : 28; + } + } + } } else { - if (hour < 0 || hour >= 24 || minute < 0 || minute > 59 || second < 0 - || second > 59 || millis < 0 || millis >= 1000) + // non-lenient + if (month < 0 || month > 11 || hour < 0 || hour >= 24 || minute < 0 + || minute > 59 || second < 0 || second > 59 || millis < 0 + || millis >= 1000) + throw new IllegalArgumentException(); + if (day < 1 || day > month_days[month]) throw new IllegalArgumentException(); millisInDay = (((hour * 60) + minute) * 60 + second) * 1000 + millis; } - time = getLinearTime(year, daysOfYear[0], millisInDay); - // Add the relative days after calculating the linear time, to - // get right behaviour when jumping over the gregorianCutover. - time += daysOfYear[1] * (24 * 60 * 60 * 1000L); + // ok, by here we have valid day,month,year,era and millisinday + int dayOfYear = dayCount[month] + day - 1; // (day starts on 1) + if (isLeapYear(year) && month > 1) + dayOfYear++; + + int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear + - EPOCH_DAYS; // gregorian days from 1 to epoch. + int gregFactor = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover) + relativeDay += gregFactor; + else + relativeDay -= 2; + + time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay; TimeZone zone = getTimeZone(); int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET] : zone.getRawOffset(); - int day = (int) (time / (24 * 60 * 60 * 1000L)); - millisInDay = (int) (time % (24 * 60 * 60 * 1000L)); - if (millisInDay < 0) - { - millisInDay += (24 * 60 * 60 * 1000); - day--; - } + // the epoch was a Thursday. + int weekday = (int) (relativeDay + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + fields[DAY_OF_WEEK] = weekday; - int[] f = new int[FIELD_COUNT]; - calculateDay(f, day, time - rawOffset >= gregorianCutover); - year = f[YEAR]; - int month = f[MONTH]; - day = f[DAY_OF_MONTH]; - int weekday = f[DAY_OF_WEEK]; int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET] - : (zone.getOffset((year < 0) ? BC : AD, - (year < 0) ? 1 - year + : (zone.getOffset((year < 1) ? BC : AD, + (year < 1) ? 1 - year : year, month, day, weekday, millisInDay) - zone.getRawOffset()); - time -= rawOffset + dstOffset; + time -= (rawOffset + dstOffset); isTimeSet = true; } /** - *

- * Determines if the given year is a leap year. - *

- *

- * To specify a year in the BC era, use a negative value calculated - * as 1 - y, where y is the required year in BC. So, 1 BC is 0, - * 2 BC is -1, 3 BC is -2, etc. - *

- * - * @param year a year (use a negative value for BC). - * @param gregorian if true, use the gregorian leap year rule. - * @return true, if the given year is a leap year, false otherwise. - */ - private boolean isLeapYear(int year, boolean gregorian) - { - if ((year & 3) != 0) - // Only years divisible by 4 can be leap years - return false; - - if (! gregorian) - return true; - - // We rely on AD area here. - return ((year % 100) != 0 || (year % 400) == 0); - } - - /** * Get the linear day in days since the epoch, using the * Julian or Gregorian calendar as specified. If you specify a * nonpositive year it is interpreted as BC as following: 0 is 1 @@ -643,14 +644,14 @@ * @param gregorian true, if we should use the Gregorian rules. * @return the days since the epoch, may be negative. */ - private long getLinearDay(int year, int dayOfYear, boolean gregorian) + public long getLinearDay(int year, int dayOfYear, boolean gregorian) { // The 13 is the number of days, that were omitted in the Gregorian // Calender until the epoch. // We shift right by 2 instead of dividing by 4, to get correct // results for negative years (and this is even more efficient). - long julianDay = ((year * (365L * 4 + 1)) >> 2) + dayOfYear - - ((1970 * (365 * 4 + 1)) / 4 + 1 - 13); + long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1) + - EPOCH_DAYS; // gregorian days from 1 to epoch. if (gregorian) { @@ -663,11 +664,13 @@ // // The additional leap year factor accounts for the fact that // a leap day is not seen on Jan 1 of the leap year. - int gregOffset = (year / 400) - (year / 100) + 2; - if (isLeapYear(year, true) && dayOfYear < 31 + 29) - --gregOffset; - julianDay += gregOffset; + int gregOffset = (int) Math.floor((double) (year - 1) / 400.) + - (int) Math.floor((double) (year - 1) / 100.); + + return julianDay + gregOffset; } + else + julianDay -= 2; return julianDay; } @@ -681,7 +684,7 @@ */ private void calculateDay(int[] fields, long day, boolean gregorian) { - // the epoch is a Thursday. + // the epoch was a Thursday. int weekday = (int) (day + THURSDAY) % 7; if (weekday <= 0) weekday += 7; @@ -691,8 +694,8 @@ // year too big. int year = 1970 + (int) (gregorian - ? ((day - 100) * 400) / (365 * 400 + 100 - 4 + 1) - : ((day - 100) * 4) / (365 * 4 + 1)); + ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L + + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L)); if (day >= 0) year++; @@ -719,7 +722,7 @@ fields[YEAR] = year; } - int leapday = isLeapYear(year, gregorian) ? 1 : 0; + int leapday = isLeapYear(year) ? 1 : 0; if (day <= 31 + 28 + leapday) { fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY @@ -749,6 +752,7 @@ long day = localTime / (24 * 60 * 60 * 1000L); int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L)); + if (millisInDay < 0) { millisInDay += (24 * 60 * 60 * 1000); @@ -824,31 +828,6 @@ return (cal.getTimeInMillis() == getTimeInMillis()); } -// /** -// * Compares the given calender with this. -// * @param o the object to that we should compare. -// * @return true, if the given object is a calendar, and this calendar -// * represents a smaller time than the calender o. -// */ -// public boolean before(Object o) { -// if (!(o instanceof GregorianCalendar)) -// return false; -// GregorianCalendar cal = (GregorianCalendar) o; -// return (cal.getTimeInMillis() < getTimeInMillis()); -// } -// /** -// * Compares the given calender with this. -// * @param o the object to that we should compare. -// * @return true, if the given object is a calendar, and this calendar -// * represents a bigger time than the calender o. -// */ -// public boolean after(Object o) { -// if (!(o instanceof GregorianCalendar)) -// return false; -// GregorianCalendar cal = (GregorianCalendar) o; -// return (cal.getTimeInMillis() > getTimeInMillis()); -// } - /** * Adds the specified amount of time to the given time field. The * amount may be negative to subtract the time. If the field overflows Index: java/util/SimpleTimeZone.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/SimpleTimeZone.java,v retrieving revision 1.21 diff -u -r1.21 SimpleTimeZone.java --- java/util/SimpleTimeZone.java 21 Jan 2005 16:29:01 -0000 1.21 +++ java/util/SimpleTimeZone.java 21 Jan 2005 16:42:04 -0000 @@ -71,7 +71,7 @@ /** * The daylight savings offset. This is a positive offset in * milliseconds with respect to standard time. Typically this - * is one hour, but for some time zones this may be half an hour. + * is one hour, but for some time zones this may be half an our. * @serial * @since JDK1.1.4 */ @@ -424,8 +424,6 @@ */ private int checkRule(int month, int day, int dayOfWeek) { - if (month < 0 || month > 11) - throw new IllegalArgumentException("month out of range"); int daysInMonth = getDaysInMonth(month, 1); if (dayOfWeek == 0) { @@ -586,7 +584,7 @@ * * Note that this API isn't incredibly well specified. It appears that the * after flag must override the parameters, since normally, the day and - * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or + * dayofweek can select this. I.e., if day < 0 and dayOfWeek < 0, on or * before mode is chosen. But if after == true, this implementation * overrides the signs of the other arguments. And if dayOfWeek == 0, it * falls back to the behavior in the other APIs. I guess this should be @@ -680,7 +678,7 @@ if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) throw new IllegalArgumentException("dayOfWeek out of range"); if (month < Calendar.JANUARY || month > Calendar.DECEMBER) - throw new IllegalArgumentException("month out of range"); + throw new IllegalArgumentException("month out of range:" + month); // This method is called by Calendar, so we mustn't use that class. int daylightSavings = 0; @@ -691,9 +689,9 @@ boolean afterStart = ! isBefore(year, month, day, dayOfWeek, millis, startMode, startMonth, startDay, startDayOfWeek, startTime); - boolean beforeEnd = isBefore(year, month, day, dayOfWeek, - millis + dstSavings, endMode, endMonth, - endDay, endDayOfWeek, endTime); + boolean beforeEnd = isBefore(year, month, day, dayOfWeek, millis, + endMode, endMonth, endDay, endDayOfWeek, + endTime); if (startMonth < endMonth) // use daylight savings, if the date is after the start of @@ -765,20 +763,28 @@ } /** - * Returns the number of days in the given month. It does always - * use the Gregorian leap year rule. + * Returns the number of days in the given month. + * Uses gregorian rules prior to 1582 (The default and earliest cutover) * @param month The month, zero based; use one of the Calendar constants. * @param year The year. */ private int getDaysInMonth(int month, int year) { - // Most of this is copied from GregorianCalendar.getActualMaximum() if (month == Calendar.FEBRUARY) - return ((year & 3) == 0 && (year % 100 != 0 || year % 400 == 0)) ? 29 : 28; - else if (month < Calendar.AUGUST) - return 31 - (month & 1); + { + if ((year & 3) != 0) + return 28; + + // Assume default Gregorian cutover, + // all years prior to this must be Julian + if (year < 1582) + return 29; + + // Gregorian rules + return ((year % 100) != 0 || (year % 400) == 0) ? 29 : 28; + } else - return 30 + (month & 1); + return monthArr[month]; } /**