0N/A/*
2362N/A * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.util.calendar;
0N/A
0N/Aimport java.util.Locale;
0N/Aimport java.util.TimeZone;
0N/A
0N/A/**
0N/A * The <code>BaseCalendar</code> provides basic calendar calculation
0N/A * functions to support the Julian, Gregorian, and Gregorian-based
0N/A * calendar systems.
0N/A *
0N/A * @author Masayoshi Okutsu
0N/A * @since 1.5
0N/A */
0N/A
0N/Apublic abstract class BaseCalendar extends AbstractCalendar {
0N/A
0N/A public static final int JANUARY = 1;
0N/A public static final int FEBRUARY = 2;
0N/A public static final int MARCH = 3;
0N/A public static final int APRIL = 4;
0N/A public static final int MAY = 5;
0N/A public static final int JUNE = 6;
0N/A public static final int JULY = 7;
0N/A public static final int AUGUST = 8;
0N/A public static final int SEPTEMBER = 9;
0N/A public static final int OCTOBER = 10;
0N/A public static final int NOVEMBER = 11;
0N/A public static final int DECEMBER = 12;
0N/A
0N/A // day of week constants
0N/A public static final int SUNDAY = 1;
0N/A public static final int MONDAY = 2;
0N/A public static final int TUESDAY = 3;
0N/A public static final int WEDNESDAY = 4;
0N/A public static final int THURSDAY = 5;
0N/A public static final int FRIDAY = 6;
0N/A public static final int SATURDAY = 7;
0N/A
0N/A // The base Gregorian year of FIXED_DATES[]
0N/A private static final int BASE_YEAR = 1970;
0N/A
0N/A // Pre-calculated fixed dates of January 1 from BASE_YEAR
0N/A // (Gregorian). This table covers all the years that can be
0N/A // supported by the POSIX time_t (32-bit) after the Epoch. Note
0N/A // that the data type is int[].
0N/A private static final int[] FIXED_DATES = {
0N/A 719163, // 1970
0N/A 719528, // 1971
0N/A 719893, // 1972
0N/A 720259, // 1973
0N/A 720624, // 1974
0N/A 720989, // 1975
0N/A 721354, // 1976
0N/A 721720, // 1977
0N/A 722085, // 1978
0N/A 722450, // 1979
0N/A 722815, // 1980
0N/A 723181, // 1981
0N/A 723546, // 1982
0N/A 723911, // 1983
0N/A 724276, // 1984
0N/A 724642, // 1985
0N/A 725007, // 1986
0N/A 725372, // 1987
0N/A 725737, // 1988
0N/A 726103, // 1989
0N/A 726468, // 1990
0N/A 726833, // 1991
0N/A 727198, // 1992
0N/A 727564, // 1993
0N/A 727929, // 1994
0N/A 728294, // 1995
0N/A 728659, // 1996
0N/A 729025, // 1997
0N/A 729390, // 1998
0N/A 729755, // 1999
0N/A 730120, // 2000
0N/A 730486, // 2001
0N/A 730851, // 2002
0N/A 731216, // 2003
0N/A 731581, // 2004
0N/A 731947, // 2005
0N/A 732312, // 2006
0N/A 732677, // 2007
0N/A 733042, // 2008
0N/A 733408, // 2009
0N/A 733773, // 2010
0N/A 734138, // 2011
0N/A 734503, // 2012
0N/A 734869, // 2013
0N/A 735234, // 2014
0N/A 735599, // 2015
0N/A 735964, // 2016
0N/A 736330, // 2017
0N/A 736695, // 2018
0N/A 737060, // 2019
0N/A 737425, // 2020
0N/A 737791, // 2021
0N/A 738156, // 2022
0N/A 738521, // 2023
0N/A 738886, // 2024
0N/A 739252, // 2025
0N/A 739617, // 2026
0N/A 739982, // 2027
0N/A 740347, // 2028
0N/A 740713, // 2029
0N/A 741078, // 2030
0N/A 741443, // 2031
0N/A 741808, // 2032
0N/A 742174, // 2033
0N/A 742539, // 2034
0N/A 742904, // 2035
0N/A 743269, // 2036
0N/A 743635, // 2037
0N/A 744000, // 2038
0N/A 744365, // 2039
0N/A };
0N/A
0N/A public abstract static class Date extends CalendarDate {
0N/A protected Date() {
0N/A super();
0N/A }
0N/A protected Date(TimeZone zone) {
0N/A super(zone);
0N/A }
0N/A
0N/A public Date setNormalizedDate(int normalizedYear, int month, int dayOfMonth) {
0N/A setNormalizedYear(normalizedYear);
0N/A setMonth(month).setDayOfMonth(dayOfMonth);
0N/A return this;
0N/A }
0N/A
0N/A public abstract int getNormalizedYear();
0N/A
0N/A public abstract void setNormalizedYear(int normalizedYear);
0N/A
0N/A // Cache for the fixed date of January 1 and year length of the
0N/A // cachedYear. A simple benchmark showed 7% performance
0N/A // improvement with >90% cache hit. The initial values are for Gregorian.
0N/A int cachedYear = 2004;
0N/A long cachedFixedDateJan1 = 731581L;
0N/A long cachedFixedDateNextJan1 = cachedFixedDateJan1 + 366;
0N/A
0N/A protected final boolean hit(int year) {
0N/A return year == cachedYear;
0N/A }
0N/A
0N/A protected final boolean hit(long fixedDate) {
0N/A return (fixedDate >= cachedFixedDateJan1 &&
0N/A fixedDate < cachedFixedDateNextJan1);
0N/A }
0N/A protected int getCachedYear() {
0N/A return cachedYear;
0N/A }
0N/A
0N/A protected long getCachedJan1() {
0N/A return cachedFixedDateJan1;
0N/A }
0N/A
0N/A protected void setCache(int year, long jan1, int len) {
0N/A cachedYear = year;
0N/A cachedFixedDateJan1 = jan1;
0N/A cachedFixedDateNextJan1 = jan1 + len;
0N/A }
0N/A }
0N/A
0N/A public boolean validate(CalendarDate date) {
0N/A Date bdate = (Date) date;
0N/A if (bdate.isNormalized()) {
0N/A return true;
0N/A }
0N/A int month = bdate.getMonth();
0N/A if (month < JANUARY || month > DECEMBER) {
0N/A return false;
0N/A }
0N/A int d = bdate.getDayOfMonth();
0N/A if (d <= 0 || d > getMonthLength(bdate.getNormalizedYear(), month)) {
0N/A return false;
0N/A }
0N/A int dow = bdate.getDayOfWeek();
0N/A if (dow != bdate.FIELD_UNDEFINED && dow != getDayOfWeek(bdate)) {
0N/A return false;
0N/A }
0N/A
0N/A if (!validateTime(date)) {
0N/A return false;
0N/A }
0N/A
0N/A bdate.setNormalized(true);
0N/A return true;
0N/A }
0N/A
0N/A public boolean normalize(CalendarDate date) {
0N/A if (date.isNormalized()) {
0N/A return true;
0N/A }
0N/A
0N/A Date bdate = (Date) date;
0N/A TimeZone zi = bdate.getZone();
0N/A
0N/A // If the date has a time zone, then we need to recalculate
0N/A // the calendar fields. Let getTime() do it.
0N/A if (zi != null) {
0N/A getTime(date);
0N/A return true;
0N/A }
0N/A
0N/A int days = normalizeTime(bdate);
0N/A normalizeMonth(bdate);
0N/A long d = (long)bdate.getDayOfMonth() + days;
0N/A int m = bdate.getMonth();
0N/A int y = bdate.getNormalizedYear();
0N/A int ml = getMonthLength(y, m);
0N/A
0N/A if (!(d > 0 && d <= ml)) {
0N/A if (d <= 0 && d > -28) {
0N/A ml = getMonthLength(y, --m);
0N/A d += ml;
0N/A bdate.setDayOfMonth((int) d);
0N/A if (m == 0) {
0N/A m = DECEMBER;
0N/A bdate.setNormalizedYear(y - 1);
0N/A }
0N/A bdate.setMonth(m);
0N/A } else if (d > ml && d < (ml + 28)) {
0N/A d -= ml;
0N/A ++m;
0N/A bdate.setDayOfMonth((int)d);
0N/A if (m > DECEMBER) {
0N/A bdate.setNormalizedYear(y + 1);
0N/A m = JANUARY;
0N/A }
0N/A bdate.setMonth(m);
0N/A } else {
0N/A long fixedDate = d + getFixedDate(y, m, 1, bdate) - 1L;
0N/A getCalendarDateFromFixedDate(bdate, fixedDate);
0N/A }
0N/A } else {
0N/A bdate.setDayOfWeek(getDayOfWeek(bdate));
0N/A }
0N/A date.setLeapYear(isLeapYear(bdate.getNormalizedYear()));
0N/A date.setZoneOffset(0);
0N/A date.setDaylightSaving(0);
0N/A bdate.setNormalized(true);
0N/A return true;
0N/A }
0N/A
0N/A void normalizeMonth(CalendarDate date) {
0N/A Date bdate = (Date) date;
0N/A int year = bdate.getNormalizedYear();
0N/A long month = bdate.getMonth();
0N/A if (month <= 0) {
0N/A long xm = 1L - month;
0N/A year -= (int)((xm / 12) + 1);
0N/A month = 13 - (xm % 12);
0N/A bdate.setNormalizedYear(year);
0N/A bdate.setMonth((int) month);
0N/A } else if (month > DECEMBER) {
0N/A year += (int)((month - 1) / 12);
0N/A month = ((month - 1)) % 12 + 1;
0N/A bdate.setNormalizedYear(year);
0N/A bdate.setMonth((int) month);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns 366 if the specified date is in a leap year, or 365
0N/A * otherwise This method does not perform the normalization with
0N/A * the specified <code>CalendarDate</code>. The
0N/A * <code>CalendarDate</code> must be normalized to get a correct
0N/A * value.
0N/A *
0N/A * @param a <code>CalendarDate</code>
0N/A * @return a year length in days
0N/A * @throws ClassCastException if the specified date is not a
0N/A * {@link BaseCalendar.Date}
0N/A */
0N/A public int getYearLength(CalendarDate date) {
0N/A return isLeapYear(((Date)date).getNormalizedYear()) ? 366 : 365;
0N/A }
0N/A
0N/A public int getYearLengthInMonths(CalendarDate date) {
0N/A return 12;
0N/A }
0N/A
0N/A static final int[] DAYS_IN_MONTH
0N/A // 12 1 2 3 4 5 6 7 8 9 10 11 12
0N/A = { 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
0N/A static final int[] ACCUMULATED_DAYS_IN_MONTH
0N/A // 12/1 1/1 2/1 3/1 4/1 5/1 6/1 7/1 8/1 9/1 10/1 11/1 12/1
0N/A = { -30, 0, 31, 59, 90,120,151,181,212,243, 273, 304, 334};
0N/A
0N/A static final int[] ACCUMULATED_DAYS_IN_MONTH_LEAP
0N/A // 12/1 1/1 2/1 3/1 4/1 5/1 6/1 7/1 8/1 9/1 10/1 11/1 12/1
0N/A = { -30, 0, 31, 59+1, 90+1,120+1,151+1,181+1,212+1,243+1, 273+1, 304+1, 334+1};
0N/A
0N/A public int getMonthLength(CalendarDate date) {
0N/A Date gdate = (Date) date;
0N/A int month = gdate.getMonth();
0N/A if (month < JANUARY || month > DECEMBER) {
0N/A throw new IllegalArgumentException("Illegal month value: " + month);
0N/A }
0N/A return getMonthLength(gdate.getNormalizedYear(), month);
0N/A }
0N/A
0N/A // accepts 0 (December in the previous year) to 12.
0N/A private final int getMonthLength(int year, int month) {
0N/A int days = DAYS_IN_MONTH[month];
0N/A if (month == FEBRUARY && isLeapYear(year)) {
0N/A days++;
0N/A }
0N/A return days;
0N/A }
0N/A
0N/A public long getDayOfYear(CalendarDate date) {
0N/A return getDayOfYear(((Date)date).getNormalizedYear(),
0N/A date.getMonth(),
0N/A date.getDayOfMonth());
0N/A }
0N/A
0N/A final long getDayOfYear(int year, int month, int dayOfMonth) {
0N/A return (long) dayOfMonth
0N/A + (isLeapYear(year) ?
0N/A ACCUMULATED_DAYS_IN_MONTH_LEAP[month] : ACCUMULATED_DAYS_IN_MONTH[month]);
0N/A }
0N/A
0N/A // protected
0N/A public long getFixedDate(CalendarDate date) {
0N/A if (!date.isNormalized()) {
0N/A normalizeMonth(date);
0N/A }
0N/A return getFixedDate(((Date)date).getNormalizedYear(),
0N/A date.getMonth(),
0N/A date.getDayOfMonth(),
0N/A (BaseCalendar.Date) date);
0N/A }
0N/A
0N/A // public for java.util.GregorianCalendar
0N/A public long getFixedDate(int year, int month, int dayOfMonth, BaseCalendar.Date cache) {
0N/A boolean isJan1 = month == JANUARY && dayOfMonth == 1;
0N/A
0N/A // Look up the one year cache
0N/A if (cache != null && cache.hit(year)) {
0N/A if (isJan1) {
0N/A return cache.getCachedJan1();
0N/A }
0N/A return cache.getCachedJan1() + getDayOfYear(year, month, dayOfMonth) - 1;
0N/A }
0N/A
0N/A // Look up the pre-calculated fixed date table
0N/A int n = year - BASE_YEAR;
0N/A if (n >= 0 && n < FIXED_DATES.length) {
0N/A long jan1 = FIXED_DATES[n];
0N/A if (cache != null) {
0N/A cache.setCache(year, jan1, isLeapYear(year) ? 366 : 365);
0N/A }
0N/A return isJan1 ? jan1 : jan1 + getDayOfYear(year, month, dayOfMonth) - 1;
0N/A }
0N/A
0N/A long prevyear = (long)year - 1;
0N/A long days = dayOfMonth;
0N/A
0N/A if (prevyear >= 0) {
0N/A days += (365 * prevyear)
0N/A + (prevyear / 4)
0N/A - (prevyear / 100)
0N/A + (prevyear / 400)
0N/A + ((367 * month - 362) / 12);
0N/A } else {
0N/A days += (365 * prevyear)
0N/A + CalendarUtils.floorDivide(prevyear, 4)
0N/A - CalendarUtils.floorDivide(prevyear, 100)
0N/A + CalendarUtils.floorDivide(prevyear, 400)
0N/A + CalendarUtils.floorDivide((367 * month - 362), 12);
0N/A }
0N/A
0N/A if (month > FEBRUARY) {
0N/A days -= isLeapYear(year) ? 1 : 2;
0N/A }
0N/A
0N/A // If it's January 1, update the cache.
0N/A if (cache != null && isJan1) {
0N/A cache.setCache(year, days, isLeapYear(year) ? 366 : 365);
0N/A }
0N/A
0N/A return days;
0N/A }
0N/A
0N/A /**
0N/A * Calculates calendar fields and store them in the specified
0N/A * <code>CalendarDate</code>.
0N/A */
0N/A // should be 'protected'
0N/A public void getCalendarDateFromFixedDate(CalendarDate date,
0N/A long fixedDate) {
0N/A Date gdate = (Date) date;
0N/A int year;
0N/A long jan1;
0N/A boolean isLeap;
0N/A if (gdate.hit(fixedDate)) {
0N/A year = gdate.getCachedYear();
0N/A jan1 = gdate.getCachedJan1();
0N/A isLeap = isLeapYear(year);
0N/A } else {
0N/A // Looking up FIXED_DATES[] here didn't improve performance
0N/A // much. So we calculate year and jan1. getFixedDate()
0N/A // will look up FIXED_DATES[] actually.
0N/A year = getGregorianYearFromFixedDate(fixedDate);
0N/A jan1 = getFixedDate(year, JANUARY, 1, null);
0N/A isLeap = isLeapYear(year);
0N/A // Update the cache data
0N/A gdate.setCache (year, jan1, isLeap ? 366 : 365);
0N/A }
0N/A
0N/A int priorDays = (int)(fixedDate - jan1);
0N/A long mar1 = jan1 + 31 + 28;
0N/A if (isLeap) {
0N/A ++mar1;
0N/A }
0N/A if (fixedDate >= mar1) {
0N/A priorDays += isLeap ? 1 : 2;
0N/A }
0N/A int month = 12 * priorDays + 373;
0N/A if (month > 0) {
0N/A month /= 367;
0N/A } else {
0N/A month = CalendarUtils.floorDivide(month, 367);
0N/A }
0N/A long month1 = jan1 + ACCUMULATED_DAYS_IN_MONTH[month];
0N/A if (isLeap && month >= MARCH) {
0N/A ++month1;
0N/A }
0N/A int dayOfMonth = (int)(fixedDate - month1) + 1;
0N/A int dayOfWeek = getDayOfWeekFromFixedDate(fixedDate);
0N/A assert dayOfWeek > 0 : "negative day of week " + dayOfWeek;
0N/A gdate.setNormalizedYear(year);
0N/A gdate.setMonth(month);
0N/A gdate.setDayOfMonth(dayOfMonth);
0N/A gdate.setDayOfWeek(dayOfWeek);
0N/A gdate.setLeapYear(isLeap);
0N/A gdate.setNormalized(true);
0N/A }
0N/A
0N/A /**
0N/A * Returns the day of week of the given Gregorian date.
0N/A */
0N/A public int getDayOfWeek(CalendarDate date) {
0N/A long fixedDate = getFixedDate(date);
0N/A return getDayOfWeekFromFixedDate(fixedDate);
0N/A }
0N/A
0N/A public static final int getDayOfWeekFromFixedDate(long fixedDate) {
0N/A // The fixed day 1 (January 1, 1 Gregorian) is Monday.
0N/A if (fixedDate >= 0) {
0N/A return (int)(fixedDate % 7) + SUNDAY;
0N/A }
0N/A return (int)CalendarUtils.mod(fixedDate, 7) + SUNDAY;
0N/A }
0N/A
0N/A public int getYearFromFixedDate(long fixedDate) {
0N/A return getGregorianYearFromFixedDate(fixedDate);
0N/A }
0N/A
0N/A /**
0N/A * Returns the Gregorian year number of the given fixed date.
0N/A */
0N/A final int getGregorianYearFromFixedDate(long fixedDate) {
0N/A long d0;
0N/A int d1, d2, d3, d4;
0N/A int n400, n100, n4, n1;
0N/A int year;
0N/A
0N/A if (fixedDate > 0) {
0N/A d0 = fixedDate - 1;
0N/A n400 = (int)(d0 / 146097);
0N/A d1 = (int)(d0 % 146097);
0N/A n100 = d1 / 36524;
0N/A d2 = d1 % 36524;
0N/A n4 = d2 / 1461;
0N/A d3 = d2 % 1461;
0N/A n1 = d3 / 365;
0N/A d4 = (d3 % 365) + 1;
0N/A } else {
0N/A d0 = fixedDate - 1;
0N/A n400 = (int)CalendarUtils.floorDivide(d0, 146097L);
0N/A d1 = (int)CalendarUtils.mod(d0, 146097L);
0N/A n100 = CalendarUtils.floorDivide(d1, 36524);
0N/A d2 = CalendarUtils.mod(d1, 36524);
0N/A n4 = CalendarUtils.floorDivide(d2, 1461);
0N/A d3 = CalendarUtils.mod(d2, 1461);
0N/A n1 = CalendarUtils.floorDivide(d3, 365);
0N/A d4 = CalendarUtils.mod(d3, 365) + 1;
0N/A }
0N/A year = 400 * n400 + 100 * n100 + 4 * n4 + n1;
0N/A if (!(n100 == 4 || n1 == 4)) {
0N/A ++year;
0N/A }
0N/A return year;
0N/A }
0N/A
0N/A /**
0N/A * @return true if the specified year is a Gregorian leap year, or
0N/A * false otherwise.
0N/A * @see BaseCalendar#isGregorianLeapYear
0N/A */
0N/A protected boolean isLeapYear(CalendarDate date) {
0N/A return isLeapYear(((Date)date).getNormalizedYear());
0N/A }
0N/A
0N/A boolean isLeapYear(int normalizedYear) {
0N/A return CalendarUtils.isGregorianLeapYear(normalizedYear);
0N/A }
0N/A}