FileDocCategorySizeDatePackage
GregorianCalendar.javaAPI DocAndroid 1.5 API52485Wed May 06 22:41:04 BST 2009java.util

GregorianCalendar

public class GregorianCalendar extends Calendar
{@code GregorianCalendar} is a concrete subclass of {@link Calendar} and provides the standard calendar used by most of the world.

The standard (Gregorian) calendar has 2 eras, BC and AD.

This implementation handles a single discontinuity, which corresponds by default to the date the Gregorian calendar was instituted (October 15, 1582 in some countries, later in others). The cutover date may be changed by the caller by calling {@code setGregorianChange()}.

Historically, in those countries which adopted the Gregorian calendar first, October 4, 1582 was thus followed by October 15, 1582. This calendar models this correctly. Before the Gregorian cutover, {@code GregorianCalendar} implements the Julian calendar. The only difference between the Gregorian and the Julian calendar is the leap year rule. The Julian calendar specifies leap years every four years, whereas the Gregorian calendar omits century years which are not divisible by 400.

{@code GregorianCalendar} implements proleptic Gregorian and Julian calendars. That is, dates are computed by extrapolating the current rules indefinitely far backward and forward in time. As a result, {@code GregorianCalendar} may be used for all years to generate meaningful and consistent results. However, dates obtained using {@code GregorianCalendar} are historically accurate only from March 1, 4 AD onward, when modern Julian calendar rules were adopted. Before this date, leap year rules were applied irregularly, and before 45 BC the Julian calendar did not even exist.

Prior to the institution of the Gregorian calendar, New Year's Day was March 25. To avoid confusion, this calendar always uses January 1. A manual adjustment may be made if desired for dates that are prior to the Gregorian changeover and which fall between January 1 and March 24.

Values calculated for the {@code WEEK_OF_YEAR} field range from 1 to 53. Week 1 for a year is the earliest seven day period starting on {@code getFirstDayOfWeek()} that contains at least {@code getMinimalDaysInFirstWeek()} days from that year. It thus depends on the values of {@code getMinimalDaysInFirstWeek()}, {@code getFirstDayOfWeek()}, and the day of the week of January 1. Weeks between week 1 of one year and week 1 of the following year are numbered sequentially from 2 to 52 or 53 (as needed).

For example, January 1, 1998 was a Thursday. If {@code getFirstDayOfWeek()} is {@code MONDAY} and {@code getMinimalDaysInFirstWeek()} is 4 (these are the values reflecting ISO 8601 and many national standards), then week 1 of 1998 starts on December 29, 1997, and ends on January 4, 1998. If, however, {@code getFirstDayOfWeek()} is {@code SUNDAY}, then week 1 of 1998 starts on January 4, 1998, and ends on January 10, 1998; the first three days of 1998 then are part of week 53 of 1997.

Values calculated for the {@code WEEK_OF_MONTH} field range from 0 or 1 to 4 or 5. Week 1 of a month (the days with WEEK_OF_MONTH = 1) is the earliest set of at least {@code getMinimalDaysInFirstWeek()} contiguous days in that month, ending on the day before {@code getFirstDayOfWeek()}. Unlike week 1 of a year, week 1 of a month may be shorter than 7 days, need not start on {@code getFirstDayOfWeek()}, and will not include days of the previous month. Days of a month before week 1 have a {@code WEEK_OF_MONTH} of 0.

For example, if {@code getFirstDayOfWeek()} is {@code SUNDAY} and {@code getMinimalDaysInFirstWeek()} is 4, then the first week of January 1998 is Sunday, January 4 through Saturday, January 10. These days have a {@code WEEK_OF_MONTH} of 1. Thursday, January 1 through Saturday, January 3 have a {@code WEEK_OF_MONTH} of 0. If {@code getMinimalDaysInFirstWeek()} is changed to 3, then January 1 through January 3 have a {@code WEEK_OF_MONTH} of 1.

Example:

// get the supported ids for GMT-08:00 (Pacific Standard Time)
String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000);
// if no ids were returned, something is wrong. get out.
if (ids.length == 0)
System.exit(0);

// begin output
System.out.println("Current Time");

// create a Pacific Standard Time time zone
SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, ids[0]);

// set up rules for daylight savings time
pdt.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);
pdt.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);

// create a GregorianCalendar with the Pacific Daylight time zone
// and the current date and time
Calendar calendar = new GregorianCalendar(pdt);
Date trialTime = new Date();
calendar.setTime(trialTime);

// print out a bunch of interesting things
System.out.println("ERA: " + calendar.get(Calendar.ERA));
System.out.println("YEAR: " + calendar.get(Calendar.YEAR));
System.out.println("MONTH: " + calendar.get(Calendar.MONTH));
System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));
System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));
System.out.println("DATE: " + calendar.get(Calendar.DATE));
System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));
System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));
System.out.println("DAY_OF_WEEK_IN_MONTH: "
+ calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));
System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));
System.out.println("HOUR: " + calendar.get(Calendar.HOUR));
System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));
System.out.println("SECOND: " + calendar.get(Calendar.SECOND));
System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));
System.out.println("ZONE_OFFSET: "
+ (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000)));
System.out.println("DST_OFFSET: "
+ (calendar.get(Calendar.DST_OFFSET)/(60*60*1000)));

