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

Lines 1–500 of 538.