GregorianCalendar.java [plain text]
package java.util;
public class GregorianCalendar extends Calendar
{
public static final int BC = 0;
public static final int AD = 1;
private long gregorianCutover;
static final long serialVersionUID = -8125100834729963327L;
private static final String bundleName = "gnu.java.locale.Calendar";
public GregorianCalendar()
{
this(TimeZone.getDefault(), Locale.getDefault());
}
public GregorianCalendar(TimeZone zone)
{
this(zone, Locale.getDefault());
}
public GregorianCalendar(Locale locale)
{
this(TimeZone.getDefault(), locale);
}
public GregorianCalendar(TimeZone zone, Locale locale)
{
super(zone, locale);
ResourceBundle rb = ResourceBundle.getBundle(bundleName, locale);
gregorianCutover = ((Date) rb.getObject("gregorianCutOver")).getTime();
setTimeInMillis(System.currentTimeMillis());
}
public GregorianCalendar(int year, int month, int day)
{
super();
set(year, month, day);
}
public GregorianCalendar(int year, int month, int day, int hour, int minute)
{
super();
set(year, month, day, hour, minute);
}
public GregorianCalendar(int year, int month, int day,
int hour, int minute, int second)
{
super();
set(year, month, day, hour, minute, second);
}
public void setGregorianChange(Date date)
{
gregorianCutover = date.getTime();
}
public final Date getGregorianChange()
{
return new Date(gregorianCutover);
}
public boolean isLeapYear(int year)
{
if ((year & 3) != 0)
return false;
int julianDay = (((year-1) * (365*4+1)) >> 2) + (31+29 -
(((1970-1) * (365*4+1)) / 4 + 1 - 13));
if (julianDay * (24 * 60 * 60 * 1000L) < gregorianCutover)
return true;
return ((year % 100) != 0 || (year % 400) == 0);
}
private long getLinearTime(int year, int dayOfYear, int millis)
{
int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear -
((1970 * (365 * 4 + 1)) / 4 + 1 - 13);
long time = julianDay * (24 * 60 * 60 * 1000L) + millis;
if (time >= gregorianCutover)
{
int gregOffset = (year / 400) - (year / 100) + 2;
if (isLeapYear (year, true) && dayOfYear < 31 + 29)
--gregOffset;
time += gregOffset * (24 * 60 * 60 * 1000L);
}
return time;
}
private int getWeekDay(int year, int dayOfYear)
{
int day =
(int) (getLinearTime(year, dayOfYear, 0) / (24 * 60 * 60 * 1000L));
int weekday = (day + THURSDAY) % 7;
if (weekday <= 0)
weekday += 7;
return weekday;
}
private int[] getDayOfYear(int year)
{
if (isSet[MONTH])
{
int dayOfYear;
if (fields[MONTH] > FEBRUARY)
{
dayOfYear = (fields[MONTH] * (31 + 30 + 31 + 30 + 31) - 9) / 5;
if (isLeapYear(year))
dayOfYear++;
}
else
dayOfYear = 31 * fields[MONTH];
if (isSet[DAY_OF_MONTH])
{
return new int[]
{
dayOfYear + fields[DAY_OF_MONTH], 0};
}
if (isSet[WEEK_OF_MONTH] && isSet[DAY_OF_WEEK])
{
int weekday = getWeekDay(year, ++dayOfYear);
return new int[]
{
dayOfYear,
fields[DAY_OF_WEEK] - weekday +
7 * (fields[WEEK_OF_MONTH]
+ (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1)
+ (weekday < getFirstDayOfWeek()? -1 : 0))};
}
if (isSet[DAY_OF_WEEK] && isSet[DAY_OF_WEEK_IN_MONTH])
{
int weekday = getWeekDay(year, ++dayOfYear);
return new int[] {
dayOfYear,
fields[DAY_OF_WEEK] - weekday +
7 * (fields[DAY_OF_WEEK_IN_MONTH]
+ (fields[DAY_OF_WEEK] < weekday ? 0 : -1))};
}
}
if (isSet[DAY_OF_YEAR])
{
return new int[] {0, fields[DAY_OF_YEAR]};
}
if (isSet[DAY_OF_WEEK] && isSet[WEEK_OF_YEAR])
{
int dayOfYear = getMinimalDaysInFirstWeek();
int weekday = getWeekDay(year, dayOfYear);
return new int[] {
dayOfYear,
fields[DAY_OF_WEEK] - weekday
+ 7 * (fields[WEEK_OF_YEAR]
+ (fields[DAY_OF_WEEK] < getFirstDayOfWeek()? 0 : -1)
+ (weekday < getFirstDayOfWeek()? -1 : 0))};
}
return new int[] {1, 0};
}
protected synchronized void computeTime()
{
int era = isSet[ERA] ? fields[ERA] : AD;
int year = isSet[YEAR] ? fields[YEAR] : 1970;
if (era == BC)
year = 1 - year;
int[] daysOfYear = getDayOfYear(year);
int hour = 0;
if (isSet[HOUR_OF_DAY])
hour = fields[HOUR_OF_DAY];
else if (isSet[HOUR])
{
hour = fields[HOUR];
if (isSet[AM_PM] && fields[AM_PM] == PM)
hour += 12;
}
int minute = isSet[MINUTE] ? fields[MINUTE] : 0;
int second = isSet[SECOND] ? fields[SECOND] : 0;
int millis = isSet[MILLISECOND] ? fields[MILLISECOND] : 0;
int millisInDay;
if (isLenient())
{
long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L
+ millis;
daysOfYear[1] += allMillis / (24 * 60 * 60 * 1000L);
millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L));
}
else
{
if (hour < 0 || hour >= 24 || minute < 0 || minute > 59
|| second < 0 || second > 59 || millis < 0 || millis >= 1000)
throw new IllegalArgumentException();
millisInDay = (((hour * 60) + minute) * 60 + second) * 1000 + millis;
}
time = getLinearTime(year, daysOfYear[0], millisInDay);
time += daysOfYear[1] * (24 * 60 * 60 * 1000L);
TimeZone zone = getTimeZone();
int rawOffset = isSet[ZONE_OFFSET]
? fields[ZONE_OFFSET] : zone.getRawOffset();
int dayOfYear = daysOfYear[0] + daysOfYear[1];
int month = (dayOfYear * 5 + 3) / (31 + 30 + 31 + 30 + 31);
int day = (6 + (dayOfYear * 5 + 3) % (31 + 30 + 31 + 30 + 31)) / 5;
int weekday = ((int) (time / (24 * 60 * 60 * 1000L)) + THURSDAY) % 7;
if (weekday <= 0)
weekday += 7;
int dstOffset = isSet[DST_OFFSET]
? fields[DST_OFFSET] : (zone.getOffset((year < 0) ? BC : AD,
(year < 0) ? 1 - year : year,
month, day, weekday, millisInDay)
- zone.getRawOffset());
time -= rawOffset + dstOffset;
isTimeSet = true;
}
private boolean isLeapYear(int year, boolean gregorian)
{
if ((year & 3) != 0)
return false;
if (!gregorian)
return true;
return ((year % 100) != 0 || (year % 400) == 0);
}
private int getLinearDay(int year, int dayOfYear, boolean gregorian)
{
int julianDay = ((year * (365 * 4 + 1)) >> 2) + dayOfYear -
((1970 * (365 * 4 + 1)) / 4 + 1 - 13);
if (gregorian)
{
int gregOffset = (year / 400) - (year / 100) + 2;
if (isLeapYear (year, true) && dayOfYear < 31 + 29)
--gregOffset;
julianDay += gregOffset;
}
return julianDay;
}
private void calculateDay(int day, boolean gregorian)
{
int weekday = (day + THURSDAY) % 7;
if (weekday <= 0)
weekday += 7;
fields[DAY_OF_WEEK] = weekday;
int year = 1970 + (gregorian
? ((day - 100) * 400) / (365 * 400 + 100 - 4 + 1)
: ((day - 100) * 4) / (365 * 4 + 1));
if (day >= 0)
year++;
int firstDayOfYear = getLinearDay(year, 1, gregorian);
if (day < firstDayOfYear)
{
year--;
firstDayOfYear = getLinearDay(year, 1, gregorian);
}
day -= firstDayOfYear - 1;
fields[DAY_OF_YEAR] = day;
if (year <= 0)
{
fields[ERA] = BC;
fields[YEAR] = 1 - year;
}
else
{
fields[ERA] = AD;
fields[YEAR] = year;
}
int leapday = isLeapYear(year, gregorian) ? 1 : 0;
if (day <= 31 + 28 + leapday)
{
fields[MONTH] = day / 32; fields[DAY_OF_MONTH] = day - 31 * fields[MONTH];
}
else
{
int scaledDay = (day - leapday) * 5 + 8;
fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31);
fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1;
}
}
protected synchronized void computeFields()
{
boolean gregorian = (time >= gregorianCutover);
TimeZone zone = getTimeZone();
fields[ZONE_OFFSET] = zone.getRawOffset();
long localTime = time + fields[ZONE_OFFSET];
int day = (int) (localTime / (24 * 60 * 60 * 1000L));
int millisInDay = (int) (localTime % (24 * 60 * 60 * 1000L));
if (millisInDay < 0)
{
millisInDay += (24 * 60 * 60 * 1000);
day--;
}
calculateDay(day, gregorian);
fields[DST_OFFSET] =
zone.getOffset(fields[ERA], fields[YEAR], fields[MONTH],
fields[DAY_OF_MONTH], fields[DAY_OF_WEEK],
millisInDay) - fields[ZONE_OFFSET];
millisInDay += fields[DST_OFFSET];
if (millisInDay >= 24 * 60 * 60 * 1000)
{
millisInDay -= 24 * 60 * 60 * 1000;
calculateDay(++day, gregorian);
}
fields[DAY_OF_WEEK_IN_MONTH] = (fields[DAY_OF_MONTH] + 6) / 7;
int relativeWeekday = (7 + fields[DAY_OF_WEEK] - getFirstDayOfWeek()) % 7;
fields[WEEK_OF_MONTH] = (fields[DAY_OF_MONTH] - relativeWeekday + 6) / 7;
int weekOfYear = (fields[DAY_OF_YEAR] - relativeWeekday + 6) / 7;
int minDays = getMinimalDaysInFirstWeek();
int firstWeekday =
(7 + getWeekDay(fields[YEAR], minDays) - getFirstDayOfWeek()) % 7;
if (minDays - firstWeekday < 1)
weekOfYear++;
fields[WEEK_OF_YEAR] = weekOfYear;
int hourOfDay = millisInDay / (60 * 60 * 1000);
fields[AM_PM] = (hourOfDay < 12) ? AM : PM;
int hour = hourOfDay % 12;
fields[HOUR] = (hour == 0) ? 12 : hour;
fields[HOUR_OF_DAY] = hourOfDay;
millisInDay %= (60 * 60 * 1000);
fields[MINUTE] = millisInDay / (60 * 1000);
millisInDay %= (60 * 1000);
fields[SECOND] = millisInDay / (1000);
fields[MILLISECOND] = millisInDay % 1000;
areFieldsSet = isSet[ERA] = isSet[YEAR] = isSet[MONTH] =
isSet[WEEK_OF_YEAR] = isSet[WEEK_OF_MONTH] =
isSet[DAY_OF_MONTH] = isSet[DAY_OF_YEAR] = isSet[DAY_OF_WEEK] =
isSet[DAY_OF_WEEK_IN_MONTH] = isSet[AM_PM] = isSet[HOUR] =
isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] =
isSet[MILLISECOND] = isSet[ZONE_OFFSET] = isSet[DST_OFFSET] = true;
}
public boolean equals(Object o)
{
if (!(o instanceof GregorianCalendar))
return false;
GregorianCalendar cal = (GregorianCalendar) o;
return (cal.getTimeInMillis() == getTimeInMillis());
}
public void add(int field, int amount)
{
switch (field)
{
case YEAR:
complete();
fields[YEAR] += amount;
isTimeSet = false;
break;
case MONTH:
complete();
int months = fields[MONTH] + amount;
fields[YEAR] += months / 12;
fields[MONTH] = months % 12;
if (fields[MONTH] < 0)
{
fields[MONTH] += 12;
fields[YEAR]--;
}
isTimeSet = false;
int maxDay = getActualMaximum(DAY_OF_MONTH);
if (fields[DAY_OF_MONTH] > maxDay)
{
fields[DAY_OF_MONTH] = maxDay;
isTimeSet = false;
}
break;
case DAY_OF_MONTH:
case DAY_OF_YEAR:
case DAY_OF_WEEK:
if (!isTimeSet)
computeTime();
time += amount * (24 * 60 * 60 * 1000L);
areFieldsSet = false;
break;
case WEEK_OF_YEAR:
case WEEK_OF_MONTH:
case DAY_OF_WEEK_IN_MONTH:
if (!isTimeSet)
computeTime();
time += amount * (7 * 24 * 60 * 60 * 1000L);
areFieldsSet = false;
break;
case AM_PM:
if (!isTimeSet)
computeTime();
time += amount * (12 * 60 * 60 * 1000L);
areFieldsSet = false;
break;
case HOUR:
case HOUR_OF_DAY:
if (!isTimeSet)
computeTime();
time += amount * (60 * 60 * 1000L);
areFieldsSet = false;
break;
case MINUTE:
if (!isTimeSet)
computeTime();
time += amount * (60 * 1000L);
areFieldsSet = false;
break;
case SECOND:
if (!isTimeSet)
computeTime();
time += amount * (1000L);
areFieldsSet = false;
break;
case MILLISECOND:
if (!isTimeSet)
computeTime();
time += amount;
areFieldsSet = false;
break;
case ZONE_OFFSET:
complete();
fields[ZONE_OFFSET] += amount;
time -= amount;
break;
case DST_OFFSET:
complete();
fields[DST_OFFSET] += amount;
isTimeSet = false;
break;
default:
throw new IllegalArgumentException
("Unknown Calendar field: " + field);
}
}
public void roll(int field, boolean up)
{
roll(field, up ? 1 : -1);
}
private void cleanUpAfterRoll(int field, int delta)
{
switch (field)
{
case ERA:
case YEAR:
case MONTH:
if (fields[DAY_OF_MONTH] > getActualMaximum(DAY_OF_MONTH))
fields[DAY_OF_MONTH] = getActualMaximum(DAY_OF_MONTH);
isTimeSet = false;
isSet[WEEK_OF_MONTH] = false;
isSet[DAY_OF_WEEK] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false;
isSet[DAY_OF_YEAR] = false;
isSet[WEEK_OF_YEAR] = false;
break;
case DAY_OF_MONTH:
isSet[WEEK_OF_MONTH] = false;
isSet[DAY_OF_WEEK] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false;
isSet[DAY_OF_YEAR] = false;
isSet[WEEK_OF_YEAR] = false;
time += delta * (24 * 60 * 60 * 1000L);
break;
case WEEK_OF_MONTH:
isSet[DAY_OF_MONTH] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false;
isSet[DAY_OF_YEAR] = false;
isSet[WEEK_OF_YEAR] = false;
time += delta * (7 * 24 * 60 * 60 * 1000L);
break;
case DAY_OF_WEEK_IN_MONTH:
isSet[DAY_OF_MONTH] = false;
isSet[WEEK_OF_MONTH] = false;
isSet[DAY_OF_YEAR] = false;
isSet[WEEK_OF_YEAR] = false;
time += delta * (7 * 24 * 60 * 60 * 1000L);
break;
case DAY_OF_YEAR:
isSet[MONTH] = false;
isSet[DAY_OF_MONTH] = false;
isSet[WEEK_OF_MONTH] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false;
isSet[DAY_OF_WEEK] = false;
isSet[WEEK_OF_YEAR] = false;
time += delta * (24 * 60 * 60 * 1000L);
break;
case WEEK_OF_YEAR:
isSet[MONTH] = false;
isSet[DAY_OF_MONTH] = false;
isSet[WEEK_OF_MONTH] = false;
isSet[DAY_OF_WEEK_IN_MONTH] = false;
isSet[DAY_OF_YEAR] = false;
time += delta * (7 * 24 * 60 * 60 * 1000L);
break;
case AM_PM:
isSet[HOUR_OF_DAY] = false;
time += delta * (12 * 60 * 60 * 1000L);
break;
case HOUR:
isSet[HOUR_OF_DAY] = false;
time += delta * (60 * 60 * 1000L);
break;
case HOUR_OF_DAY:
isSet[HOUR] = false;
isSet[AM_PM] = false;
time += delta * (60 * 60 * 1000L);
break;
case MINUTE:
time += delta * (60 * 1000L);
break;
case SECOND:
time += delta * (1000L);
break;
case MILLISECOND:
time += delta;
break;
}
}
public void roll(int field, int amount)
{
switch (field)
{
case DAY_OF_WEEK:
add(field, amount);
return;
case ZONE_OFFSET:
case DST_OFFSET:
throw new IllegalArgumentException("Can't roll time zone");
}
complete();
int min = getActualMinimum(field);
int range = getActualMaximum(field) - min + 1;
int oldval = fields[field];
int newval = (oldval - min + range + amount) % range + min;
if (newval < min)
newval += range;
fields[field] = newval;
cleanUpAfterRoll(field, newval - oldval);
}
private static final int[] minimums =
{ BC, 1, 0, 0, 1, 1, 1, SUNDAY, 1,
AM, 1, 0, 1, 1, 1, -(12*60*60*1000), 0 };
private static final int[] maximums =
{ AD, 5000000, 11, 53, 5, 31, 366, SATURDAY, 5,
PM, 12, 23, 59, 59, 999, +(12*60*60*1000), (12*60*60*1000) };
public int getMinimum(int field)
{
return minimums[field];
}
public int getMaximum(int field)
{
return maximums[field];
}
public int getGreatestMinimum(int field)
{
if (field == WEEK_OF_YEAR)
return 1;
return minimums[field];
}
public int getLeastMaximum(int field)
{
switch (field)
{
case WEEK_OF_YEAR:
return 52;
case DAY_OF_MONTH:
return 28;
case DAY_OF_YEAR:
return 365;
case DAY_OF_WEEK_IN_MONTH:
case WEEK_OF_MONTH:
return 4;
default:
return maximums[field];
}
}
public int getActualMinimum(int field)
{
if (field == WEEK_OF_YEAR)
{
int min = getMinimalDaysInFirstWeek();
if (min == 0)
return 1;
if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR])
complete();
int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
int weekday = getWeekDay(year, min);
if ((7 + weekday - getFirstDayOfWeek()) % 7 >= min - 1)
return 1;
return 0;
}
return minimums[field];
}
public int getActualMaximum(int field)
{
switch (field)
{
case WEEK_OF_YEAR:
{
if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR])
complete();
int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
int lastDay = isLeapYear(year) ? 366 : 365;
int weekday = getWeekDay(year, lastDay);
int week = (lastDay + 6
- (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
int minimalDays = getMinimalDaysInFirstWeek();
int firstWeekday = getWeekDay(year, minimalDays);
if (minimalDays - (7 + firstWeekday - getFirstDayOfWeek()) % 7 < 1)
return week + 1;
}
case DAY_OF_MONTH:
{
if (!areFieldsSet || !isSet[MONTH])
complete();
int month = fields[MONTH];
if (month == FEBRUARY)
{
if (!isSet[YEAR] || !isSet[ERA])
complete();
int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
return isLeapYear(year) ? 29 : 28;
}
else if (month < AUGUST)
return 31 - (month & 1);
else
return 30 + (month & 1);
}
case DAY_OF_YEAR:
{
if (!areFieldsSet || !isSet[ERA] || !isSet[YEAR])
complete();
int year = fields[ERA] == AD ? fields[YEAR] : 1 - fields[YEAR];
return isLeapYear(year) ? 366 : 365;
}
case DAY_OF_WEEK_IN_MONTH:
{
int daysInMonth = getActualMaximum(DAY_OF_MONTH);
return (daysInMonth - (fields[DAY_OF_MONTH] - 1) % 7 + 6) / 7;
}
case WEEK_OF_MONTH:
{
int daysInMonth = getActualMaximum(DAY_OF_MONTH);
int weekday = (daysInMonth - fields[DAY_OF_MONTH]
+ fields[DAY_OF_WEEK] - SUNDAY) % 7 + SUNDAY;
return (daysInMonth + 6
- (7 + weekday - getFirstDayOfWeek()) % 7) / 7;
}
default:
return maximums[field];
}
}
}