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