System.out.println("Current Time, with hour reset to 3");
calendar.clear(Calendar.HOUR_OF_DAY); // so doesn't override
calendar.set(Calendar.HOUR, 3);
System.out.println("ERA: " + calendar.get(Calendar.ERA));
System.out.println("YEAR: " + calendar.get(Calendar.YEAR));
System.out.println("MONTH: " + calendar.get(Calendar.MONTH));
System.out.println("WEEK_OF_YEAR: " + calendar.get(Calendar.WEEK_OF_YEAR));
System.out.println("WEEK_OF_MONTH: " + calendar.get(Calendar.WEEK_OF_MONTH));
System.out.println("DATE: " + calendar.get(Calendar.DATE));
System.out.println("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
System.out.println("DAY_OF_YEAR: " + calendar.get(Calendar.DAY_OF_YEAR));
System.out.println("DAY_OF_WEEK: " + calendar.get(Calendar.DAY_OF_WEEK));
System.out.println("DAY_OF_WEEK_IN_MONTH: "
+ calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH));
System.out.println("AM_PM: " + calendar.get(Calendar.AM_PM));
System.out.println("HOUR: " + calendar.get(Calendar.HOUR));
System.out.println("HOUR_OF_DAY: " + calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("MINUTE: " + calendar.get(Calendar.MINUTE));
System.out.println("SECOND: " + calendar.get(Calendar.SECOND));
System.out.println("MILLISECOND: " + calendar.get(Calendar.MILLISECOND));
System.out.println("ZONE_OFFSET: "
+ (calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000))); // in hours
System.out.println("DST_OFFSET: "
+ (calendar.get(Calendar.DST_OFFSET)/(60*60*1000))); // in hours
see
Calendar
see
TimeZone
since
Android 1.0

Fields Summary
private static final long
serialVersionUID
public static final int
BC
Value for the BC era.
public static final int
AD
Value for the AD era.
private static final long
defaultGregorianCutover
private long
gregorianCutover
private transient int
changeYear
private transient int
julianSkew
static byte[]
DaysInMonth
private static int[]
DaysInYear
private static int[]
maximums
private static int[]
minimums
private static int[]
leastMaximums
private boolean
isCached
private int[]
cachedFields
private long
nextMidnightMillis
private long
lastMidnightMillis
private int
currentYearSkew
private int
lastYearSkew
Constructors Summary
public GregorianCalendar()
Constructs a new {@code GregorianCalendar} initialized to the current date and time with the default {@code Locale} and {@code TimeZone}.

since
Android 1.0


                                 
      
        this(TimeZone.getDefault(), Locale.getDefault());
    
public GregorianCalendar(int year, int month, int day)
Constructs a new {@code GregorianCalendar} initialized to midnight in the default {@code TimeZone} and {@code Locale} on the specified date.

param
year the year.
param
month the month.
param
day the day of the month.
since
Android 1.0

        super(TimeZone.getDefault(), Locale.getDefault());
        set(year, month, day);
    
public GregorianCalendar(int year, int month, int day, int hour, int minute)
Constructs a new {@code GregorianCalendar} initialized to the specified date and time in the default {@code TimeZone} and {@code Locale}.

param
year the year.
param
month the month.
param
day the day of the month.
param
hour the hour.
param
minute the minute.
since
Android 1.0

        super(TimeZone.getDefault(), Locale.getDefault());
        set(year, month, day, hour, minute);
    
public GregorianCalendar(int year, int month, int day, int hour, int minute, int second)
Constructs a new {@code GregorianCalendar} initialized to the specified date and time in the default {@code TimeZone} and {@code Locale}.

param
year the year.
param
month the month.
param
day the day of the month.
param
hour the hour.
param
minute the minute.
param
second the second.
since
Android 1.0

        super(TimeZone.getDefault(), Locale.getDefault());
        set(year, month, day, hour, minute, second);
    
GregorianCalendar(long milliseconds)

        this(false);
        setTimeInMillis(milliseconds);
    
public GregorianCalendar(Locale locale)
Constructs a new {@code GregorianCalendar} initialized to the current date and time and using the specified {@code Locale} and the default {@code TimeZone}.

param
locale the {@code Locale}.
since
Android 1.0

        this(TimeZone.getDefault(), locale);
    
public GregorianCalendar(TimeZone timezone)
Constructs a new {@code GregorianCalendar} initialized to the current date and time and using the specified {@code TimeZone} and the default {@code Locale}.

param
timezone the {@code TimeZone}.
since
Android 1.0

        this(timezone, Locale.getDefault());
    
public GregorianCalendar(TimeZone timezone, Locale locale)
Constructs a new {@code GregorianCalendar} initialized to the current date and time and using the specified {@code TimeZone} and {@code Locale}.

param
timezone the {@code TimeZone}.
param
locale the {@code Locale}.
since
Android 1.0

        super(timezone, locale);
        setTimeInMillis(System.currentTimeMillis());
    
GregorianCalendar(boolean ignored)

        super(TimeZone.getDefault());
        setFirstDayOfWeek(SUNDAY);
        setMinimalDaysInFirstWeek(1);
    
Methods Summary
public voidadd(int field, int value)
Adds the specified amount to a {@code Calendar} field.

