/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program 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.
*
* This program 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 at /legal/license.txt).
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package java.util;
/**
* <code>Calendar</code> is an abstract class for getting and setting dates
* using a set of integer fields such as
* <code>YEAR</code>, <code>MONTH</code>, <code>DAY</code>,
* and so on. (A <code>Date</code> object represents a specific instant in
* time with millisecond precision. See
* {@link Date}
* for information about the <code>Date</code> class.)
*
* <p>
* Subclasses of <code>Calendar</code> interpret a <code>Date</code>
* according to the rules of a specific calendar system.
*
* <p>
* Like other locale-sensitive classes, <code>Calendar</code> provides a
* class method, <code>getInstance</code>, for getting a generally useful
* object of this type.
* <blockquote>
* <pre>
* Calendar rightNow = Calendar.getInstance();
* </pre>
* </blockquote>
*
* <p>
* A <code>Calendar</code> object can produce all the time field values
* needed to implement the date-time formatting for a particular language
* and calendar style (for example, Japanese-Gregorian, Japanese-Traditional).
*
* <p>
* When computing a <code>Date</code> from time fields,
* there may be insufficient information to compute the
* <code>Date</code> (such as only year and month but no day in the month).
*
* <p>
* <strong>Insufficient information.</strong> The calendar will use default
* information to specify the missing fields. This may vary by calendar; for
* the Gregorian calendar, the default for a field is the same as that of the
* start of the epoch: i.e., YEAR = 1970, MONTH = JANUARY, DATE = 1, etc.
*
* <p>
* <strong>Inconsistent information.</strong> In the J2SE calendar, it is
* possible to set fields inconsistently. However, in this subset, the
* DAY_OF_WEEK field cannot be set, and only a subset of the other J2SE
* Calendar fields are included. So it is not possible to set
* inconsistent data.
* <p>
*
* <strong>Note:</strong> The ambiguity in interpretation of what day midnight
* belongs to, is resolved as so: midnight "belongs" to the following day.<br>
* 23:59 on Dec 31, 1969 < 00:00 on Jan 1, 1970.<br>
* 12:00 PM is midday, and 12:00 AM is midnight.<br>
* 11:59 PM on Jan 1 < 12:00 AM on Jan 2 < 12:01 AM on Jan 2.<br>
* 11:59 AM on Mar 10 < 12:00 PM on Mar 10 < 12:01 PM on Mar 10.<br>
* 24:00 or greater are invalid.
* Hours greater than 12 are invalid in AM/PM mode.
* Setting the time will never change the date.
* <p>
* If equivalent times are entered in AM/PM or 24 hour mode, equality will be
* determined by the actual time rather than the entered time.
* <p>
*
* This class is a subset for J2ME of the J2SE Calendar class.
* Many methods and variables have been
* pruned, and other methods simplified, in an effort to reduce the size
* of this class.
*
*
* @see TimeZone
* @version 1.0 (J2ME MIDP)
*/
public abstract class Calendar {
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* year. This is a calendar-specific value.
*/
public final static int YEAR = 1;
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* month. This is a calendar-specific value.
*/
public final static int MONTH = 2;
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* day of the month. This is a synonym for <code>DAY_OF_MONTH</code>.
* @see #DAY_OF_MONTH
*/
public final static int DATE = 5;
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* day of the month. This is a synonym for <code>DATE</code>.
* @see #DATE
*/
public final static int DAY_OF_MONTH = 5;
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* day of the week.
*/
public final static int DAY_OF_WEEK = 7;
/**
* Field number for <code>get</code> and <code>set</code> indicating
* whether the <code>HOUR</code> is before or after noon.
* E.g., at 10:04:15.250 PM the <code>AM_PM</code> is <code>PM</code>.
* @see #AM
* @see #PM
* @see #HOUR
*/
public final static int AM_PM = 9;
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* hour of the morning or afternoon. <code>HOUR</code> is used for the
* 12-hour clock.
* E.g., at 10:04:15.250 PM the <code>HOUR</code> is 10.
* @see #AM_PM
* @see #HOUR_OF_DAY
*/
public final static int HOUR = 10;
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* hour of the day. <code>HOUR_OF_DAY</code> is used for the 24-hour clock.
* E.g., at 10:04:15.250 PM the <code>HOUR_OF_DAY</code> is 22.
*/
public final static int HOUR_OF_DAY = 11;
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* minute within the hour.
* E.g., at 10:04:15.250 PM the <code>MINUTE</code> is 4.
*/
public final static int MINUTE = 12;
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* second within the minute.
* E.g., at 10:04:15.250 PM the <code>SECOND</code> is 15.
*/
public final static int SECOND = 13;
/**
* Field number for <code>get</code> and <code>set</code> indicating the
* millisecond within the second.
* E.g., at 10:04:15.250 PM the <code>MILLISECOND</code> is 250.
*/
public final static int MILLISECOND = 14;
/**
* Value of the <code>DAY_OF_WEEK</code> field indicating
* Sunday.
*/
public final static int SUNDAY = 1;
/**
* Value of the <code>DAY_OF_WEEK</code> field indicating
* Monday.
*/
public final static int MONDAY = 2;
/**
* Value of the <code>DAY_OF_WEEK</code> field indicating
* Tuesday.
*/
public final static int TUESDAY = 3;
/**
* Value of the <code>DAY_OF_WEEK</code> field indicating
* Wednesday.
*/
public final static int WEDNESDAY = 4;
/**
* Value of the <code>DAY_OF_WEEK</code> field indicating
* Thursday.
*/
public final static int THURSDAY = 5;
/**
* Value of the <code>DAY_OF_WEEK</code> field indicating
* Friday.
*/
public final static int FRIDAY = 6;
/**
* Value of the <code>DAY_OF_WEEK</code> field indicating
* Saturday.
*/
public final static int SATURDAY = 7;
/**
* Value of the <code>MONTH</code> field indicating the
* first month of the year.
*/
public final static int JANUARY = 0;
/**
* Value of the <code>MONTH</code> field indicating the
* second month of the year.
*/
public final static int FEBRUARY = 1;
/**
* Value of the <code>MONTH</code> field indicating the
* third month of the year.
*/
public final static int MARCH = 2;
/**
* Value of the <code>MONTH</code> field indicating the
* fourth month of the year.
*/
public final static int APRIL = 3;
/**
* Value of the <code>MONTH</code> field indicating the
* fifth month of the year.
*/
public final static int MAY = 4;
/**
* Value of the <code>MONTH</code> field indicating the
* sixth month of the year.
*/
public final static int JUNE = 5;
/**
* Value of the <code>MONTH</code> field indicating the
* seventh month of the year.
*/
public final static int JULY = 6;
/**
* Value of the <code>MONTH</code> field indicating the
* eighth month of the year.
*/
public final static int AUGUST = 7;
/**
* Value of the <code>MONTH</code> field indicating the
* ninth month of the year.
*/
public final static int SEPTEMBER = 8;
/**
* Value of the <code>MONTH</code> field indicating the
* tenth month of the year.
*/
public final static int OCTOBER = 9;
/**
* Value of the <code>MONTH</code> field indicating the
* eleventh month of the year.
*/
public final static int NOVEMBER = 10;
/**
* Value of the <code>MONTH</code> field indicating the
* twelfth month of the year.
*/
public final static int DECEMBER = 11;
/**
* Value of the <code>AM_PM</code> field indicating the
* period of the day from midnight to just before noon.
*/
public final static int AM = 0;
/**
* Value of the <code>AM_PM</code> field indicating the
* period of the day from noon to just before midnight.
*/
public final static int PM = 1;
// Internal notes:
// Calendar contains two kinds of time representations: current "time" in
// milliseconds, and a set of time "fields" representing the current time.
// The two representations are usually in sync, but can get out of sync
// as follows.
// 1. Initially, no fields are set, and the time is invalid.
// 2. If the time is set, all fields are computed and in sync.
// 3. If a single field is set, the time is invalid.
// Recomputation of the time and fields happens when the object needs
// to return a result to the user, or use a result for a computation.
private int packed_time = 0;
private int packed_date = 0;
private int day_field = 0;
private int dstOffset = 0;
private boolean dstSet = false;
/**
* The currently set time for this calendar, expressed in milliseconds after
* January 1, 1970, 0:00:00 GMT.
*/
private long time;
/**
* True if then the value of <code>time</code> is valid.
* The time is made invalid by a change to an item of <code>field[]</code>.
* @see #time
*/
private boolean millisSet; // NOTE: Make transient when possible
/**
* The <code>TimeZone</code> used by this calendar. </code>Calendar</code>
* uses the time zone data to translate between locale and GMT time.
*/
private TimeZone zone;
//////////////////
// Class Variables
//////////////////
private static final int JAN_1_1_JULIAN_DAY = 1721426; // January 1, year 1 (Gregorian)
private static final int EPOCH_JULIAN_DAY = 2440588; // Jaunary 1, 1970 (Gregorian)
private static final int EPOCH_YEAR = 1970;
private static final int NUM_DAYS[]
= {0,31,59,90,120,151,181,212,243,273,304,334}; // 0-based, for day-in-year
private static final int LEAP_NUM_DAYS[]
= {0,31,60,91,121,152,182,213,244,274,305,335}; // 0-based, for day-in-year
// Useful millisecond constants. Although ONE_DAY and ONE_WEEK can fit
// into ints, they must be longs in order to prevent arithmetic overflow
// when performing (CR 4173516).
private static final int ONE_SECOND = 1000;
private static final int ONE_MINUTE = 60*ONE_SECOND;
private static final int ONE_HOUR = 60*ONE_MINUTE;
private static final long ONE_DAY = 24*ONE_HOUR;
private static final long ONE_WEEK = 7*ONE_DAY;
/////////////////////
// Instance Variables
/////////////////////
/**
* The point at which the Gregorian calendar rules are used, measured in
* milliseconds from the standard epoch. Default is October 15, 1582
* (Gregorian) 00:00:00 UTC or -12219292800000L. For this value, October 4,
* 1582 (Julian) is followed by October 15, 1582 (Gregorian). This
* corresponds to Julian day number 2299161.
*/
private static final long gregorianCutover = -12219292800000L;
/**
* The year of the gregorianCutover, with 0 representing
* 1 BC, -1 representing 2 BC, etc.
*/
private static final int gregorianCutoverYear = 1582;
private Date date = null;
/** if both of these are set, the set() method will recalculate
* the HOUR_OF_DAY using 12hr time.
*/
private int hour_12hr = -1;
private int am_pm_12hr = -1;
/**
* The platform name
*/
private static String platform = null;
/**
* The root of the classes
*/
private static String classRoot = null;
/**
* Constructs a Calendar with the default time zone
* and default locale.
*
* @see TimeZone#getDefault
*/
protected Calendar() {
zone = TimeZone.getDefault();
if (zone == null) {
throw new RuntimeException(
/* #ifdef VERBOSE_EXCEPTIONS */
/// skipped "Could not find default timezone"
/* #endif */
);
}
setTimeInMillis(System.currentTimeMillis());
}
/**
* Gets this Calendar's current time.
*
* @return the current time.
*
* @see #setTime
*/
public final Date getTime() {
if (date == null)
return date = new Date( getTimeInMillis() );
else {
synchronized (date) {
date.setTime( getTimeInMillis() );
return date;
}
}
}
/**
* Sets this Calendar's current time with the given Date.
* <p>
* Note: Calling <code>setTime()</code> with
* <code>Date(Long.MAX_VALUE)</code> or <code>Date(Long.MIN_VALUE)</code>
* may yield incorrect field values from <code>get()</code>.
*
* @param date the given Date.
*
* @see #getTime
*/
public final void setTime(Date date) {
setTimeInMillis( date.getTime() );
}
/**
* Gets a calendar using the default time zone and default locale.
*
* @return a Calendar.
*/
/* <p>
* The following is information for implementers. Applications should
* not need to be aware of this or rely on it, because each
* implementation may do it differently:
* <p>
* The Calendar will look up a class the name of which includes
* the platform name. The class name will take the form: <p>
*
* <code>{classRoot}.util.{platform}.CalendarImpl</code>
*
* <p>
* The classRoot is derived from the system by looking up the system
* property "microedition.implpath". If this property key is not
* found or the associated class is not present then "com.sun.cldc"
* is used.
*/
public static synchronized Calendar getInstance() {
if (platform == null) {
/* Setup the platform name */
platform = "j2me";
/* See if there is an alternate protocol class root */
classRoot = System.getProperty("microedition.implpath");
if (classRoot == null) {
classRoot = "com.sun.cldc";
}
}
try {
/* Using the platform and protocol names lookup a class to implement the connection */
Class clazz = Class.forName(classRoot+".util."+platform+".CalendarImpl");
/* Construct a new instance */
return (Calendar)clazz.newInstance();
}
catch (Exception x) {}
return null;
}
/**
* Gets a calendar using the specified time zone and default locale.
* @param zone the time zone to use
* @return a Calendar.
*/
public static synchronized Calendar getInstance(TimeZone zone) {
Calendar cal = getInstance();
cal.setTimeZone(zone);
return cal;
}
/**
* Gets this Calendar's current time as a long expressed in milliseconds
* after January 1, 1970, 0:00:00 GMT (the epoch).
*
* @return the current time as UTC milliseconds from the epoch.
*
* @see #setTimeInMillis
*/
protected long getTimeInMillis() {
if (!millisSet) {
calculateTime();
millisSet = true;
}
return time;
}
/**
* Sets this Calendar's current time from the given long value.
* @param millis the new time in UTC milliseconds from the epoch.
*
* @see #getTimeInMillis
*/
protected void setTimeInMillis( long millis ) {
millisSet = true;
day_field = 0;
time = millis;
calculateFields();
}
/**
* Gets the value for a given time field.
* @param field the given time field (either YEAR, MONTH, DATE, DAY_OF_WEEK,
* HOUR_OF_DAY, HOUR, AM_PM, MINUTE,
* SECOND, or MILLISECOND
* @return the value for the given time field.
* @exception ArrayIndexOutOfBoundsException if the parameter is not
* one of the above.
*/
public final int get(int field) {
switch (field) {
case YEAR: return packed_date >> 9;
case MONTH: return (packed_date >> 5) & 15;
case DATE: return packed_date & 31;
case DAY_OF_WEEK: {
if (day_field == 0) {
getTimeInMillis();
calculateFields();
}
return day_field;
}
case HOUR_OF_DAY: return packed_time >> 22;
case HOUR: {
int hr = (packed_time >> 22) % 12;
return hr == 0? 12 : hr;
}
case AM_PM: return (packed_time >> 22) < 12? AM: PM;
case MINUTE: return (packed_time >> 16) & 63;
case SECOND: return (packed_time >> 10) & 63;
case MILLISECOND: return packed_time & 1023;
}
throw new ArrayIndexOutOfBoundsException();
}
/**
* Sets the time field with the given value.
*
* @param field the given time field.
* Note that the DAY_OF_WEEK field cannot be set.
* @param value the value to be set for the given time field.
*
* @exception ArrayIndexOutOfBoundsException if an illegal field
* parameter is received.
*/
public final void set(int field, int value) {
millisSet = false;
day_field = 0;
switch (field) {
case YEAR: // 16383 = (2 to the power 14) - 1
packed_date = (packed_date & 511) | (value << 9);
break;
case MONTH:
packed_date = (packed_date & (~(15<<5))) | ((value & 15) << 5);
break;
case DATE:
packed_date = (packed_date & (~31)) | (value & 31);
break;
case HOUR_OF_DAY:
value = value % 24;
packed_time = (packed_time & 4194303) | (value << 22);
hour_12hr = am_pm_12hr = -1;
break;
case HOUR: {
if (value > 12)
value = 12;
if (am_pm_12hr != -1) {
if (am_pm_12hr == PM) {
if (value != 12)
value += 12;
}
else if (value == 12)
value = 0;
hour_12hr = am_pm_12hr = -1;
}
else
hour_12hr = value;
packed_time = (packed_time & 4194303) | (value << 22);
break;
}
case AM_PM: {
if (hour_12hr != -1) {
if (value == PM) {
if (hour_12hr != 12)
hour_12hr += 12;
}
else if (hour_12hr == 12)
hour_12hr = 0;
packed_time = (packed_time & 4194303) | (hour_12hr << 22);
hour_12hr = am_pm_12hr = -1;
}
else
am_pm_12hr = value;
break;
}
case MINUTE:
packed_time = (packed_time & (~(63<<16))) | ((value & 63) << 16);
break;
case SECOND:
packed_time = (packed_time & (~(63<<10))) | ((value & 63) << 10);
break;
case MILLISECOND:
packed_time = (packed_time & (~1023)) | (value & 1023);
break;
default: throw new ArrayIndexOutOfBoundsException();
}
dstSet = false;
}
/**
* Compares this calendar to the specified object.
* The result is <code>true</code> if and only if the argument is
* not <code>null</code> and is a <code>Calendar</code> object that
* represents the same calendar as this object.
* @param obj the object to compare with.
* @return <code>true</code> if the objects are the same;
* <code>false</code> otherwise.
*/
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof Calendar))
return false;
Calendar that = (Calendar)obj;
return getTimeInMillis() == that.getTimeInMillis() && zone.equals(that.zone);
}
/**
* Compares the time field records.
* Equivalent to comparing result of conversion to UTC.
* @param when the Calendar to be compared with this Calendar.
* @return true if the current time of this Calendar is before
* the time of Calendar when; false otherwise.
*/
public boolean before(Object when) {
return (when instanceof Calendar
&& getTimeInMillis() < ((Calendar)when).getTimeInMillis());
}
/**
* Compares the time field records.
* Equivalent to comparing result of conversion to UTC.
* @param when the Calendar to be compared with this Calendar.
* @return true if the current time of this Calendar is after
* the time of Calendar when; false otherwise.
*/
public boolean after(Object when) {
return (when instanceof Calendar
&& getTimeInMillis() > ((Calendar)when).getTimeInMillis());
}
/**
* Sets the time zone with the given time zone value.
* @param value the given time zone.
*
* @see #getTimeZone
*/
public void setTimeZone(TimeZone value) {
zone = value;
getTimeInMillis();
calculateFields();
}
/**
* Gets the time zone.
* @return the time zone object associated with this calendar.
*
* @see #setTimeZone
*/
public TimeZone getTimeZone() {
return zone;
}
/////////////////////////////
// Time => Fields computation
/////////////////////////////
private void calculateDstOffset() {
if (day_field == 0) {
getTimeInMillis();
calculateFields();
}
int rawOffset = zone.getRawOffset();
long localMillis = time + rawOffset;
long days = (long) (localMillis / ONE_DAY);
int millisInDay = (int) (localMillis - (days * ONE_DAY));
if (millisInDay < 0)
millisInDay += ONE_DAY;
dstOffset = zone.getOffset(1,
packed_date >> 9,
(packed_date >> 5) & 15,
packed_date & 31,
day_field,
millisInDay) - rawOffset;
dstSet = true;
}
/**
* Converts UTC as milliseconds to time field values.
*/
private void calculateFields() {
int rawOffset = zone.getRawOffset();
long localMillis = time + rawOffset;
/* Check for very extreme values -- millis near Long.MIN_VALUE or
* Long.MAX_VALUE. For these values, adding the zone offset can push
* the millis past MAX_VALUE to MIN_VALUE, or vice versa. This produces
* the undesirable effect that the time can wrap around at the ends,
* yielding, for example, a Date(Long.MAX_VALUE) with a big BC year
* (should be AD). Handle this by pinning such values to Long.MIN_VALUE
* or Long.MAX_VALUE. - liu 8/11/98 CR 4149677 */
if (time > 0 && localMillis < 0 && rawOffset > 0) {
localMillis = Long.MAX_VALUE;
} else if (time < 0 && localMillis > 0 && rawOffset < 0) {
localMillis = Long.MIN_VALUE;
}
// Time to fields takes the wall millis (Standard or DST).
timeToFields(localMillis);
long days = (long) (localMillis / ONE_DAY);
int millisInDay = (int) (localMillis - (days * ONE_DAY));
if (millisInDay < 0) millisInDay += ONE_DAY;
// Call getOffset() to get the TimeZone offset. The millisInDay value must
// be standard local millis.
dstOffset = zone.getOffset(1,
packed_date >> 9,
(packed_date >> 5) & 15,
packed_date & 31,
day_field,
millisInDay) - rawOffset;
dstSet = true;
// Adjust our millisInDay for DST, if necessary.
millisInDay += dstOffset;
// If DST has pushed us into the next day, we must call timeToFields() again.
// This happens in DST between 12:00 am and 1:00 am every day. The call to
// timeToFields() will give the wrong day, since the Standard time is in the
// previous day.
if (millisInDay >= ONE_DAY) {
long dstMillis = localMillis + dstOffset;
millisInDay -= ONE_DAY;
// As above, check for and pin extreme values
if (localMillis > 0 && dstMillis < 0 && dstOffset > 0) {
dstMillis = Long.MAX_VALUE;
} else if (localMillis < 0 && dstMillis > 0 && dstOffset < 0) {
dstMillis = Long.MIN_VALUE;
}
timeToFields(dstMillis);
}
// Fill in all time-related fields based on millisInDay.
// so as not to perturb flags.
packed_time = (packed_time & (~1023)) | (millisInDay % 1000);
millisInDay /= 1000;
packed_time = (packed_time & (~(63<<10))) | ((millisInDay % 60) << 10);
millisInDay /= 60;
packed_time = (packed_time & (~(63<<16))) | ((millisInDay % 60) << 16);
millisInDay /= 60;
packed_time = (packed_time & (~(31<<22))) | ((millisInDay & 31) << 22);
}
/**
* Convert the time as milliseconds to the date fields. Millis must be
* given as local wall millis to get the correct local day. For example,
* if it is 11:30 pm Standard, and DST is in effect, the correct DST millis
* must be passed in to get the right date.
* <p>
* Fields that are completed by this method: YEAR, MONTH, DATE, DAY_OF_WEEK.
* @param theTime the time in wall millis (either Standard or DST),
* whichever is in effect
* @param quick if true, only compute the YEAR, MONTH, DATE, and DAY_OF_WEEK.
*/
private final void timeToFields(long theTime) {
int dayOfYear, weekCount, year_field;
boolean isLeap;
// Compute the year, month, and day of month from the given millis
if (theTime >= gregorianCutover) {
// The Gregorian epoch day is zero for Monday January 1, year 1.
long gregorianEpochDay = millisToJulianDay(theTime) - JAN_1_1_JULIAN_DAY;
// Here we convert from the day number to the multiple radix
// representation. We use 400-year, 100-year, and 4-year cycles.
// For example, the 4-year cycle has 4 years + 1 leap day; giving
// 1461 == 365*4 + 1 days.
int[] rem = new int[1];
int n400 = floorDivide(gregorianEpochDay, 146097, rem); // 400-year cycle length
int n100 = floorDivide(rem[0], 36524, rem); // 100-year cycle length
int n4 = floorDivide(rem[0], 1461, rem); // 4-year cycle length
int n1 = floorDivide(rem[0], 365, rem);
year_field = 400*n400 + 100*n100 + 4*n4 + n1;
dayOfYear = rem[0]; // zero-based day of year
if (n100 == 4 || n1 == 4) dayOfYear = 365; // Dec 31 at end of 4- or 400-yr cycle
else ++year_field;
isLeap = ((year_field&0x3) == 0) && // equiv. to (year_field%4 == 0)
(year_field%100 != 0 || year_field%400 == 0);
// Gregorian day zero is a Monday
day_field = (int)((gregorianEpochDay+1) % 7);
}
else {
// The Julian epoch day (not the same as Julian Day)
// is zero on Saturday December 30, 0 (Gregorian).
long julianEpochDay = millisToJulianDay(theTime) - (JAN_1_1_JULIAN_DAY - 2);
year_field = (int) floorDivide(4*julianEpochDay + 1464, 1461);
// Compute the Julian calendar day number for January 1, year
long january1 = 365*(year_field-1) + floorDivide(year_field-1, 4);
dayOfYear = (int)(julianEpochDay - january1); // 0-based
// Julian leap years occurred historically every 4 years starting
// with 8 AD. Before 8 AD the spacing is irregular; every 3 years
// from 45 BC to 9 BC, and then none until 8 AD. However, we don't
// implement this historical detail; instead, we implement the
// computationally cleaner proleptic calendar, which assumes
// consistent 4-year cycles throughout time.
isLeap = ((year_field&0x3) == 0); // equiv. to (year_field%4 == 0)
// Julian calendar day zero is a Saturday
day_field = (int)((julianEpochDay-1) % 7);
}
// Common Julian/Gregorian calculation
int correction = 0;
int march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
if (dayOfYear >= march1) correction = isLeap ? 1 : 2;
int month_field = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month
int date_field = dayOfYear -
(isLeap ? LEAP_NUM_DAYS[month_field] : NUM_DAYS[month_field]) + 1; // one-based DOM
// Normalize day of week
day_field += (day_field < 0) ? (SUNDAY+7) : SUNDAY;
month_field += JANUARY; // 0-based
packed_date = year_field << 9;
packed_date |= (month_field & 15) << 5;
packed_date |= date_field & 31;
}
/////////////////////////////
// Fields => Time computation
/////////////////////////////
/**
* Converts time field values to UTC as milliseconds.
* @exception IllegalArgumentException if any fields are invalid.
*/
private void calculateTime() {
// This function takes advantage of the fact that unset fields in
// the time field list have a value of zero.
// First, use the year to determine whether to use the Gregorian or the
// Julian calendar. If the year is not the year of the cutover, this
// computation will be correct. But if the year is the cutover year,
// this may be incorrect. In that case, assume the Gregorian calendar,
// make the computation, and then recompute if the resultant millis
// indicate the wrong calendar has been assumed.
// A date such as Oct. 10, 1582 does not exist in a Gregorian calendar
// with the default changeover of Oct. 15, 1582, since in such a
// calendar Oct. 4 (Julian) is followed by Oct. 15 (Gregorian). This
// algorithm will interpret such a date using the Julian calendar,
// yielding Oct. 20, 1582 (Gregorian).
int year_field = packed_date >> 9;
boolean isGregorian = year_field >= gregorianCutoverYear;
long julianDay = calculateJulianDay(isGregorian, year_field);
long millis = julianDayToMillis(julianDay);
// The following check handles portions of the cutover year BEFORE the
// cutover itself happens. The check for the julianDate number is for a
// rare case; it's a hardcoded number, but it's efficient. The given
// Julian day number corresponds to Dec 3, 292269055 BC, which
// corresponds to millis near Long.MIN_VALUE. The need for the check
// arises because for extremely negative Julian day numbers, the millis
// actually overflow to be positive values. Without the check, the
// initial date is interpreted with the Gregorian calendar, even when
// the cutover doesn't warrant it.
if (isGregorian != (millis >= gregorianCutover) &&
julianDay != -106749550580L) { // See above
julianDay = calculateJulianDay(!isGregorian, year_field);
millis = julianDayToMillis(julianDay);
}
// Do the time portion of the conversion.
int millisInDay = 0;
// Hours
// Don't normalize here; let overflow bump into the next period.
// This is consistent with how we handle other fields.
millisInDay += (packed_time >> 22) & 31;
millisInDay *= 60;
millisInDay += (packed_time >> 16) & 63; // now have minutes
millisInDay *= 60;
millisInDay += (packed_time >> 10) & 63; // now have seconds
millisInDay *= 1000;
millisInDay += packed_time & 1023; // now have millis
// Compute the time zone offset and DST offset. There are two potential
// ambiguities here. We'll assume a 2:00 am (wall time) switchover time
// for discussion purposes here.
// 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
// can be in standard or in DST depending. However, 2:00 am is an invalid
// representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
// We assume standard time.
// 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
// can be in standard or DST. Both are valid representations (the rep
// jumps from 1:59:59 DST to 1:00:00 Std).
// Again, we assume standard time.
// We use the TimeZone object to get the zone offset
int zoneOffset = zone.getRawOffset();
// Now add date and millisInDay together, to make millis contain local wall
// millis, with no zone or DST adjustments
millis += millisInDay;
dstOffset = 0;
/* Normalize the millisInDay to 0..ONE_DAY-1. If the millis is out
* of range, then we must call timeToFields() to recompute our
* fields. */
int[] normalizedMillisInDay = new int[1];
floorDivide(millis, (int)ONE_DAY, normalizedMillisInDay);
// We need to have the month, the day, and the day of the week.
// Calling timeToFields will compute the MONTH and DATE fields.
//
// It's tempting to try to use DAY_OF_WEEK here, if it
// is set, but we CAN'T. Even if it's set, it might have
// been set wrong by the user. We should rely only on
// the Julian day number, which has been computed correctly
// using the disambiguation algorithm above. [LIU]
int dow = julianDayToDayOfWeek(julianDay);
// It's tempting to try to use DAY_OF_WEEK here, if it
// is set, but we CAN'T. Even if it's set, it might have
// been set wrong by the user. We should rely only on
// the Julian day number, which has been computed correctly
// using the disambiguation algorithm above. [LIU]
dstOffset = zone.getOffset(1,
packed_date >> 9,
(packed_date >> 5) & 15,
packed_date & 31,
dow,
normalizedMillisInDay[0]) -
zoneOffset;
dstSet = true;
// Note: Because we pass in wall millisInDay, rather than
// standard millisInDay, we interpret "1:00 am" on the day
// of cessation of DST as "1:00 am Std" (assuming the time
// of cessation is 2:00 am).
// Store our final computed GMT time, with timezone adjustments.
time = millis - zoneOffset - dstOffset;
}
/**
* Compute the Julian day number under either the Gregorian or the
* Julian calendar, using the given year and the remaining fields.
* @param isGregorian if true, use the Gregorian calendar
* @param year the adjusted year number, with 0 indicating the
* year 1 BC, -1 indicating 2 BC, etc.
* @return the Julian day number
*/
private final long calculateJulianDay(boolean isGregorian, int year) {
int month = 0, y;
long millis = 0;
month = (packed_date >> 5) & 15 - JANUARY;
// If the month is out of range, adjust it into range
if (month < 0 || month > 11) {
int[] rem = new int[1];
year += floorDivide(month, 12, rem);
month = rem[0];
}
boolean isLeap = year%4 == 0;
y = year - 1;
long julianDay = 365L*y + floorDivide(y, 4) + (JAN_1_1_JULIAN_DAY - 3);
if (isGregorian) {
isLeap = isLeap && ((year%100 != 0) || (year%400 == 0));
// Add 2 because Gregorian calendar starts 2 days after Julian calendar
julianDay += floorDivide(y, 400) - floorDivide(y, 100) + 2;
}
// At this point julianDay is the 0-based day BEFORE the first day of
// January 1, year 1 of the given calendar. If julianDay == 0, it
// specifies (Jan. 1, 1) - 1, in whatever calendar we are using (Julian
// or Gregorian).
julianDay += isLeap ? LEAP_NUM_DAYS[month] : NUM_DAYS[month];
julianDay += packed_date & 31;
return julianDay;
}
/////////////////
// Implementation
/////////////////
/**
* Converts time as milliseconds to Julian day.
* @param millis the given milliseconds.
* @return the Julian day number.
*/
private static final long millisToJulianDay(long millis) {
return EPOCH_JULIAN_DAY + floorDivide(millis, ONE_DAY);
}
/**
* Converts Julian day to time as milliseconds.
* @param julian the given Julian day number.
* @return time as milliseconds.
*/
private static final long julianDayToMillis(long julian) {
return (julian - EPOCH_JULIAN_DAY) * ONE_DAY;
}
private static final int julianDayToDayOfWeek(long julian) {
// If julian is negative, then julian%7 will be negative, so we adjust
// accordingly. We add 1 because Julian day 0 is Monday.
int dayOfWeek = (int)((julian + 1) % 7);
return dayOfWeek + ((dayOfWeek < 0) ? (7 + SUNDAY) : SUNDAY);
}
/**
* Divide two long integers, returning the floor of the quotient.
* <p>
* Unlike the built-in division, this is mathematically well-behaved.
* E.g., <code>-1/4</code> => 0
* but <code>floorDivide(-1,4)</code> => -1.
* @param numerator the numerator
* @param denominator a divisor which must be > 0
* @return the floor of the quotient.
*/
private static final long floorDivide(long numerator, long denominator) {
// We do this computation in order to handle
// a numerator of Long.MIN_VALUE correctly
return (numerator >= 0) ?
numerator / denominator :
((numerator + 1) / denominator) - 1;
}
/**
* Divide two integers, returning the floor of the quotient.
* <p>
* Unlike the built-in division, this is mathematically well-behaved.
* E.g., <code>-1/4</code> => 0
* but <code>floorDivide(-1,4)</code> => -1.
* @param numerator the numerator
* @param denominator a divisor which must be > 0
* @return the floor of the quotient.
*/
private static final int floorDivide(int numerator, int denominator) {
// We do this computation in order to handle
// a numerator of Integer.MIN_VALUE correctly
return (numerator >= 0) ?
numerator / denominator :
((numerator + 1) / denominator) - 1;
}
/**
* Divide two integers, returning the floor of the quotient, and
* the modulus remainder.
* <p>
* Unlike the built-in division, this is mathematically well-behaved.
* E.g., <code>-1/4</code> => 0 and <code>-1%4</code> => -1,
* but <code>floorDivide(-1,4)</code> => -1 with <code>remainder[0]</code> => 3.
* @param numerator the numerator
* @param denominator a divisor which must be > 0
* @param remainder an array of at least one element in which the value
* <code>numerator mod denominator</code> is returned. Unlike <code>numerator
* % denominator</code>, this will always be non-negative.
* @return the floor of the quotient.
*/
private static final int floorDivide(int numerator, int denominator, int[] remainder) {
if (numerator >= 0) {
remainder[0] = numerator % denominator;
return numerator / denominator;
}
int quotient = ((numerator + 1) / denominator) - 1;
remainder[0] = numerator - (quotient * denominator);
return quotient;
}
/**
* Divide two integers, returning the floor of the quotient, and
* the modulus remainder.
* <p>
* Unlike the built-in division, this is mathematically well-behaved.
* E.g., <code>-1/4</code> => 0 and <code>-1%4</code> => -1,
* but <code>floorDivide(-1,4)</code> => -1 with <code>remainder[0]</code> => 3.
* @param numerator the numerator
* @param denominator a divisor which must be > 0
* @param remainder an array of at least one element in which the value
* <code>numerator mod denominator</code> is returned. Unlike <code>numerator
* % denominator</code>, this will always be non-negative.
* @return the floor of the quotient.
*/
private static final int floorDivide(long numerator, int denominator, int[] remainder) {
if (numerator >= 0) {
remainder[0] = (int)(numerator % denominator);
return (int)(numerator / denominator);
}
int quotient = (int)(((numerator + 1) / denominator) - 1);
remainder[0] = (int)(numerator - (quotient * denominator));
return quotient;
}
}
|