/* * 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; }}
/*
* 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;
}
}