param
field the {@code Calendar} field to modify.
param
value the amount to add to the field.
exception
IllegalArgumentException when the specified field is DST_OFFSET or ZONE_OFFSET.
since
Android 1.0

        if (value == 0) {
            return;
        }
        if (field < 0 || field >= ZONE_OFFSET) {
            throw new IllegalArgumentException();
        }

        isCached = false;

        if (field == ERA) {
            complete();
            if (fields[ERA] == AD) {
                if (value >= 0) {
                    return;
                }
                set(ERA, BC);
            } else {
                if (value <= 0) {
                    return;
                }
                set(ERA, AD);
            }
            complete();
            return;
        }

        if (field == YEAR || field == MONTH) {
            complete();
            if (field == MONTH) {
                int month = fields[MONTH] + value;
                if (month < 0) {
                    value = (month - 11) / 12;
                    month = 12 + (month % 12);
                } else {
                    value = month / 12;
                }
                set(MONTH, month % 12);
            }
            set(YEAR, fields[YEAR] + value);
            int days = daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]);
            if (fields[DATE] > days) {
                set(DATE, days);
            }
            complete();
            return;
        }

        long multiplier = 0;
        getTimeInMillis(); // Update the time
        switch (field) {
            case MILLISECOND:
                time += value;
                break;
            case SECOND:
                time += value * 1000L;
                break;
            case MINUTE:
                time += value * 60000L;
                break;
            case HOUR:
            case HOUR_OF_DAY:
                time += value * 3600000L;
                break;
            case AM_PM:
                multiplier = 43200000L;
                break;
            case DATE:
            case DAY_OF_YEAR:
            case DAY_OF_WEEK:
                multiplier = 86400000L;
                break;
            case WEEK_OF_YEAR:
            case WEEK_OF_MONTH:
            case DAY_OF_WEEK_IN_MONTH:
                multiplier = 604800000L;
                break;
        }
        if (multiplier > 0) {
            int zoneOffset = getTimeZone().getRawOffset();
            int offset = getOffset(time + zoneOffset);
            time += value * multiplier;
            int newOffset = getOffset(time + zoneOffset);
            // Adjust for moving over a DST boundary
            if (newOffset != offset) {
                time += offset - newOffset;
            }
        }
        areFieldsSet = false;
        complete();
    
private final voidcachedFieldsCheckAndGet(long timeVal, long newTimeMillis, long newTimeMillisAdjusted, int millis, int zoneOffset)

        int dstOffset = fields[DST_OFFSET];
        if (!isCached
                || newTimeMillis >= nextMidnightMillis
                || newTimeMillis <= lastMidnightMillis
                || cachedFields[4] != zoneOffset
                || (dstOffset == 0 && (newTimeMillisAdjusted >= nextMidnightMillis))
                || (dstOffset != 0 && (newTimeMillisAdjusted <= lastMidnightMillis))) {
            fullFieldsCalc(timeVal, millis, zoneOffset);
            isCached = false;
        } else {
            fields[YEAR] = cachedFields[0];
            fields[MONTH] = cachedFields[1];
            fields[DATE] = cachedFields[2];
            fields[DAY_OF_WEEK] = cachedFields[3];
            fields[ERA] = cachedFields[5];
            fields[WEEK_OF_YEAR] = cachedFields[6];
            fields[WEEK_OF_MONTH] = cachedFields[7];
            fields[DAY_OF_YEAR] = cachedFields[8];
            fields[DAY_OF_WEEK_IN_MONTH] = cachedFields[9];
        }
    
public java.lang.Objectclone()
Creates new instance of {@code GregorianCalendar} with the same properties.

return
a shallow copy of this {@code GregorianCalendar}.
since
Android 1.0

        GregorianCalendar thisClone = (GregorianCalendar) super.clone();
        thisClone.cachedFields = cachedFields.clone();
        return thisClone;
    
protected voidcomputeFields()

        int zoneOffset = getTimeZone().getRawOffset();

        fields[ZONE_OFFSET] = zoneOffset;

        int millis = (int) (time % 86400000);
        int savedMillis = millis;
        int dstOffset = fields[DST_OFFSET];
        // compute without a change in daylight saving time
        int offset = zoneOffset + dstOffset;
        long newTime = time + offset;

        if (time > 0L && newTime < 0L && offset > 0) {
            newTime = 0x7fffffffffffffffL;
        } else if (time < 0L && newTime > 0L && offset < 0) {
            newTime = 0x8000000000000000L;
        }

        if (isCached) {
            if (millis < 0) {
                millis += 86400000;
            }

            // Cannot add ZONE_OFFSET to time as it might overflow
            millis += zoneOffset;
            millis += dstOffset;

            if (millis < 0) {
                millis += 86400000;
            } else if (millis >= 86400000) {
                millis -= 86400000;
            }

            fields[MILLISECOND] = (millis % 1000);
            millis /= 1000;
            fields[SECOND] = (millis % 60);
            millis /= 60;
            fields[MINUTE] = (millis % 60);
            millis /= 60;
            fields[HOUR_OF_DAY] = (millis % 24);
            millis /= 24;
            fields[AM_PM] = fields[HOUR_OF_DAY] > 11 ? 1 : 0;
            fields[HOUR] = fields[HOUR_OF_DAY] % 12;

            long newTimeAdjusted = newTime;
            if (getTimeZone().useDaylightTime()) {
                // BEGIN android-changed: removed unnecessary cast
                int dstSavings = (/* (SimpleTimeZone) */ getTimeZone())
                        .getDSTSavings();
                // END android-changed
                newTimeAdjusted += (dstOffset == 0) ? dstSavings : -dstSavings;
            }

            if (newTime > 0L && newTimeAdjusted < 0L && dstOffset == 0) {
                newTimeAdjusted = 0x7fffffffffffffffL;
            } else if (newTime < 0L && newTimeAdjusted > 0L && dstOffset != 0) {
                newTimeAdjusted = 0x8000000000000000L;
            }

            cachedFieldsCheckAndGet(time, newTime, newTimeAdjusted,
                    savedMillis, zoneOffset);
        } else {
            fullFieldsCalc(time, savedMillis, zoneOffset);
        }

        for (int i = 0; i < FIELD_COUNT; i++) {
            isSet[i] = true;
        }

        // Caching
        if (!isCached
                && newTime != 0x7fffffffffffffffL
                && newTime != 0x8000000000000000L
                && (!getTimeZone().useDaylightTime() || getTimeZone() instanceof SimpleTimeZone)) {
            int cacheMillis = 0;

            cachedFields[0] = fields[YEAR];
            cachedFields[1] = fields[MONTH];
            cachedFields[2] = fields[DATE];
            cachedFields[3] = fields[DAY_OF_WEEK];
            cachedFields[4] = zoneOffset;
            cachedFields[5] = fields[ERA];
            cachedFields[6] = fields[WEEK_OF_YEAR];
            cachedFields[7] = fields[WEEK_OF_MONTH];
            cachedFields[8] = fields[DAY_OF_YEAR];
            cachedFields[9] = fields[DAY_OF_WEEK_IN_MONTH];

            cacheMillis += (23 - fields[HOUR_OF_DAY]) * 60 * 60 * 1000;
            cacheMillis += (59 - fields[MINUTE]) * 60 * 1000;
            cacheMillis += (59 - fields[SECOND]) * 1000;
            nextMidnightMillis = newTime + cacheMillis;

            cacheMillis = fields[HOUR_OF_DAY] * 60 * 60 * 1000;
            cacheMillis += fields[MINUTE] * 60 * 1000;
            cacheMillis += fields[SECOND] * 1000;
            lastMidnightMillis = newTime - cacheMillis;

            isCached = true;
        }
    
