398 lines · 13.4 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>AbstractCalendar</code> class provides a framework for * implementing a concrete calendar system. * * <p><a name="fixed_date"></a><B>Fixed Date</B><br> * * For implementing a concrete calendar system, each calendar must * have the common date numbering, starting from midnight the onset of * Monday, January 1, 1 (Gregorian). It is called a <I>fixed date</I> * in this class. January 1, 1 (Gregorian) is fixed date 1. (See * Nachum Dershowitz and Edward M. Reingold, <I>CALENDRICAL * CALCULATION The Millennium Edition</I>, Section 1.2 for details.) * * @author Masayoshi Okutsu * @since 1.5 */ public abstract class AbstractCalendar extends CalendarSystem {     // The constants assume no leap seconds support.    static final int SECOND_IN_MILLIS = 1000;    static final int MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;    static final int HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;    static final int DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;     // The number of days between January 1, 1 and January 1, 1970 (Gregorian)    static final int EPOCH_OFFSET = 719163;     private Era[] eras;     protected AbstractCalendar() {    }     public Era getEra(String eraName) {        if (eras != null) {            for (int i = 0; i < eras.length; i++) {                if (eras[i].equals(eraName)) {                    return eras[i];                }            }        }        return null;    }     public Era[] getEras() {        Era[] e = null;        if (eras != null) {            e = new Era[eras.length];            System.arraycopy(eras, 0, e, 0, eras.length);        }        return e;    }     public void setEra(CalendarDate date, String eraName) {        if (eras == null) {            return; // should report an error???        }        for (int i = 0; i < eras.length; i++) {            Era e = eras[i];            if (e != null && e.getName().equals(eraName)) {                date.setEra(e);                return;            }        }        throw new IllegalArgumentException("unknown era name: " + eraName);    }     protected void setEras(Era[] eras) {        this.eras = eras;    }     public CalendarDate getCalendarDate() {        return getCalendarDate(System.currentTimeMillis(), newCalendarDate());    }     public CalendarDate getCalendarDate(long millis) {        return getCalendarDate(millis, newCalendarDate());    }     public CalendarDate getCalendarDate(long millis, TimeZone zone) {        CalendarDate date = newCalendarDate(zone);        return getCalendarDate(millis, date);    }     public CalendarDate getCalendarDate(long millis, CalendarDate date) {        int ms = 0;             // time of day        int zoneOffset = 0;        int saving = 0;        long days = 0;          // fixed date         // adjust to local time if `date' has time zone.        TimeZone zi = date.getZone();        if (zi != null) {            int[] offsets = new int[2];            if (zi instanceof ZoneInfo) {                zoneOffset = ((ZoneInfo)zi).getOffsets(millis, offsets);            } else {                zoneOffset = zi.getOffset(millis);                offsets[0] = zi.getRawOffset();                offsets[1] = zoneOffset - offsets[0];            }             // We need to calculate the given millis and time zone            // offset separately for java.util.GregorianCalendar            // compatibility. (i.e., millis + zoneOffset could cause            // overflow or underflow, which must be avoided.) Usually            // days should be 0 and ms is in the range of -13:00 to            // +14:00. However, we need to deal with extreme cases.            days = zoneOffset / DAY_IN_MILLIS;            ms = zoneOffset % DAY_IN_MILLIS;            saving = offsets[1];        }        date.setZoneOffset(zoneOffset);        date.setDaylightSaving(saving);         days += millis / DAY_IN_MILLIS;        ms += (int) (millis % DAY_IN_MILLIS);        if (ms >= DAY_IN_MILLIS) {            // at most ms is (DAY_IN_MILLIS - 1) * 2.            ms -= DAY_IN_MILLIS;            ++days;        } else {            // at most ms is (1 - DAY_IN_MILLIS) * 2. Adding one            // DAY_IN_MILLIS results in still negative.            while (ms < 0) {                ms += DAY_IN_MILLIS;                --days;            }        }         // convert to fixed date (offset from Jan. 1, 1 (Gregorian))        days += EPOCH_OFFSET;         // calculate date fields from the fixed date        getCalendarDateFromFixedDate(date, days);         // calculate time fields from the time of day        setTimeOfDay(date, ms);        date.setLeapYear(isLeapYear(date));        date.setNormalized(true);        return date;    }     public long getTime(CalendarDate date) {        long gd = getFixedDate(date);        long ms = (gd - EPOCH_OFFSET) * DAY_IN_MILLIS + getTimeOfDay(date);        int zoneOffset = 0;        TimeZone zi = date.getZone();        if (zi != null) {            if (date.isNormalized()) {                return ms - date.getZoneOffset();            }            // adjust time zone and daylight saving            int[] offsets = new int[2];            if (date.isStandardTime()) {                // 1) 2:30am during starting-DST transition is                //    intrepreted as 2:30am ST                // 2) 5:00pm during DST is still interpreted as 5:00pm ST                // 3) 1:30am during ending-DST transition is interpreted                //    as 1:30am ST (after transition)                if (zi instanceof ZoneInfo) {                    ((ZoneInfo)zi).getOffsetsByStandard(ms, offsets);                    zoneOffset = offsets[0];                } else {                    zoneOffset = zi.getOffset(ms - zi.getRawOffset());                }            } else {                // 1) 2:30am during starting-DST transition is                //    intrepreted as 3:30am DT                // 2) 5:00pm during DST is intrepreted as 5:00pm DT                // 3) 1:30am during ending-DST transition is interpreted                //    as 1:30am DT/0:30am ST (before transition)                if (zi instanceof ZoneInfo) {                    zoneOffset = ((ZoneInfo)zi).getOffsetsByWall(ms, offsets);                } else {                    zoneOffset = zi.getOffset(ms - zi.getRawOffset());                }            }        }        ms -= zoneOffset;        getCalendarDate(ms, date);        return ms;    }     protected long getTimeOfDay(CalendarDate date) {        long fraction = date.getTimeOfDay();        if (fraction != CalendarDate.TIME_UNDEFINED) {            return fraction;        }        fraction = getTimeOfDayValue(date);        date.setTimeOfDay(fraction);        return fraction;    }     public long getTimeOfDayValue(CalendarDate date) {        long fraction = date.getHours();        fraction *= 60;        fraction += date.getMinutes();        fraction *= 60;        fraction += date.getSeconds();        fraction *= 1000;        fraction += date.getMillis();        return fraction;    }     public CalendarDate setTimeOfDay(CalendarDate cdate, int fraction) {        if (fraction < 0) {            throw new IllegalArgumentException();        }        boolean normalizedState = cdate.isNormalized();        int time = fraction;        int hours = time / HOUR_IN_MILLIS;        time %= HOUR_IN_MILLIS;        int minutes = time / MINUTE_IN_MILLIS;        time %= MINUTE_IN_MILLIS;        int seconds = time / SECOND_IN_MILLIS;        time %= SECOND_IN_MILLIS;        cdate.setHours(hours);        cdate.setMinutes(minutes);        cdate.setSeconds(seconds);        cdate.setMillis(time);        cdate.setTimeOfDay(fraction);        if (hours < 24 && normalizedState) {            // If this time of day setting doesn't affect the date,            // then restore the normalized state.            cdate.setNormalized(normalizedState);        }        return cdate;    }     /**     * Returns 7 in this default implementation.     *     * @return 7     */    public int getWeekLength() {        return 7;    }     protected abstract boolean isLeapYear(CalendarDate date);     public CalendarDate getNthDayOfWeek(int nth, int dayOfWeek, CalendarDate date) {        CalendarDate ndate = (CalendarDate) date.clone();        normalize(ndate);        long fd = getFixedDate(ndate);        long nfd;        if (nth > 0) {            nfd = 7 * nth + getDayOfWeekDateBefore(fd, dayOfWeek);        } else {            nfd = 7 * nth + getDayOfWeekDateAfter(fd, dayOfWeek);        }        getCalendarDateFromFixedDate(ndate, nfd);        return ndate;    }     /**     * Returns a date of the given day of week before the given fixed     * date.     *     * @param fixedDate the fixed date     * @param dayOfWeek the day of week     * @return the calculated date     */    static long getDayOfWeekDateBefore(long fixedDate, int dayOfWeek) {        return getDayOfWeekDateOnOrBefore(fixedDate - 1, dayOfWeek);    }     /**     * Returns a date of the given day of week that is closest to and     * after the given fixed date.     *     * @param fixedDate the fixed date     * @param dayOfWeek the day of week     * @return the calculated date     */    static long getDayOfWeekDateAfter(long fixedDate, int dayOfWeek) {        return getDayOfWeekDateOnOrBefore(fixedDate + 7, dayOfWeek);    }     /**     * Returns a date of the given day of week on or before the given fixed     * date.     *     * @param fixedDate the fixed date     * @param dayOfWeek the day of week     * @return the calculated date     */    // public for java.util.GregorianCalendar    public static long getDayOfWeekDateOnOrBefore(long fixedDate, int dayOfWeek) {        long fd = fixedDate - (dayOfWeek - 1);        if (fd >= 0) {            return fixedDate - (fd % 7);        }        return fixedDate - CalendarUtils.mod(fd, 7);    }     /**     * Returns the fixed date calculated with the specified calendar     * date. If the specified date is not normalized, its date fields     * are normalized.     *     * @param date a <code>CalendarDate</code> with which the fixed     * date is calculated     * @return the calculated fixed date     * @see AbstractCalendar.html#fixed_date     */    protected abstract long getFixedDate(CalendarDate date);     /**     * Calculates calendar fields from the specified fixed date. This     * method stores the calculated calendar field values in the specified     * <code>CalendarDate</code>.     *     * @param date a <code>CalendarDate</code> to stored the     * calculated calendar fields.     * @param fixedDate a fixed date to calculate calendar fields     * @see AbstractCalendar.html#fixed_date     */    protected abstract void getCalendarDateFromFixedDate(CalendarDate date,                                                         long fixedDate);     public boolean validateTime(CalendarDate date) {        int t = date.getHours();        if (t < 0 || t >= 24) {            return false;        }        t = date.getMinutes();        if (t < 0 || t >= 60) {            return false;        }        t = date.getSeconds();        // TODO: Leap second support.        if (t < 0 || t >= 60) {            return false;        }        t = date.getMillis();        if (t < 0 || t >= 1000) {            return false;        }        return true;    }      int normalizeTime(CalendarDate date) {        long fraction = getTimeOfDay(date);        long days = 0;         if (fraction >= DAY_IN_MILLIS) {            days = fraction / DAY_IN_MILLIS;            fraction %= DAY_IN_MILLIS;        } else if (fraction < 0) {            days = CalendarUtils.floorDivide(fraction, DAY_IN_MILLIS);            if (days != 0) {                fraction -= DAY_IN_MILLIS * days; // mod(fraction, DAY_IN_MILLIS)            }        }        if (days != 0) {            date.setTimeOfDay(fraction);        }        date.setMillis((int)(fraction % 1000));        fraction /= 1000;        date.setSeconds((int)(fraction % 60));        fraction /= 60;        date.setMinutes((int)(fraction % 60));        date.setHours((int)(fraction / 60));        return (int)days;    }}