Path: blob/master/src/java.base/share/classes/sun/util/calendar/AbstractCalendar.java
41159 views
/*1* Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.util.calendar;2627import java.util.Locale;28import java.util.TimeZone;2930/**31* The <code>AbstractCalendar</code> class provides a framework for32* implementing a concrete calendar system.33*34* <p><a name="fixed_date"></a><B>Fixed Date</B><br>35*36* For implementing a concrete calendar system, each calendar must37* have the common date numbering, starting from midnight the onset of38* Monday, January 1, 1 (Gregorian). It is called a <I>fixed date</I>39* in this class. January 1, 1 (Gregorian) is fixed date 1. (See40* Nachum Dershowitz and Edward M. Reingold, <I>CALENDRICAL41* CALCULATION The Millennium Edition</I>, Section 1.2 for details.)42*43* @author Masayoshi Okutsu44* @since 1.545*/4647public abstract class AbstractCalendar extends CalendarSystem {4849// The constants assume no leap seconds support.50static final int SECOND_IN_MILLIS = 1000;51static final int MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;52static final int HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;53static final int DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;5455// The number of days between January 1, 1 and January 1, 1970 (Gregorian)56static final int EPOCH_OFFSET = 719163;5758private Era[] eras;5960protected AbstractCalendar() {61}6263public Era getEra(String eraName) {64if (eras != null) {65for (Era era : eras) {66if (era.getName().equals(eraName)) {67return era;68}69}70}71return null;72}7374public Era[] getEras() {75Era[] e = null;76if (eras != null) {77e = new Era[eras.length];78System.arraycopy(eras, 0, e, 0, eras.length);79}80return e;81}8283public void setEra(CalendarDate date, String eraName) {84if (eras == null) {85return; // should report an error???86}87for (int i = 0; i < eras.length; i++) {88Era e = eras[i];89if (e != null && e.getName().equals(eraName)) {90date.setEra(e);91return;92}93}94throw new IllegalArgumentException("unknown era name: " + eraName);95}9697protected void setEras(Era[] eras) {98this.eras = eras;99}100101public CalendarDate getCalendarDate() {102return getCalendarDate(System.currentTimeMillis(), newCalendarDate());103}104105public CalendarDate getCalendarDate(long millis) {106return getCalendarDate(millis, newCalendarDate());107}108109public CalendarDate getCalendarDate(long millis, TimeZone zone) {110CalendarDate date = newCalendarDate(zone);111return getCalendarDate(millis, date);112}113114public CalendarDate getCalendarDate(long millis, CalendarDate date) {115int ms = 0; // time of day116int zoneOffset = 0;117int saving = 0;118long days = 0; // fixed date119120// adjust to local time if `date' has time zone.121TimeZone zi = date.getZone();122if (zi != null) {123int[] offsets = new int[2];124if (zi instanceof ZoneInfo) {125zoneOffset = ((ZoneInfo)zi).getOffsets(millis, offsets);126} else {127zoneOffset = zi.getOffset(millis);128offsets[0] = zi.getRawOffset();129offsets[1] = zoneOffset - offsets[0];130}131132// We need to calculate the given millis and time zone133// offset separately for java.util.GregorianCalendar134// compatibility. (i.e., millis + zoneOffset could cause135// overflow or underflow, which must be avoided.) Usually136// days should be 0 and ms is in the range of -13:00 to137// +14:00. However, we need to deal with extreme cases.138days = zoneOffset / DAY_IN_MILLIS;139ms = zoneOffset % DAY_IN_MILLIS;140saving = offsets[1];141}142date.setZoneOffset(zoneOffset);143date.setDaylightSaving(saving);144145days += millis / DAY_IN_MILLIS;146ms += (int) (millis % DAY_IN_MILLIS);147if (ms >= DAY_IN_MILLIS) {148// at most ms is (DAY_IN_MILLIS - 1) * 2.149ms -= DAY_IN_MILLIS;150++days;151} else {152// at most ms is (1 - DAY_IN_MILLIS) * 2. Adding one153// DAY_IN_MILLIS results in still negative.154while (ms < 0) {155ms += DAY_IN_MILLIS;156--days;157}158}159160// convert to fixed date (offset from Jan. 1, 1 (Gregorian))161days += EPOCH_OFFSET;162163// calculate date fields from the fixed date164getCalendarDateFromFixedDate(date, days);165166// calculate time fields from the time of day167setTimeOfDay(date, ms);168date.setLeapYear(isLeapYear(date));169date.setNormalized(true);170return date;171}172173public long getTime(CalendarDate date) {174long gd = getFixedDate(date);175long ms = (gd - EPOCH_OFFSET) * DAY_IN_MILLIS + getTimeOfDay(date);176int zoneOffset = 0;177TimeZone zi = date.getZone();178if (zi != null) {179if (date.isNormalized()) {180return ms - date.getZoneOffset();181}182// adjust time zone and daylight saving183int[] offsets = new int[2];184if (date.isStandardTime()) {185// 1) 2:30am during starting-DST transition is186// intrepreted as 2:30am ST187// 2) 5:00pm during DST is still interpreted as 5:00pm ST188// 3) 1:30am during ending-DST transition is interpreted189// as 1:30am ST (after transition)190if (zi instanceof ZoneInfo) {191((ZoneInfo)zi).getOffsetsByStandard(ms, offsets);192zoneOffset = offsets[0];193} else {194zoneOffset = zi.getOffset(ms - zi.getRawOffset());195}196} else {197// 1) 2:30am during starting-DST transition is198// intrepreted as 3:30am DT199// 2) 5:00pm during DST is intrepreted as 5:00pm DT200// 3) 1:30am during ending-DST transition is interpreted201// as 1:30am DT/0:30am ST (before transition)202if (zi instanceof ZoneInfo) {203zoneOffset = ((ZoneInfo)zi).getOffsetsByWall(ms, offsets);204} else {205zoneOffset = zi.getOffset(ms - zi.getRawOffset());206}207}208}209ms -= zoneOffset;210getCalendarDate(ms, date);211return ms;212}213214protected long getTimeOfDay(CalendarDate date) {215long fraction = date.getTimeOfDay();216if (fraction != CalendarDate.TIME_UNDEFINED) {217return fraction;218}219fraction = getTimeOfDayValue(date);220date.setTimeOfDay(fraction);221return fraction;222}223224public long getTimeOfDayValue(CalendarDate date) {225long fraction = date.getHours();226fraction *= 60;227fraction += date.getMinutes();228fraction *= 60;229fraction += date.getSeconds();230fraction *= 1000;231fraction += date.getMillis();232return fraction;233}234235public CalendarDate setTimeOfDay(CalendarDate cdate, int fraction) {236if (fraction < 0) {237throw new IllegalArgumentException();238}239boolean normalizedState = cdate.isNormalized();240int time = fraction;241int hours = time / HOUR_IN_MILLIS;242time %= HOUR_IN_MILLIS;243int minutes = time / MINUTE_IN_MILLIS;244time %= MINUTE_IN_MILLIS;245int seconds = time / SECOND_IN_MILLIS;246time %= SECOND_IN_MILLIS;247cdate.setHours(hours);248cdate.setMinutes(minutes);249cdate.setSeconds(seconds);250cdate.setMillis(time);251cdate.setTimeOfDay(fraction);252if (hours < 24 && normalizedState) {253// If this time of day setting doesn't affect the date,254// then restore the normalized state.255cdate.setNormalized(normalizedState);256}257return cdate;258}259260/**261* Returns 7 in this default implementation.262*263* @return 7264*/265public int getWeekLength() {266return 7;267}268269protected abstract boolean isLeapYear(CalendarDate date);270271public CalendarDate getNthDayOfWeek(int nth, int dayOfWeek, CalendarDate date) {272CalendarDate ndate = (CalendarDate) date.clone();273normalize(ndate);274long fd = getFixedDate(ndate);275long nfd;276if (nth > 0) {277nfd = 7 * nth + getDayOfWeekDateBefore(fd, dayOfWeek);278} else {279nfd = 7 * nth + getDayOfWeekDateAfter(fd, dayOfWeek);280}281getCalendarDateFromFixedDate(ndate, nfd);282return ndate;283}284285/**286* Returns a date of the given day of week before the given fixed287* date.288*289* @param fixedDate the fixed date290* @param dayOfWeek the day of week291* @return the calculated date292*/293static long getDayOfWeekDateBefore(long fixedDate, int dayOfWeek) {294return getDayOfWeekDateOnOrBefore(fixedDate - 1, dayOfWeek);295}296297/**298* Returns a date of the given day of week that is closest to and299* after the given fixed date.300*301* @param fixedDate the fixed date302* @param dayOfWeek the day of week303* @return the calculated date304*/305static long getDayOfWeekDateAfter(long fixedDate, int dayOfWeek) {306return getDayOfWeekDateOnOrBefore(fixedDate + 7, dayOfWeek);307}308309/**310* Returns a date of the given day of week on or before the given fixed311* date.312*313* @param fixedDate the fixed date314* @param dayOfWeek the day of week315* @return the calculated date316*/317// public for java.util.GregorianCalendar318public static long getDayOfWeekDateOnOrBefore(long fixedDate, int dayOfWeek) {319long fd = fixedDate - (dayOfWeek - 1);320if (fd >= 0) {321return fixedDate - (fd % 7);322}323return fixedDate - CalendarUtils.mod(fd, 7);324}325326/**327* Returns the fixed date calculated with the specified calendar328* date. If the specified date is not normalized, its date fields329* are normalized.330*331* @param date a <code>CalendarDate</code> with which the fixed332* date is calculated333* @return the calculated fixed date334* @see AbstractCalendar.html#fixed_date335*/336protected abstract long getFixedDate(CalendarDate date);337338/**339* Calculates calendar fields from the specified fixed date. This340* method stores the calculated calendar field values in the specified341* <code>CalendarDate</code>.342*343* @param date a <code>CalendarDate</code> to stored the344* calculated calendar fields.345* @param fixedDate a fixed date to calculate calendar fields346* @see AbstractCalendar.html#fixed_date347*/348protected abstract void getCalendarDateFromFixedDate(CalendarDate date,349long fixedDate);350351public boolean validateTime(CalendarDate date) {352int t = date.getHours();353if (t < 0 || t >= 24) {354return false;355}356t = date.getMinutes();357if (t < 0 || t >= 60) {358return false;359}360t = date.getSeconds();361// TODO: Leap second support.362if (t < 0 || t >= 60) {363return false;364}365t = date.getMillis();366if (t < 0 || t >= 1000) {367return false;368}369return true;370}371372373int normalizeTime(CalendarDate date) {374long fraction = getTimeOfDay(date);375long days = 0;376377if (fraction >= DAY_IN_MILLIS) {378days = fraction / DAY_IN_MILLIS;379fraction %= DAY_IN_MILLIS;380} else if (fraction < 0) {381days = CalendarUtils.floorDivide(fraction, DAY_IN_MILLIS);382if (days != 0) {383fraction -= DAY_IN_MILLIS * days; // mod(fraction, DAY_IN_MILLIS)384}385}386if (days != 0) {387date.setTimeOfDay(fraction);388}389date.setMillis((int)(fraction % 1000));390fraction /= 1000;391date.setSeconds((int)(fraction % 60));392fraction /= 60;393date.setMinutes((int)(fraction % 60));394date.setHours((int)(fraction / 60));395return (int)days;396}397}398399400