protected voidcomputeTime()

        if (!isLenient()) {
            if (isSet[HOUR_OF_DAY]) {
                if (fields[HOUR_OF_DAY] < 0 || fields[HOUR_OF_DAY] > 23) {
                    throw new IllegalArgumentException();
                }
            } else if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) {
                throw new IllegalArgumentException();
            }
            if (isSet[MINUTE] && (fields[MINUTE] < 0 || fields[MINUTE] > 59)) {
                throw new IllegalArgumentException();
            }
            if (isSet[SECOND] && (fields[SECOND] < 0 || fields[SECOND] > 59)) {
                throw new IllegalArgumentException();
            }
            if (isSet[MILLISECOND]
                    && (fields[MILLISECOND] < 0 || fields[MILLISECOND] > 999)) {
                throw new IllegalArgumentException();
            }
            if (isSet[WEEK_OF_YEAR]
                    && (fields[WEEK_OF_YEAR] < 1 || fields[WEEK_OF_YEAR] > 53)) {
                throw new IllegalArgumentException();
            }
            if (isSet[DAY_OF_WEEK]
                    && (fields[DAY_OF_WEEK] < 1 || fields[DAY_OF_WEEK] > 7)) {
                throw new IllegalArgumentException();
            }
            if (isSet[DAY_OF_WEEK_IN_MONTH]
                    && (fields[DAY_OF_WEEK_IN_MONTH] < 1 || fields[DAY_OF_WEEK_IN_MONTH] > 6)) {
                throw new IllegalArgumentException();
            }
            if (isSet[WEEK_OF_MONTH]
                    && (fields[WEEK_OF_MONTH] < 1 || fields[WEEK_OF_MONTH] > 6)) {
                throw new IllegalArgumentException();
            }
            if (isSet[AM_PM] && fields[AM_PM] != AM && fields[AM_PM] != PM) {
                throw new IllegalArgumentException();
            }
            if (isSet[HOUR] && (fields[HOUR] < 0 || fields[HOUR] > 11)) {
                throw new IllegalArgumentException();
            }
            if (isSet[YEAR]) {
                if (isSet[ERA] && fields[ERA] == BC
                        && (fields[YEAR] < 1 || fields[YEAR] > 292269054)) {
                    throw new IllegalArgumentException();
                } else if (fields[YEAR] < 1 || fields[YEAR] > 292278994) {
                    throw new IllegalArgumentException();
                }
            }
            if (isSet[MONTH] && (fields[MONTH] < 0 || fields[MONTH] > 11)) {
                throw new IllegalArgumentException();
            }
        }

        long timeVal;
        long hour = 0;
        if (isSet[HOUR_OF_DAY] && lastTimeFieldSet != HOUR) {
            hour = fields[HOUR_OF_DAY];
        } else if (isSet[HOUR]) {
            hour = (fields[AM_PM] * 12) + fields[HOUR];
        }
        timeVal = hour * 3600000;

        if (isSet[MINUTE]) {
            timeVal += ((long) fields[MINUTE]) * 60000;
        }
        if (isSet[SECOND]) {
            timeVal += ((long) fields[SECOND]) * 1000;
        }
        if (isSet[MILLISECOND]) {
            timeVal += fields[MILLISECOND];
        }

        long days;
        int year = isSet[YEAR] ? fields[YEAR] : 1970;
        if (isSet[ERA]) {
            // Always test for valid ERA, even if the Calendar is lenient
            if (fields[ERA] != BC && fields[ERA] != AD) {
                throw new IllegalArgumentException();
            }
            if (fields[ERA] == BC) {
                year = 1 - year;
            }
        }

        boolean weekMonthSet = isSet[WEEK_OF_MONTH]
                || isSet[DAY_OF_WEEK_IN_MONTH];
        boolean useMonth = (isSet[DATE] || isSet[MONTH] || weekMonthSet)
                && lastDateFieldSet != DAY_OF_YEAR;
        if (useMonth
                && (lastDateFieldSet == DAY_OF_WEEK || lastDateFieldSet == WEEK_OF_YEAR)) {
            if (isSet[WEEK_OF_YEAR] && isSet[DAY_OF_WEEK]) {
                useMonth = lastDateFieldSet != WEEK_OF_YEAR && weekMonthSet
                        && isSet[DAY_OF_WEEK];
            } else if (isSet[DAY_OF_YEAR]) {
                useMonth = isSet[DATE] && isSet[MONTH];
            }
        }

        if (useMonth) {
            int month = fields[MONTH];
            year += month / 12;
            month %= 12;
            if (month < 0) {
                year--;
                month += 12;
            }
            boolean leapYear = isLeapYear(year);
            days = daysFromBaseYear(year) + daysInYear(leapYear, month);
            boolean useDate = isSet[DATE];
            if (useDate
                    && (lastDateFieldSet == DAY_OF_WEEK
                            || lastDateFieldSet == WEEK_OF_MONTH || lastDateFieldSet == DAY_OF_WEEK_IN_MONTH)) {
                useDate = !(isSet[DAY_OF_WEEK] && weekMonthSet);
            }
            if (useDate) {
                if (!isLenient()
                        && (fields[DATE] < 1 || fields[DATE] > daysInMonth(
                                leapYear, month))) {
                    throw new IllegalArgumentException();
                }
                days += fields[DATE] - 1;
            } else {
                int dayOfWeek;
                if (isSet[DAY_OF_WEEK]) {
                    dayOfWeek = fields[DAY_OF_WEEK] - 1;
                } else {
                    dayOfWeek = getFirstDayOfWeek() - 1;
                }
                if (isSet[WEEK_OF_MONTH]
                        && lastDateFieldSet != DAY_OF_WEEK_IN_MONTH) {
                    int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1));
                    days += (fields[WEEK_OF_MONTH] - 1) * 7
                            + mod7(skew + dayOfWeek - (days - 3)) - skew;
                } else if (isSet[DAY_OF_WEEK_IN_MONTH]) {
                    if (fields[DAY_OF_WEEK_IN_MONTH] >= 0) {
                        days += mod7(dayOfWeek - (days - 3))
                                + (fields[DAY_OF_WEEK_IN_MONTH] - 1) * 7;
                    } else {
                        days += daysInMonth(leapYear, month)
                                + mod7(dayOfWeek
                                        - (days + daysInMonth(leapYear, month) - 3))
                                + fields[DAY_OF_WEEK_IN_MONTH] * 7;
                    }
                }
            }
        } else {
            boolean useWeekYear = isSet[WEEK_OF_YEAR]
                    && lastDateFieldSet != DAY_OF_YEAR;
            if (useWeekYear && isSet[DAY_OF_YEAR]) {
                useWeekYear = isSet[DAY_OF_WEEK];
            }
            days = daysFromBaseYear(year);
            if (useWeekYear) {
                int dayOfWeek;
                if (isSet[DAY_OF_WEEK]) {
                    dayOfWeek = fields[DAY_OF_WEEK] - 1;
                } else {
                    dayOfWeek = getFirstDayOfWeek() - 1;
                }
                int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1));
                days += (fields[WEEK_OF_YEAR] - 1) * 7
                        + mod7(skew + dayOfWeek - (days - 3)) - skew;
                if (7 - skew < getMinimalDaysInFirstWeek()) {
                    days += 7;
                }
            } else if (isSet[DAY_OF_YEAR]) {
                if (!isLenient()
                        && (fields[DAY_OF_YEAR] < 1 || fields[DAY_OF_YEAR] > (365 + (isLeapYear(year) ? 1
                                : 0)))) {
                    throw new IllegalArgumentException();
                }
                days += fields[DAY_OF_YEAR] - 1;
            } else if (isSet[DAY_OF_WEEK]) {
                days += mod7(fields[DAY_OF_WEEK] - 1 - (days - 3));
            }
        }
        lastDateFieldSet = 0;

        timeVal += days * 86400000;
        // Use local time to compare with the gregorian change
        if (year == changeYear
                && timeVal >= gregorianCutover + julianError() * 86400000L) {
            timeVal -= julianError() * 86400000L;
        }

        // It is not possible to simply subtract getOffset(timeVal) from timeVal
        // to get UTC.
        // The trick is needed for the moment when DST transition occurs,
        // say 1:00 is a transition time when DST offset becomes +1 hour,
        // then wall time in the interval 1:00 - 2:00 is invalid and is
        // treated as UTC time.
        long timeValWithoutDST = timeVal - getOffset(timeVal)
                + getTimeZone().getRawOffset();
        timeVal -= getOffset(timeValWithoutDST);
        // Need to update wall time in fields, since it was invalid due to DST
        // transition
        this.time = timeVal;
        if (timeValWithoutDST != timeVal) {
            computeFields();
            areFieldsSet = true;
        }
    
