diff options
Diffstat (limited to 'libjava/java/util/GregorianCalendar.java')
-rw-r--r-- | libjava/java/util/GregorianCalendar.java | 811 |
1 files changed, 437 insertions, 374 deletions
diff --git a/libjava/java/util/GregorianCalendar.java b/libjava/java/util/GregorianCalendar.java index d99a9f2c612..710dd56f58b 100644 --- a/libjava/java/util/GregorianCalendar.java +++ b/libjava/java/util/GregorianCalendar.java @@ -39,6 +39,7 @@ exception statement from your version. */ package java.util; + /** * <p> * This class represents the Gregorian calendar, that is used in most @@ -46,7 +47,7 @@ package java.util; * for dates smaller than the date of the change to the Gregorian calendar. * The Gregorian calendar differs from the Julian calendar by a different * leap year rule (no leap year every 100 years, except if year is divisible - * by 400). + * by 400). * </p> * <p> * This change date is different from country to country, and can be changed with @@ -136,7 +137,7 @@ public class GregorianCalendar extends Calendar * Constant representing the era BC (Before Christ). */ public static final int BC = 0; - + /** * Constant representing the era AD (Anno Domini). */ @@ -164,43 +165,46 @@ public class GregorianCalendar extends Calendar private static final String bundleName = "gnu.java.locale.Calendar"; /** - * Retrieves the resource bundle. The resources should be loaded - * via this method only. Iff an application uses this method, the - * resourcebundle is required. + * 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. * - * @param locale the locale in use for this calendar. - * @return A resource bundle for the calendar for the specified locale. + * 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 ResourceBundle getBundle(Locale locale) - { - return ResourceBundle.getBundle(bundleName, locale, - ClassLoader.getSystemClassLoader()); - } + private static final int EPOCH_DAYS = 719162; /** * Constructs a new GregorianCalender representing the current - * time, using the default time zone and the default locale. + * time, using the default time zone and the default locale. */ public GregorianCalendar() { this(TimeZone.getDefault(), Locale.getDefault()); } - + /** * Constructs a new GregorianCalender representing the current - * time, using the specified time zone and the default locale. - * + * time, using the specified time zone and the default locale. + * * @param zone a time zone. */ public GregorianCalendar(TimeZone zone) { this(zone, Locale.getDefault()); } - + /** * Constructs a new GregorianCalender representing the current * time, using the default time zone and the specified locale. - * + * * @param locale a locale. */ public GregorianCalendar(Locale locale) @@ -212,15 +216,30 @@ public class GregorianCalendar extends Calendar * Constructs a new GregorianCalender representing the current * time with the given time zone and the given locale. * - * @param zone a time zone. - * @param locale a locale. + * @param zone a time zone. + * @param locale a locale. */ public GregorianCalendar(TimeZone zone, Locale locale) { + this(zone, locale, false); + setTimeInMillis(System.currentTimeMillis()); + complete(); + } + + /** + * Common constructor that all constructors should call. + * @param zone a time zone. + * @param locale a locale. + * @param unused unused parameter to make the signature differ from + * the public constructor (TimeZone, Locale). + */ + private GregorianCalendar(TimeZone zone, Locale locale, boolean unused) + { super(zone, locale); - ResourceBundle rb = getBundle(locale); + ResourceBundle rb = ResourceBundle.getBundle(bundleName, locale, + ClassLoader + .getSystemClassLoader()); gregorianCutover = ((Date) rb.getObject("gregorianCutOver")).getTime(); - setTimeInMillis(System.currentTimeMillis()); } /** @@ -232,7 +251,7 @@ public class GregorianCalendar extends Calendar */ public GregorianCalendar(int year, int month, int day) { - super(); + this(TimeZone.getDefault(), Locale.getDefault(), false); set(year, month, day); } @@ -248,7 +267,7 @@ public class GregorianCalendar extends Calendar */ public GregorianCalendar(int year, int month, int day, int hour, int minute) { - super(); + this(TimeZone.getDefault(), Locale.getDefault(), false); set(year, month, day, hour, minute); } @@ -264,10 +283,10 @@ public class GregorianCalendar extends Calendar * @param minute corresponds to the MINUTE time field. * @param second corresponds to the SECOND time field. */ - public GregorianCalendar(int year, int month, int day, - int hour, int minute, int second) + public GregorianCalendar(int year, int month, int day, int hour, int minute, + int second) { - super(); + this(TimeZone.getDefault(), Locale.getDefault(), false); set(year, month, day, hour, minute, second); } @@ -308,71 +327,23 @@ public class GregorianCalendar extends Calendar * </p> * * @param year a year (use a negative value for BC). - * @return true, if the given year is a leap year, false otherwise. + * @return true, if the given year is a leap year, false otherwise. */ 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. * @@ -382,8 +353,8 @@ public class GregorianCalendar extends Calendar */ 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; @@ -393,235 +364,360 @@ public class GregorianCalendar extends Calendar } /** - * <p> - * Calculate the dayOfYear from the fields array. - * The relativeDays is used, to account for weeks that begin before - * the Gregorian change and end after it. - * </p> - * <p> - * We return two values. The first is used to determine, if we - * should use the Gregorian calendar or the Julian calendar, in order - * to handle the change year. The second is a relative day after the given - * day. This is necessary for week calculation in the year in - * which the Gregorian change occurs. - * </p> - * - * @param year the year, negative for BC. - * @return an array of two integer values, the first containing a reference - * day in the current year, the second a relative count since this reference - * day. + * Returns the day of the week for the first day of a given month (0..11) */ - private int[] getDayOfYear(int year) + private int getFirstDayOfMonth(int year, int month) { - if (isSet[MONTH]) - { - int dayOfYear; - if (fields[MONTH] > FEBRUARY) - { + int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - // The months after February are regular: - // 9 is an offset found by try and error. - dayOfYear = (fields[MONTH] * (31 + 30 + 31 + 30 + 31) - 9) / 5; - if (isLeapYear(year)) - dayOfYear++; - } - else - dayOfYear = 31 * fields[MONTH]; + if (month > 11) + { + year += (month / 12); + month = month % 12; + } - if (isSet[DAY_OF_MONTH]) + if (month < 0) + { + year += (int) month / 12; + month = month % 12; + if (month < 0) { - return new int[] - { - dayOfYear + fields[DAY_OF_MONTH], 0}; + month += 12; + year--; } - if (isSet[WEEK_OF_MONTH] && isSet[DAY_OF_WEEK]) - { - // the weekday of the first day in that month is: - int weekday = getWeekDay(year, ++dayOfYear); + } - return new int[] - { - dayOfYear, - // the day of week in the first week - // (weeks starting on sunday) is: - fields[DAY_OF_WEEK] - weekday + - // Now jump to the right week and correct the possible - // error made by assuming sunday is the first week day. - 7 * (fields[WEEK_OF_MONTH] - + (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1) - + (weekday < getFirstDayOfWeek()? -1 : 0))}; - } - if (isSet[DAY_OF_WEEK] && isSet[DAY_OF_WEEK_IN_MONTH]) - { - // the weekday of the first day in that month is: - int weekday = getWeekDay(year, ++dayOfYear); - return new int[] { - dayOfYear, - fields[DAY_OF_WEEK] - weekday + - 7 * (fields[DAY_OF_WEEK_IN_MONTH] - + (fields[DAY_OF_WEEK] < weekday ? 0 : -1))}; - } + int dayOfYear = dayCount[month] + 1; + if (month > 1) + if (isLeapYear(year)) + dayOfYear++; + + boolean greg = isGregorian(year, dayOfYear); + int day = (int) getLinearDay(year, dayOfYear, greg); + + // The epoch was a thursday. + int weekday = (day + THURSDAY) % 7; + if (weekday <= 0) + weekday += 7; + return weekday; + } + + /** + * 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); + } + + /** + * Check set fields for validity, without leniency. + * + * @throws IllegalArgumentException if a field is invalid + */ + private void nonLeniencyCheck() throws IllegalArgumentException + { + int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int year = fields[YEAR]; + int month = fields[MONTH]; + int leap = isLeapYear(year) ? 1 : 0; + + if (isSet[ERA] && fields[ERA] != AD && fields[ERA] != BC) + throw new IllegalArgumentException("Illegal ERA."); + if (isSet[YEAR] && fields[YEAR] < 1) + throw new IllegalArgumentException("Illegal YEAR."); + if (isSet[MONTH] && (month < 0 || month > 11)) + throw new IllegalArgumentException("Illegal MONTH."); + if (isSet[WEEK_OF_YEAR]) + { + int daysInYear = 365 + leap; + daysInYear += (getFirstDayOfMonth(year, 0) - 1); // pad first week + int last = getFirstDayOfMonth(year, 11) + 4; + if (last > 7) + last -= 7; + daysInYear += 7 - last; + int weeks = daysInYear / 7; + if (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > weeks) + throw new IllegalArgumentException("Illegal WEEK_OF_YEAR."); } - // MONTH + something did not succeed. - if (isSet[DAY_OF_YEAR]) + if (isSet[WEEK_OF_MONTH]) { - return new int[] {0, fields[DAY_OF_YEAR]}; + int weeks = (month == 1 && leap == 0) ? 4 : 5; + if (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > weeks) + throw new IllegalArgumentException("Illegal WEEK_OF_MONTH."); } - - if (isSet[DAY_OF_WEEK] && isSet[WEEK_OF_YEAR]) + + if (isSet[DAY_OF_MONTH]) + if (fields[DAY_OF_MONTH] < 1 + || fields[DAY_OF_MONTH] > month_days[month] + + ((month == 1) ? leap : 0)) + throw new IllegalArgumentException("Illegal DAY_OF_MONTH."); + + if (isSet[DAY_OF_YEAR] + && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > 365 + leap)) + throw new IllegalArgumentException("Illegal DAY_OF_YEAR."); + + if (isSet[DAY_OF_WEEK] + && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7)) + throw new IllegalArgumentException("Illegal DAY_OF_WEEK."); + + if (isSet[DAY_OF_WEEK_IN_MONTH]) { - int dayOfYear = getMinimalDaysInFirstWeek(); - // the weekday of the day, that begins the first week - // in that year is: - int weekday = getWeekDay(year, dayOfYear); - - return new int[] { - dayOfYear, - // the day of week in the first week - // (weeks starting on sunday) is: - fields[DAY_OF_WEEK] - weekday - // Now jump to the right week and correct the possible - // error made by assuming sunday is the first week day. - + 7 * (fields[WEEK_OF_YEAR] - + (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1) - + (weekday < getFirstDayOfWeek()? -1 : 0))}; + int weeks = (month == 1 && leap == 0) ? 4 : 5; + if (fields[DAY_OF_WEEK_IN_MONTH] < -weeks + || fields[DAY_OF_WEEK_IN_MONTH] > weeks) + throw new IllegalArgumentException("Illegal DAY_OF_WEEK_IN_MONTH."); } - // As last resort return Jan, 1st. - return new int[] {1, 0}; + if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM) + throw new IllegalArgumentException("Illegal AM_PM."); + if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 12)) + throw new IllegalArgumentException("Illegal HOUR."); + if (isSet[HOUR_OF_DAY] + && (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23)) + throw new IllegalArgumentException("Illegal HOUR_OF_DAY."); + if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59)) + throw new IllegalArgumentException("Illegal MINUTE."); + if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59)) + throw new IllegalArgumentException("Illegal SECOND."); + if (isSet[MILLISECOND] + && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999)) + throw new IllegalArgumentException("Illegal MILLISECOND."); + if (isSet[ZONE_OFFSET] + && (fields[ZONE_OFFSET] < -12 * 60 * 60 * 1000L + || fields[ZONE_OFFSET] > 12 * 60 * 60 * 1000L)) + throw new IllegalArgumentException("Illegal ZONE_OFFSET."); + if (isSet[DST_OFFSET] + && (fields[DST_OFFSET] < -12 * 60 * 60 * 1000L + || fields[DST_OFFSET] > 12 * 60 * 60 * 1000L)) + throw new IllegalArgumentException("Illegal DST_OFFSET."); } /** * Converts the time field values (<code>fields</code>) to - * milliseconds since the epoch UTC (<code>time</code>). + * milliseconds since the epoch UTC (<code>time</code>). * * @throws IllegalArgumentException if any calendar fields * are invalid. */ protected synchronized void computeTime() { - int era = isSet[ERA] ? fields[ERA] : AD; - int year = isSet[YEAR] ? fields[YEAR] : 1970; - if (era == BC) - year = 1 - year; + int millisInDay = 0; + int era = fields[ERA]; + int year = fields[YEAR]; + int month = fields[MONTH]; + int day = fields[DAY_OF_MONTH]; + + int minute = fields[MINUTE]; + int second = fields[SECOND]; + int millis = fields[MILLISECOND]; + 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; - int[] daysOfYear = getDayOfYear(year); + if (! isLenient()) + nonLeniencyCheck(); - int hour = 0; - if (isSet[HOUR_OF_DAY]) - hour = fields[HOUR_OF_DAY]; - else if (isSet[HOUR]) + if (! isSet[MONTH] && (! isSet[DAY_OF_WEEK] || isSet[WEEK_OF_YEAR])) + { + // 5: YEAR + DAY_OF_WEEK + WEEK_OF_YEAR + if (isSet[WEEK_OF_YEAR]) + { + int first = getFirstDayOfMonth(year, 0); + int offs = 1; + int daysInFirstWeek = getFirstDayOfWeek() - first; + if (daysInFirstWeek <= 0) + daysInFirstWeek += 7; + + if (daysInFirstWeek < getMinimalDaysInFirstWeek()) + offs += daysInFirstWeek; + else + offs -= 7 - daysInFirstWeek; + month = 0; + day = offs + 7 * (fields[WEEK_OF_YEAR] - 1); + offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek(); + + if (offs < 0) + offs += 7; + day += offs; + } + else + { + // 4: YEAR + DAY_OF_YEAR + month = 0; + day = fields[DAY_OF_YEAR]; + } + } + else + { + if (isSet[DAY_OF_WEEK]) + { + int first = getFirstDayOfMonth(year, month); + + // 3: YEAR + MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK + if (isSet[DAY_OF_WEEK_IN_MONTH]) + { + int offs = fields[DAY_OF_WEEK] - first; + if (offs < 0) + offs += 7; + day = 1 + 7 * (fields[DAY_OF_WEEK_IN_MONTH] - 1); + day += offs; + } + else + { // 2: YEAR + MONTH + WEEK_OF_MONTH + DAY_OF_WEEK + int offs = 1; + int daysInFirstWeek = getFirstDayOfWeek() - first; + if (daysInFirstWeek <= 0) + daysInFirstWeek += 7; + + if (daysInFirstWeek < getMinimalDaysInFirstWeek()) + offs += daysInFirstWeek; + else + offs -= 7 - daysInFirstWeek; + + day = offs + 7 * (fields[WEEK_OF_MONTH] - 1); + offs = fields[DAY_OF_WEEK] - getFirstDayOfWeek(); + if (offs < 0) + offs += 7; + day += offs; + } + } + + // 1: YEAR + MONTH + DAY_OF_MONTH + } + if (era == BC && year > 0) + year = 1 - year; + + // rest of code assumes day/month/year set + // should negative BC years be AD? + // get the hour (but no check for validity) + if (isSet[HOUR]) { hour = fields[HOUR]; - if (isSet[AM_PM] && fields[AM_PM] == PM) + if (fields[AM_PM] == PM) if (hour != 12) /* not Noon */ - hour += 12; + hour += 12; /* Fix the problem of the status of 12:00 AM (midnight). */ - if (isSet[AM_PM] && fields[AM_PM] == AM && hour == 12) + if (fields[AM_PM] == AM && hour == 12) hour = 0; } + else + hour = fields[HOUR_OF_DAY]; - int minute = isSet[MINUTE] ? fields[MINUTE] : 0; - int second = isSet[SECOND] ? fields[SECOND] : 0; - int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0; - int millisInDay; + // 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; + day += allMillis / (24 * 60 * 60 * 1000L); + millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L)); - if (isLenient()) + if (month < 0) { - // prevent overflow - long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L - + millis; - daysOfYear[1] += allMillis / (24 * 60 * 60 * 1000L); - millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L)); + year += (int) month / 12; + month = month % 12; + if (month < 0) + { + month += 12; + year--; + } } - else + if (month > 11) { - if (hour < 0 || hour >= 24 || minute < 0 || minute > 59 - || second < 0 || second > 59 || millis < 0 || millis >= 1000) - throw new IllegalArgumentException(); - millisInDay = (((hour * 60) + minute) * 60 + second) * 1000 + millis; + year += (month / 12); + month = month % 12; } - 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); + 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; + } + } - TimeZone zone = getTimeZone(); - int rawOffset = isSet[ZONE_OFFSET] - ? fields[ZONE_OFFSET] : zone.getRawOffset(); - - int dayOfYear = daysOfYear[0] + daysOfYear[1]; - // This formula isn't right, so check for month as a quick fix. - // It doesn't compensate for leap years and puts day 30 in month 1 - // instead of month 0. - int month = isSet[MONTH] - ? fields[MONTH] : (dayOfYear * 5 + 3) / (31 + 30 + 31 + 30 + 31); - // This formula isn't right, so check for day as a quick fix. It - // doesn't compensate for leap years, either. - int day = isSet[DAY_OF_MONTH] ? fields[DAY_OF_MONTH] - : (6 + (dayOfYear * 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5; - int weekday = ((int) (time / (24 * 60 * 60 * 1000L)) + THURSDAY) % 7; + // 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; + + // the epoch was a Thursday. + int weekday = (int) (relativeDay + THURSDAY) % 7; if (weekday <= 0) weekday += 7; - int dstOffset = isSet[DST_OFFSET] - ? fields[DST_OFFSET] : (zone.getOffset((year < 0) ? BC : AD, - (year < 0) ? 1 - year : year, - month, day, weekday, millisInDay) - - zone.getRawOffset()); - time -= rawOffset + dstOffset; - isTimeSet = true; - } + fields[DAY_OF_WEEK] = weekday; - /** - * <p> - * Determines if the given year is a leap year. - * </p> - * <p> - * 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. - * </p> - * - * @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; + // Time zone corrections. + TimeZone zone = getTimeZone(); + int rawOffset = isSet[ZONE_OFFSET] ? fields[ZONE_OFFSET] + : zone.getRawOffset(); - if (!gregorian) - return true; + int dstOffset = isSet[DST_OFFSET] ? fields[DST_OFFSET] + : (zone.getOffset((year < 0) ? BC : AD, + (year < 0) ? 1 - year + : year, + month, day, weekday, + millisInDay) + - zone.getRawOffset()); - // We rely on AD area here. - return ((year % 100) != 0 || (year % 400) == 0); + time -= rawOffset + dstOffset; + + isTimeSet = true; } /** * 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 - * BC, -1 is 2 BC and so on. + * BC, -1 is 2 BC and so on. * * @param year the year of the date. * @param dayOfYear the day of year of the date; 1 based. * @param gregorian <code>true</code>, if we should use the Gregorian rules. * @return the days since the epoch, may be negative. */ - private int 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). - int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear - - ((1970 * (365 * 4 + 1)) / 4 + 1 - 13); - + 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 - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1) + - EPOCH_DAYS; // gregorian days from 1 to epoch. + if (gregorian) { // subtract the days that are missing in gregorian calendar @@ -633,11 +729,13 @@ public class GregorianCalendar extends Calendar // // 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; } @@ -646,26 +744,27 @@ public class GregorianCalendar extends Calendar * day_of_year, day_of_month, day_of_week, and writes the result * into the fields array. * - * @param day the linear day. + * @param day the linear day. * @param gregorian true, if we should use Gregorian rules. */ - private void calculateDay(int day, boolean gregorian) + private void calculateDay(int[] fields, long day, boolean gregorian) { - // the epoch is a Thursday. - int weekday = (day + THURSDAY) % 7; + // the epoch was a Thursday. + int weekday = (int) (day + THURSDAY) % 7; if (weekday <= 0) weekday += 7; fields[DAY_OF_WEEK] = weekday; // get a first approximation of the year. This may be one // year too big. - int year = 1970 + (gregorian - ? ((day - 100) * 400) / (365 * 400 + 100 - 4 + 1) - : ((day - 100) * 4) / (365 * 4 + 1)); + int year = 1970 + + (int) (gregorian + ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L + + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L)); if (day >= 0) year++; - int firstDayOfYear = getLinearDay(year, 1, gregorian); + long firstDayOfYear = getLinearDay(year, 1, gregorian); // Now look in which year day really lies. if (day < firstDayOfYear) @@ -674,9 +773,9 @@ public class GregorianCalendar extends Calendar firstDayOfYear = getLinearDay(year, 1, gregorian); } - day -= firstDayOfYear - 1; // day of year, one based. + day -= firstDayOfYear - 1; // day of year, one based. - fields[DAY_OF_YEAR] = day; + fields[DAY_OF_YEAR] = (int) day; if (year <= 0) { fields[ERA] = BC; @@ -688,16 +787,16 @@ public class GregorianCalendar extends Calendar fields[YEAR] = year; } - int leapday = isLeapYear(year, gregorian) ? 1 : 0; + int leapday = isLeapYear(year) ? 1 : 0; if (day <= 31 + 28 + leapday) { - fields[MONTH] = day / 32; // 31->JANUARY, 32->FEBRUARY - fields[DAY_OF_MONTH] = day - 31 * fields[MONTH]; + fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY + fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH]; } else { // A few more magic formulas - int scaledDay = (day - leapday) * 5 + 8; + int scaledDay = ((int) day - leapday) * 5 + 8; fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31); fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1; } @@ -716,25 +815,26 @@ public class GregorianCalendar extends Calendar fields[ZONE_OFFSET] = zone.getRawOffset(); long localTime = time + fields[ZONE_OFFSET]; - int day = (int) (localTime / (24 * 60 * 60 * 1000L)); + long day = localTime / (24 * 60 * 60 * 1000L); int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L)); + if (millisInDay < 0) { millisInDay += (24 * 60 * 60 * 1000); day--; } - calculateDay(day, gregorian); - fields[DST_OFFSET] = - zone.getOffset(fields[ERA], fields[YEAR], fields[MONTH], - fields[DAY_OF_MONTH], fields[DAY_OF_WEEK], - millisInDay) - fields[ZONE_OFFSET]; + calculateDay(fields, day, gregorian); + fields[DST_OFFSET] = zone.getOffset(fields[ERA], fields[YEAR], + fields[MONTH], fields[DAY_OF_MONTH], + fields[DAY_OF_WEEK], millisInDay) + - fields[ZONE_OFFSET]; millisInDay += fields[DST_OFFSET]; if (millisInDay >= 24 * 60 * 60 * 1000) { millisInDay -= 24 * 60 * 60 * 1000; - calculateDay(++day, gregorian); + calculateDay(fields, ++day, gregorian); } fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7; @@ -749,13 +849,12 @@ public class GregorianCalendar extends Calendar // Do the Correction: getMinimalDaysInFirstWeek() is always in the // first week. int minDays = getMinimalDaysInFirstWeek(); - int firstWeekday = - (7 + getWeekDay(fields[YEAR], minDays) - getFirstDayOfWeek()) % 7; + int firstWeekday = (7 + getWeekDay(fields[YEAR], minDays) + - getFirstDayOfWeek()) % 7; if (minDays - firstWeekday < 1) weekOfYear++; fields[WEEK_OF_YEAR] = weekOfYear; - int hourOfDay = millisInDay / (60 * 60 * 1000); fields[AM_PM] = (hourOfDay < 12) ? AM : PM; int hour = hourOfDay % 12; @@ -767,14 +866,7 @@ public class GregorianCalendar extends Calendar fields[SECOND] = millisInDay / (1000); fields[MILLISECOND] = millisInDay % 1000; - - areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] = - isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] = - isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] = - isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] = - isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = - isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true; - + areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] = isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] = isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] = isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] = isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true; } /** @@ -782,7 +874,7 @@ public class GregorianCalendar extends Calendar * equivalent to this if it is also a <code>GregorianCalendar</code> * with the same time since the epoch under the same conditions * (same change date and same time zone). - * + * * @param o the object to that we should compare. * @return true, if the given object is a calendar, that represents * the same time (but doesn't necessarily have the same fields). @@ -794,48 +886,20 @@ public class GregorianCalendar extends Calendar */ public boolean equals(Object o) { - if (!(o instanceof GregorianCalendar)) + 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 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 * it does what you expect: Jan, 25 + 10 Days is Feb, 4. * @param field one of the time field constants. * @param amount the amount of time to add. - * @exception IllegalArgumentException if <code>field</code> is + * @exception IllegalArgumentException if <code>field</code> is * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or invalid; or * if <code>amount</code> contains an out-of-range value and the calendar * is not in lenient mode. @@ -859,18 +923,18 @@ public class GregorianCalendar extends Calendar fields[MONTH] += 12; fields[YEAR]--; } - isTimeSet = false; int maxDay = getActualMaximum(DAY_OF_MONTH); if (fields[DAY_OF_MONTH] > maxDay) { fields[DAY_OF_MONTH] = maxDay; - isTimeSet = false; } + set(YEAR, fields[YEAR]); + set(MONTH, fields[MONTH]); break; case DAY_OF_MONTH: case DAY_OF_YEAR: case DAY_OF_WEEK: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (24 * 60 * 60 * 1000L); areFieldsSet = false; @@ -878,59 +942,57 @@ public class GregorianCalendar extends Calendar case WEEK_OF_YEAR: case WEEK_OF_MONTH: case DAY_OF_WEEK_IN_MONTH: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (7 * 24 * 60 * 60 * 1000L); areFieldsSet = false; break; case AM_PM: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (12 * 60 * 60 * 1000L); areFieldsSet = false; break; case HOUR: case HOUR_OF_DAY: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (60 * 60 * 1000L); areFieldsSet = false; break; case MINUTE: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (60 * 1000L); areFieldsSet = false; break; case SECOND: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount * (1000L); areFieldsSet = false; break; case MILLISECOND: - if (!isTimeSet) + if (! isTimeSet) computeTime(); time += amount; areFieldsSet = false; break; case ZONE_OFFSET: - case DST_OFFSET: - default: + case DST_OFFSET:default: throw new IllegalArgumentException("Invalid or unknown field"); } } - /** * Rolls the specified time field up or down. This means add one * to the specified field, but don't change the other fields. If - * the maximum for this field is reached, start over with the - * minimum value. + * the maximum for this field is reached, start over with the + * minimum value. * * <strong>Note:</strong> There may be situation, where the other - * fields must be changed, e.g rolling the month on May, 31. - * The date June, 31 is automatically converted to July, 1. + * fields must be changed, e.g rolling the month on May, 31. + * The date June, 31 is automatically converted to July, 1. * This requires lenient settings. * * @param field the time field. One of the time field constants. @@ -972,7 +1034,6 @@ public class GregorianCalendar extends Calendar isSet[DAY_OF_YEAR] = false; isSet[WEEK_OF_YEAR] = false; break; - case DAY_OF_MONTH: isSet[WEEK_OF_MONTH] = false; isSet[DAY_OF_WEEK] = false; @@ -981,7 +1042,6 @@ public class GregorianCalendar extends Calendar isSet[WEEK_OF_YEAR] = false; time += delta * (24 * 60 * 60 * 1000L); break; - case WEEK_OF_MONTH: isSet[DAY_OF_MONTH] = false; isSet[DAY_OF_WEEK_IN_MONTH] = false; @@ -1013,7 +1073,6 @@ public class GregorianCalendar extends Calendar isSet[DAY_OF_YEAR] = false; time += delta * (7 * 24 * 60 * 60 * 1000L); break; - case AM_PM: isSet[HOUR_OF_DAY] = false; time += delta * (12 * 60 * 60 * 1000L); @@ -1027,7 +1086,6 @@ public class GregorianCalendar extends Calendar isSet[AM_PM] = false; time += delta * (60 * 60 * 1000L); break; - case MINUTE: time += delta * (60 * 1000L); break; @@ -1047,7 +1105,7 @@ public class GregorianCalendar extends Calendar * with the minimum value and vice versa for negative amounts. * * <strong>Note:</strong> There may be situation, where the other - * fields must be changed, e.g rolling the month on May, 31. + * fields must be changed, e.g rolling the month on May, 31. * The date June, 31 is automatically corrected to June, 30. * * @param field the time field. One of the time field constants. @@ -1084,16 +1142,23 @@ public class GregorianCalendar extends Calendar /** * The minimum values for the calendar fields. */ - private static final int[] minimums = - { BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, - AM, 1, 0, 1, 1, 1, -(12*60*60*1000), 0 }; + private static final int[] minimums = + { + BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1, AM, + 1, 0, 0, 0, 0, -(12 * 60 * 60 * 1000), + 0 + }; /** * The maximum values for the calendar fields. */ - private static final int[] maximums = - { AD, 5000000, 11, 53, 5, 31, 366, SATURDAY, 5, - PM, 12, 23, 59, 59, 999, +(12*60*60*1000), (12*60*60*1000) }; + private static final int[] maximums = + { + AD, 5000000, 11, 53, 5, 31, 366, + SATURDAY, 5, PM, 12, 23, 59, 59, 999, + +(12 * 60 * 60 * 1000), + (12 * 60 * 60 * 1000) + }; /** * Gets the smallest value that is allowed for the specified field. @@ -1117,7 +1182,6 @@ public class GregorianCalendar extends Calendar return maximums[field]; } - /** * Gets the greatest minimum value that is allowed for the specified field. * This is the largest value returned by the <code>getActualMinimum(int)</code> @@ -1142,7 +1206,7 @@ public class GregorianCalendar extends Calendar * 28 days). * * @param field the time field. One of the time field constants. - * @return the least maximum value. + * @return the least maximum value. * @see #getActualMaximum(int) * @since 1.2 */ @@ -1182,7 +1246,7 @@ public class GregorianCalendar extends Calendar int min = getMinimalDaysInFirstWeek(); if (min == 0) return 1; - if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) complete(); int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; @@ -1203,45 +1267,46 @@ public class GregorianCalendar extends Calendar * 29, rather than 28. * * @param field the time field. One of the time field constants. - * @return the actual maximum value. + * @return the actual maximum value. */ public int getActualMaximum(int field) { switch (field) { case WEEK_OF_YEAR: - { - if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) + { + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) complete(); + // This is wrong for the year that contains the gregorian change. // I.e it gives the weeks in the julian year or in the gregorian // year in that case. int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; int lastDay = isLeapYear(year) ? 366 : 365; int weekday = getWeekDay(year, lastDay); - int week = (lastDay + 6 - - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; + int week = (lastDay + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; int minimalDays = getMinimalDaysInFirstWeek(); int firstWeekday = getWeekDay(year, minimalDays); - /* + /* * Is there a set of days at the beginning of the year, before the * first day of the week, equal to or greater than the minimum number * of days required in the first week? */ if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1) return week + 1; /* Add week 1: firstWeekday through to firstDayOfWeek */ - } - case DAY_OF_MONTH: - { - if (!areFieldsSet || !isSet[MONTH]) + } + case DAY_OF_MONTH: + { + if (! areFieldsSet || ! isSet[MONTH]) complete(); int month = fields[MONTH]; + // If you change this, you should also change // SimpleTimeZone.getDaysInMonth(); if (month == FEBRUARY) { - if (!isSet[YEAR] || !isSet[ERA]) + if (! isSet[YEAR] || ! isSet[ERA]) complete(); int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; return isLeapYear(year) ? 29 : 28; @@ -1250,33 +1315,31 @@ public class GregorianCalendar extends Calendar return 31 - (month & 1); else return 30 + (month & 1); - } + } case DAY_OF_YEAR: - { - if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR]) + { + if (! areFieldsSet || ! isSet[ERA] || ! isSet[YEAR]) complete(); int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR]; return isLeapYear(year) ? 366 : 365; - } + } case DAY_OF_WEEK_IN_MONTH: - { + { // This is wrong for the month that contains the gregorian change. int daysInMonth = getActualMaximum(DAY_OF_MONTH); + // That's black magic, I know return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7; - } + } case WEEK_OF_MONTH: - { + { int daysInMonth = getActualMaximum(DAY_OF_MONTH); int weekday = (daysInMonth - fields[DAY_OF_MONTH] - + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY; - return (daysInMonth + 6 - - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; - } + + fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY; + return (daysInMonth + 6 - (7 + weekday - getFirstDayOfWeek()) % 7) / 7; + } default: return maximums[field]; } } - - } |