private intcomputeYearAndDay(long dayCount, long localTime)

        int year = 1970;
        long days = dayCount;
        if (localTime < gregorianCutover) {
            days -= julianSkew;
        }
        int approxYears;

        while ((approxYears = (int) (days / 365)) != 0) {
            year = year + approxYears;
            days = dayCount - daysFromBaseYear(year);
        }
        if (days < 0) {
            year = year - 1;
            days = days + daysInYear(year);
        }
        fields[YEAR] = year;
        return (int) days + 1;
    
private longdaysFromBaseYear(int iyear)

        long year = iyear;

        if (year >= 1970) {
            long days = (year - 1970) * 365 + ((year - 1969) / 4);
            if (year > changeYear) {
                days -= ((year - 1901) / 100) - ((year - 1601) / 400);
            } else {
                if(year == changeYear){
                    days += currentYearSkew;
                }else if(year == changeYear -1){
                    days += lastYearSkew;
                }else{
                    days += julianSkew;
                }
            }
            return days;
        } else if (year <= changeYear) {
            return (year - 1970) * 365 + ((year - 1972) / 4) + julianSkew;
        }
        return (year - 1970) * 365 + ((year - 1972) / 4)
                - ((year - 2000) / 100) + ((year - 2000) / 400);
    
private intdaysInMonth()

        return daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]);
    
private intdaysInMonth(boolean leapYear, int month)

        if (leapYear && month == FEBRUARY) {
            return DaysInMonth[month] + 1;
        }

        return DaysInMonth[month];
    
private intdaysInYear(int year)

        int daysInYear = isLeapYear(year) ? 366 : 365;
        if (year == changeYear) {
            daysInYear -= currentYearSkew;
        }
        if (year == changeYear - 1) {
            daysInYear -= lastYearSkew;
        }
        return daysInYear;
    
private intdaysInYear(boolean leapYear, int month)

        if (leapYear && month > FEBRUARY) {
            return DaysInYear[month] + 1;
        }

        return DaysInYear[month];
    
public booleanequals(java.lang.Object object)
Compares the specified {@code Object} to this {@code GregorianCalendar} and returns whether they are equal. To be equal, the {@code Object} must be an instance of {@code GregorianCalendar} and have the same properties.

param
object the {@code Object} to compare with this {@code GregorianCalendar}.
return
{@code true} if {@code object} is equal to this {@code GregorianCalendar}, {@code false} otherwise.
exception
IllegalArgumentException when the time is not set and the time cannot be computed from the current field values.
see
#hashCode
since
Android 1.0

        return super.equals(object)
                && gregorianCutover == ((GregorianCalendar) object).gregorianCutover;
    
private final voidfullFieldsCalc(long timeVal, int millis, int zoneOffset)

        long days = timeVal / 86400000;

        if (millis < 0) {
            millis += 86400000;
            days--;
        }
        // Cannot add ZONE_OFFSET to time as it might overflow
        millis += zoneOffset;
        while (millis < 0) {
            millis += 86400000;
            days--;
        }
        while (millis >= 86400000) {
            millis -= 86400000;
            days++;
        }

        int dayOfYear = computeYearAndDay(days, timeVal + zoneOffset);
        fields[DAY_OF_YEAR] = dayOfYear;
        if(fields[YEAR] == changeYear && gregorianCutover < timeVal + zoneOffset){
            dayOfYear += currentYearSkew;
        }
        int month = dayOfYear / 32;
        boolean leapYear = isLeapYear(fields[YEAR]);
        int date = dayOfYear - daysInYear(leapYear, month);
        if (date > daysInMonth(leapYear, month)) {
            date -= daysInMonth(leapYear, month);
            month++;
        }
        fields[DAY_OF_WEEK] = mod7(days - 3) + 1;
        int dstOffset = fields[YEAR] <= 0 ? 0 : getTimeZone().getOffset(AD,
                fields[YEAR], month, date, fields[DAY_OF_WEEK], millis);
        if (fields[YEAR] > 0) {
            dstOffset -= zoneOffset;
        }
        fields[DST_OFFSET] = dstOffset;
        if (dstOffset != 0) {
            long oldDays = days;
            millis += dstOffset;
            if (millis < 0) {
                millis += 86400000;
                days--;
            } else if (millis >= 86400000) {
                millis -= 86400000;
                days++;
            }
            if (oldDays != days) {
                dayOfYear = computeYearAndDay(days, timeVal - zoneOffset
                        + dstOffset);
                fields[DAY_OF_YEAR] = dayOfYear;
                if(fields[YEAR] == changeYear && gregorianCutover < timeVal - zoneOffset + dstOffset){
                    dayOfYear += currentYearSkew;
                }
                month = dayOfYear / 32;
                leapYear = isLeapYear(fields[YEAR]);
                date = dayOfYear - daysInYear(leapYear, month);
                if (date > daysInMonth(leapYear, month)) {
                    date -= daysInMonth(leapYear, month);
                    month++;
                }
                fields[DAY_OF_WEEK] = mod7(days - 3) + 1;
            }
        }

        fields[MILLISECOND] = (millis % 1000);
        millis /= 1000;
        fields[SECOND] = (millis % 60);
        millis /= 60;
        fields[MINUTE] = (millis % 60);
        millis /= 60;
        fields[HOUR_OF_DAY] = (millis % 24);
        millis /= 24;
        fields[AM_PM] = fields[HOUR_OF_DAY] > 11 ? 1 : 0;
        fields[HOUR] = fields[HOUR_OF_DAY] % 12;

        if (fields[YEAR] <= 0) {
            fields[ERA] = BC;
            fields[YEAR] = -fields[YEAR] + 1;
        } else {
            fields[ERA] = AD;
        }
        fields[MONTH] = month;
        fields[DATE] = date;
        fields[DAY_OF_WEEK_IN_MONTH] = (date - 1) / 7 + 1;
        fields[WEEK_OF_MONTH] = (date - 1 + mod7(days - date - 2
                - (getFirstDayOfWeek() - 1))) / 7 + 1;
        int daysFromStart = mod7(days - 3 - (fields[DAY_OF_YEAR] - 1)
                - (getFirstDayOfWeek() - 1));
        int week = (fields[DAY_OF_YEAR] - 1 + daysFromStart) / 7
                + (7 - daysFromStart >= getMinimalDaysInFirstWeek() ? 1 : 0);
        if (week == 0) {
            fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart
                    - (isLeapYear(fields[YEAR] - 1) ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 53
                    : 52;
        } else if (fields[DAY_OF_YEAR] >= (leapYear ? 367 : 366)
                - mod7(daysFromStart + (leapYear ? 2 : 1))) {
            fields[WEEK_OF_YEAR] = 7 - mod7(daysFromStart + (leapYear ? 2 : 1)) >= getMinimalDaysInFirstWeek() ? 1
                    : week;
        } else {
            fields[WEEK_OF_YEAR] = week;
        }
    
public intgetActualMaximum(int field)
Gets the maximum value of the specified field for the current date. For example, the maximum number of days in the current month.

param
field the field.
return
the maximum value of the specified field.
since
Android 1.0

        int value;
        if ((value = maximums[field]) == leastMaximums[field]) {
            return value;
        }

        switch (field) {
            case WEEK_OF_YEAR:
            case WEEK_OF_MONTH:
                isCached = false;
                break;
        }

        complete();
        long orgTime = time;
        int result = 0;
        switch (field) {
            case WEEK_OF_YEAR:
                set(DATE, 31);
                set(MONTH, DECEMBER);
                result = get(WEEK_OF_YEAR);
                if (result == 1) {
                    set(DATE, 31 - 7);
                    result = get(WEEK_OF_YEAR);
                }
                areFieldsSet = false;
                break;
            case WEEK_OF_MONTH:
                set(DATE, daysInMonth());
                result = get(WEEK_OF_MONTH);
                areFieldsSet = false;
                break;
            case DATE:
                return daysInMonth();
            case DAY_OF_YEAR:
                return daysInYear(fields[YEAR]);
            case DAY_OF_WEEK_IN_MONTH:
                result = get(DAY_OF_WEEK_IN_MONTH)
                        + ((daysInMonth() - get(DATE)) / 7);
                break;
            case YEAR:
                GregorianCalendar clone = (GregorianCalendar) clone();
                if (get(ERA) == AD) {
                    clone.setTimeInMillis(Long.MAX_VALUE);
                } else {
                    clone.setTimeInMillis(Long.MIN_VALUE);
                }
                result = clone.get(YEAR);
                clone.set(YEAR, get(YEAR));
                if (clone.before(this)) {
                    result--;
                }
                break;
            case DST_OFFSET:
                result = getMaximum(DST_OFFSET);
                break;
        }
        time = orgTime;
        return result;
    
public intgetActualMinimum(int field)
Gets the minimum value of the specified field for the current date. For the gregorian calendar, this value is the same as {@code getMinimum()}.

param
field the field.
return
the minimum value of the specified field.
since
Android 1.0

        return getMinimum(field);
    
public intgetGreatestMinimum(int field)
Gets the greatest minimum value of the specified field. For the gregorian calendar, this value is the same as {@code getMinimum()}.

param
field the field.
return
the greatest minimum value of the specified field.
since
Android 1.0

        return minimums[field];
    
public final java.util.DategetGregorianChange()
Returns the gregorian change date of this calendar. This is the date on which the gregorian calendar came into effect.

return
a {@code Date} which represents the gregorian change date.
since
Android 1.0

        return new Date(gregorianCutover);
    
public intgetLeastMaximum(int field)
Gets the smallest maximum value of the specified field. For example, 28 for the day of month field.

param
field the field.
return
the smallest maximum value of the specified field.
since
Android 1.0

        // return value for WEEK_OF_YEAR should make corresponding changes when
        // the gregorian change date have been reset.
        if (gregorianCutover != defaultGregorianCutover
                && field == WEEK_OF_YEAR) {
            long currentTimeInMillis = time;
            setTimeInMillis(gregorianCutover);
            int actual = getActualMaximum(field);
            setTimeInMillis(currentTimeInMillis);
            return actual;
        }
        return leastMaximums[field];
    
public intgetMaximum(int field)
Gets the greatest maximum value of the specified field. For example, 31 for the day of month field.

param
field the field.
return
the greatest maximum value of the specified field.
since
Android 1.0

        return maximums[field];
    
public intgetMinimum(int field)
Gets the smallest minimum value of the specified field.

param
field the field.
return
the smallest minimum value of the specified field.
since
Android 1.0

        return minimums[field];
    
intgetOffset(long localTime)

        TimeZone timeZone = getTimeZone();
        if (!timeZone.useDaylightTime()) {
            return timeZone.getRawOffset();
        }

        long dayCount = localTime / 86400000;
        int millis = (int) (localTime % 86400000);
        if (millis < 0) {
            millis += 86400000;
            dayCount--;
        }

        int year = 1970;
        long days = dayCount;
        if (localTime < gregorianCutover) {
            days -= julianSkew;
        }
        int approxYears;

        while ((approxYears = (int) (days / 365)) != 0) {
            year = year + approxYears;
            days = dayCount - daysFromBaseYear(year);
        }
        if (days < 0) {
            year = year - 1;
            days = days + 365 + (isLeapYear(year) ? 1 : 0);
            if (year == changeYear && localTime < gregorianCutover) {
                days -= julianError();
            }
        }
        if (year <= 0) {
            return timeZone.getRawOffset();
        }
        int dayOfYear = (int) days + 1;

        int month = dayOfYear / 32;
        boolean leapYear = isLeapYear(year);
        int date = dayOfYear - daysInYear(leapYear, month);
        if (date > daysInMonth(leapYear, month)) {
            date -= daysInMonth(leapYear, month);
            month++;
        }
        int dayOfWeek = mod7(dayCount - 3) + 1;
        int offset = timeZone.getOffset(AD, year, month, date, dayOfWeek,
                millis);
        return offset;
    
public inthashCode()
Returns an integer hash code for the receiver. Objects which are equal return the same value for this method.

return
the receiver's hash.
see
#equals
since
Android 1.0

        return super.hashCode()
                + ((int) (gregorianCutover >>> 32) ^ (int) gregorianCutover);
    
public booleanisLeapYear(int year)
Returns whether the specified year is a leap year.

param
year the year.
return
{@code true} if the specified year is a leap year, {@code false} otherwise.
since
Android 1.0

        if (year > changeYear) {
            return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
        }

        return year % 4 == 0;
    
private intjulianError()

        return changeYear / 100 - changeYear / 400 - 2;
    
private intmod(int value, int mod)

        int rem = value % mod;
        if (value < 0 && rem < 0) {
            return rem + mod;
        }
        return rem;
    
private intmod7(long num1)

        int rem = (int) (num1 % 7);
        if (num1 < 0 && rem < 0) {
            return rem + 7;
        }
        return rem;
    
private voidreadObject(java.io.ObjectInputStream stream)


        stream.defaultReadObject();
        setGregorianChange(new Date(gregorianCutover));
        isCached = false;
    
public voidroll(int field, int value)
Adds the specified amount the specified field and wraps the value of the field when it goes beyond the maximum or minimum value for the current date. Other fields will be adjusted as required to maintain a consistent date.

param
field the field to roll.
param
value the amount to add.
exception
IllegalArgumentException when an invalid field is specified.
since
Android 1.0

        if (value == 0) {
            return;
        }
        if (field < 0 || field >= ZONE_OFFSET) {
            throw new IllegalArgumentException();
        }

        isCached = false;

        complete();
        int max = -1;
        switch (field) {
            case YEAR:
                max = maximums[field];
                break;
            case WEEK_OF_YEAR:
            case WEEK_OF_MONTH:
                int days,
                day;
                if (field == WEEK_OF_YEAR) {
                    days = daysInYear(fields[YEAR]);
                    day = DAY_OF_YEAR;
                } else {
                    days = daysInMonth();
                    day = DATE;
                }
                int mod = mod7(fields[DAY_OF_WEEK] - fields[day]
                        - (getFirstDayOfWeek() - 1));
                int maxWeeks = (days - 1 + mod) / 7 + 1;
                int newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1;
                if (newWeek == maxWeeks) {
                    if (fields[day] + (newWeek - fields[field]) * 7 > days) {
                        set(day, days);
                    } else {
                        set(field, newWeek);
                    }
                } else if (newWeek == 1) {
                    int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1;
                    if (week > 1) {
                        set(day, 1);
                    } else {
                        set(field, newWeek);
                    }
                } else {
                    set(field, newWeek);
                }
                break;
            case DATE:
                max = daysInMonth();
                break;
            case DAY_OF_YEAR:
                max = daysInYear(fields[YEAR]);
                break;
            case DAY_OF_WEEK:
                max = maximums[field];
                lastDateFieldSet = WEEK_OF_MONTH;
                break;
            case DAY_OF_WEEK_IN_MONTH:
                max = (fields[DATE] + ((daysInMonth() - fields[DATE]) / 7 * 7) - 1) / 7 + 1;
                break;

            case ERA:
            case MONTH:
            case AM_PM:
            case HOUR:
            case HOUR_OF_DAY:
            case MINUTE:
            case SECOND:
            case MILLISECOND:
                set(field, mod(fields[field] + value, maximums[field] + 1));
                if (field == MONTH && fields[DATE] > daysInMonth()) {
                    set(DATE, daysInMonth());
                } else if (field == AM_PM) {
                    lastTimeFieldSet = HOUR;
                }
                break;
        }
        if (max != -1) {
            set(field, mod(fields[field] - 1 + value, max) + 1);
        }
        complete();
    
public voidroll(int field, boolean increment)
Increments or decrements the specified field and wraps the value of the field when it goes beyond the maximum or minimum value for the current date. Other fields will be adjusted as required to maintain a consistent date. For example, March 31 will roll to April 30 when rolling the month field.

param
field the field to roll.
param
increment {@code true} to increment the field, {@code false} to decrement.
exception
IllegalArgumentException when an invalid field is specified.
since
Android 1.0

        roll(field, increment ? 1 : -1);
    
public voidsetFirstDayOfWeek(int value)

        super.setFirstDayOfWeek(value);
        isCached = false;
    
public voidsetGregorianChange(java.util.Date date)
Sets the gregorian change date of this calendar.

param
date a {@code Date} which represents the gregorian change date.
since
Android 1.0

        gregorianCutover = date.getTime();
        GregorianCalendar cal = new GregorianCalendar(TimeZone.GMT);
        cal.setTime(date);
        changeYear = cal.get(YEAR);
        if (cal.get(ERA) == BC) {
            changeYear = 1 - changeYear;
        }
        julianSkew = ((changeYear - 2000) / 400) + julianError()
                - ((changeYear - 2000) / 100);
        isCached = false;
        int dayOfYear = cal.get(DAY_OF_YEAR);
        if (dayOfYear < julianSkew) {
            currentYearSkew = dayOfYear-1;
            lastYearSkew = julianSkew - dayOfYear + 1;
        } else {
            lastYearSkew = 0;
            currentYearSkew = julianSkew;
        }
        isCached = false;
    
public voidsetMinimalDaysInFirstWeek(int value)

        super.setMinimalDaysInFirstWeek(value);
        isCached = false;
    
private voidwriteObject(java.io.ObjectOutputStream stream)

        stream.defaultWriteObject();