0N/A/*
2362N/A * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.util;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.io.ObjectInputStream;
0N/Aimport sun.util.calendar.BaseCalendar;
0N/Aimport sun.util.calendar.CalendarDate;
0N/Aimport sun.util.calendar.CalendarSystem;
0N/Aimport sun.util.calendar.CalendarUtils;
0N/Aimport sun.util.calendar.Era;
0N/Aimport sun.util.calendar.Gregorian;
0N/Aimport sun.util.calendar.LocalGregorianCalendar;
0N/Aimport sun.util.calendar.ZoneInfo;
0N/Aimport sun.util.resources.LocaleData;
0N/A
0N/A/**
0N/A * <code>JapaneseImperialCalendar</code> implements a Japanese
0N/A * calendar system in which the imperial era-based year numbering is
0N/A * supported from the Meiji era. The following are the eras supported
0N/A * by this calendar system.
0N/A * <pre><tt>
0N/A * ERA value Era name Since (in Gregorian)
0N/A * ------------------------------------------------------
0N/A * 0 N/A N/A
0N/A * 1 Meiji 1868-01-01 midnight local time
0N/A * 2 Taisho 1912-07-30 midnight local time
0N/A * 3 Showa 1926-12-25 midnight local time
0N/A * 4 Heisei 1989-01-08 midnight local time
0N/A * ------------------------------------------------------
0N/A * </tt></pre>
0N/A *
0N/A * <p><code>ERA</code> value 0 specifies the years before Meiji and
0N/A * the Gregorian year values are used. Unlike {@link
0N/A * GregorianCalendar}, the Julian to Gregorian transition is not
0N/A * supported because it doesn't make any sense to the Japanese
0N/A * calendar systems used before Meiji. To represent the years before
0N/A * Gregorian year 1, 0 and negative values are used. The Japanese
0N/A * Imperial rescripts and government decrees don't specify how to deal
0N/A * with time differences for applying the era transitions. This
0N/A * calendar implementation assumes local time for all transitions.
0N/A *
0N/A * @author Masayoshi Okutsu
0N/A * @since 1.6
0N/A */
0N/Aclass JapaneseImperialCalendar extends Calendar {
0N/A /*
0N/A * Implementation Notes
0N/A *
0N/A * This implementation uses
0N/A * sun.util.calendar.LocalGregorianCalendar to perform most of the
0N/A * calendar calculations. LocalGregorianCalendar is configurable
0N/A * and reads <JRE_HOME>/lib/calendars.properties at the start-up.
0N/A */
0N/A
0N/A /**
0N/A * The ERA constant designating the era before Meiji.
0N/A */
0N/A public static final int BEFORE_MEIJI = 0;
0N/A
0N/A /**
0N/A * The ERA constant designating the Meiji era.
0N/A */
0N/A public static final int MEIJI = 1;
0N/A
0N/A /**
0N/A * The ERA constant designating the Taisho era.
0N/A */
0N/A public static final int TAISHO = 2;
0N/A
0N/A /**
0N/A * The ERA constant designating the Showa era.
0N/A */
0N/A public static final int SHOWA = 3;
0N/A
0N/A /**
0N/A * The ERA constant designating the Heisei era.
0N/A */
0N/A public static final int HEISEI = 4;
0N/A
0N/A private static final int EPOCH_OFFSET = 719163; // Fixed date of January 1, 1970 (Gregorian)
0N/A private static final int EPOCH_YEAR = 1970;
0N/A
0N/A // Useful millisecond constants. Although ONE_DAY and ONE_WEEK can fit
0N/A // into ints, they must be longs in order to prevent arithmetic overflow
0N/A // when performing (bug 4173516).
0N/A private static final int ONE_SECOND = 1000;
0N/A private static final int ONE_MINUTE = 60*ONE_SECOND;
0N/A private static final int ONE_HOUR = 60*ONE_MINUTE;
0N/A private static final long ONE_DAY = 24*ONE_HOUR;
0N/A private static final long ONE_WEEK = 7*ONE_DAY;
0N/A
0N/A // Reference to the sun.util.calendar.LocalGregorianCalendar instance (singleton).
0N/A private static final LocalGregorianCalendar jcal
0N/A = (LocalGregorianCalendar) CalendarSystem.forName("japanese");
0N/A
0N/A // Gregorian calendar instance. This is required because era
0N/A // transition dates are given in Gregorian dates.
0N/A private static final Gregorian gcal = CalendarSystem.getGregorianCalendar();
0N/A
0N/A // The Era instance representing "before Meiji".
0N/A private static final Era BEFORE_MEIJI_ERA = new Era("BeforeMeiji", "BM", Long.MIN_VALUE, false);
0N/A
0N/A // Imperial eras. The sun.util.calendar.LocalGregorianCalendar
0N/A // doesn't have an Era representing before Meiji, which is
0N/A // inconvenient for a Calendar. So, era[0] is a reference to
0N/A // BEFORE_MEIJI_ERA.
0N/A private static final Era[] eras;
0N/A
0N/A // Fixed date of the first date of each era.
0N/A private static final long[] sinceFixedDates;
0N/A
0N/A /*
0N/A * <pre>
0N/A * Greatest Least
0N/A * Field name Minimum Minimum Maximum Maximum
0N/A * ---------- ------- ------- ------- -------
0N/A * ERA 0 0 1 1
0N/A * YEAR -292275055 1 ? ?
0N/A * MONTH 0 0 11 11
0N/A * WEEK_OF_YEAR 1 1 52* 53
0N/A * WEEK_OF_MONTH 0 0 4* 6
0N/A * DAY_OF_MONTH 1 1 28* 31
0N/A * DAY_OF_YEAR 1 1 365* 366
0N/A * DAY_OF_WEEK 1 1 7 7
0N/A * DAY_OF_WEEK_IN_MONTH -1 -1 4* 6
0N/A * AM_PM 0 0 1 1
0N/A * HOUR 0 0 11 11
0N/A * HOUR_OF_DAY 0 0 23 23
0N/A * MINUTE 0 0 59 59
0N/A * SECOND 0 0 59 59
0N/A * MILLISECOND 0 0 999 999
0N/A * ZONE_OFFSET -13:00 -13:00 14:00 14:00
0N/A * DST_OFFSET 0:00 0:00 0:20 2:00
0N/A * </pre>
0N/A * *: depends on eras
0N/A */
0N/A static final int MIN_VALUES[] = {
0N/A 0, // ERA
0N/A -292275055, // YEAR
0N/A JANUARY, // MONTH
0N/A 1, // WEEK_OF_YEAR
0N/A 0, // WEEK_OF_MONTH
0N/A 1, // DAY_OF_MONTH
0N/A 1, // DAY_OF_YEAR
0N/A SUNDAY, // DAY_OF_WEEK
0N/A 1, // DAY_OF_WEEK_IN_MONTH
0N/A AM, // AM_PM
0N/A 0, // HOUR
0N/A 0, // HOUR_OF_DAY
0N/A 0, // MINUTE
0N/A 0, // SECOND
0N/A 0, // MILLISECOND
0N/A -13*ONE_HOUR, // ZONE_OFFSET (UNIX compatibility)
0N/A 0 // DST_OFFSET
0N/A };
0N/A static final int LEAST_MAX_VALUES[] = {
0N/A 0, // ERA (initialized later)
0N/A 0, // YEAR (initialized later)
0N/A JANUARY, // MONTH (Showa 64 ended in January.)
0N/A 0, // WEEK_OF_YEAR (Showa 1 has only 6 days which could be 0 weeks.)
0N/A 4, // WEEK_OF_MONTH
0N/A 28, // DAY_OF_MONTH
0N/A 0, // DAY_OF_YEAR (initialized later)
0N/A SATURDAY, // DAY_OF_WEEK
0N/A 4, // DAY_OF_WEEK_IN
0N/A PM, // AM_PM
0N/A 11, // HOUR
0N/A 23, // HOUR_OF_DAY
0N/A 59, // MINUTE
0N/A 59, // SECOND
0N/A 999, // MILLISECOND
0N/A 14*ONE_HOUR, // ZONE_OFFSET
0N/A 20*ONE_MINUTE // DST_OFFSET (historical least maximum)
0N/A };
0N/A static final int MAX_VALUES[] = {
0N/A 0, // ERA
0N/A 292278994, // YEAR
0N/A DECEMBER, // MONTH
0N/A 53, // WEEK_OF_YEAR
0N/A 6, // WEEK_OF_MONTH
0N/A 31, // DAY_OF_MONTH
0N/A 366, // DAY_OF_YEAR
0N/A SATURDAY, // DAY_OF_WEEK
0N/A 6, // DAY_OF_WEEK_IN
0N/A PM, // AM_PM
0N/A 11, // HOUR
0N/A 23, // HOUR_OF_DAY
0N/A 59, // MINUTE
0N/A 59, // SECOND
0N/A 999, // MILLISECOND
0N/A 14*ONE_HOUR, // ZONE_OFFSET
0N/A 2*ONE_HOUR // DST_OFFSET (double summer time)
0N/A };
0N/A
0N/A // Proclaim serialization compatibility with JDK 1.6
0N/A private static final long serialVersionUID = -3364572813905467929L;
0N/A
0N/A static {
0N/A Era[] es = jcal.getEras();
0N/A int length = es.length + 1;
0N/A eras = new Era[length];
0N/A sinceFixedDates = new long[length];
0N/A
0N/A // eras[BEFORE_MEIJI] and sinceFixedDate[BEFORE_MEIJI] are the
0N/A // same as Gregorian.
0N/A int index = BEFORE_MEIJI;
0N/A sinceFixedDates[index] = gcal.getFixedDate(BEFORE_MEIJI_ERA.getSinceDate());
0N/A eras[index++] = BEFORE_MEIJI_ERA;
0N/A for (Era e : es) {
0N/A CalendarDate d = e.getSinceDate();
0N/A sinceFixedDates[index] = gcal.getFixedDate(d);
0N/A eras[index++] = e;
0N/A }
0N/A
0N/A LEAST_MAX_VALUES[ERA] = MAX_VALUES[ERA] = eras.length - 1;
0N/A
0N/A // Calculate the least maximum year and least day of Year
0N/A // values. The following code assumes that there's at most one
0N/A // era transition in a Gregorian year.
0N/A int year = Integer.MAX_VALUE;
0N/A int dayOfYear = Integer.MAX_VALUE;
0N/A CalendarDate date = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
0N/A for (int i = 1; i < eras.length; i++) {
0N/A long fd = sinceFixedDates[i];
0N/A CalendarDate transitionDate = eras[i].getSinceDate();
0N/A date.setDate(transitionDate.getYear(), BaseCalendar.JANUARY, 1);
0N/A long fdd = gcal.getFixedDate(date);
0N/A dayOfYear = Math.min((int)(fdd - fd), dayOfYear);
0N/A date.setDate(transitionDate.getYear(), BaseCalendar.DECEMBER, 31);
0N/A fdd = gcal.getFixedDate(date) + 1;
0N/A dayOfYear = Math.min((int)(fd - fdd), dayOfYear);
0N/A
0N/A LocalGregorianCalendar.Date lgd = getCalendarDate(fd - 1);
0N/A int y = lgd.getYear();
0N/A // Unless the first year starts from January 1, the actual
0N/A // max value could be one year short. For example, if it's
0N/A // Showa 63 January 8, 63 is the actual max value since
0N/A // Showa 64 January 8 doesn't exist.
0N/A if (!(lgd.getMonth() == BaseCalendar.JANUARY && lgd.getDayOfMonth() == 1))
0N/A y--;
0N/A year = Math.min(y, year);
0N/A }
0N/A LEAST_MAX_VALUES[YEAR] = year; // Max year could be smaller than this value.
0N/A LEAST_MAX_VALUES[DAY_OF_YEAR] = dayOfYear;
0N/A }
0N/A
0N/A /**
0N/A * jdate always has a sun.util.calendar.LocalGregorianCalendar.Date instance to
0N/A * avoid overhead of creating it for each calculation.
0N/A */
0N/A private transient LocalGregorianCalendar.Date jdate;
0N/A
0N/A /**
0N/A * Temporary int[2] to get time zone offsets. zoneOffsets[0] gets
0N/A * the GMT offset value and zoneOffsets[1] gets the daylight saving
0N/A * value.
0N/A */
0N/A private transient int[] zoneOffsets;
0N/A
0N/A /**
0N/A * Temporary storage for saving original fields[] values in
0N/A * non-lenient mode.
0N/A */
0N/A private transient int[] originalFields;
0N/A
0N/A /**
0N/A * Constructs a <code>JapaneseImperialCalendar</code> based on the current time
0N/A * in the given time zone with the given locale.
0N/A *
0N/A * @param zone the given time zone.
0N/A * @param aLocale the given locale.
0N/A */
0N/A public JapaneseImperialCalendar(TimeZone zone, Locale aLocale) {
0N/A super(zone, aLocale);
0N/A jdate = jcal.newCalendarDate(zone);
0N/A setTimeInMillis(System.currentTimeMillis());
0N/A }
0N/A
0N/A /**
0N/A * Compares this <code>JapaneseImperialCalendar</code> to the specified
0N/A * <code>Object</code>. The result is <code>true</code> if and
0N/A * only if the argument is a <code>JapaneseImperialCalendar</code> object
0N/A * that represents the same time value (millisecond offset from
0N/A * the <a href="Calendar.html#Epoch">Epoch</a>) under the same
0N/A * <code>Calendar</code> parameters.
0N/A *
0N/A * @param obj the object to compare with.
0N/A * @return <code>true</code> if this object is equal to <code>obj</code>;
0N/A * <code>false</code> otherwise.
0N/A * @see Calendar#compareTo(Calendar)
0N/A */
0N/A public boolean equals(Object obj) {
0N/A return obj instanceof JapaneseImperialCalendar &&
0N/A super.equals(obj);
0N/A }
0N/A
0N/A /**
0N/A * Generates the hash code for this
0N/A * <code>JapaneseImperialCalendar</code> object.
0N/A */
0N/A public int hashCode() {
0N/A return super.hashCode() ^ jdate.hashCode();
0N/A }
0N/A
0N/A /**
0N/A * Adds the specified (signed) amount of time to the given calendar field,
0N/A * based on the calendar's rules.
0N/A *
0N/A * <p><em>Add rule 1</em>. The value of <code>field</code>
0N/A * after the call minus the value of <code>field</code> before the
0N/A * call is <code>amount</code>, modulo any overflow that has occurred in
0N/A * <code>field</code>. Overflow occurs when a field value exceeds its
0N/A * range and, as a result, the next larger field is incremented or
0N/A * decremented and the field value is adjusted back into its range.</p>
0N/A *
0N/A * <p><em>Add rule 2</em>. If a smaller field is expected to be
0N/A * invariant, but it is impossible for it to be equal to its
0N/A * prior value because of changes in its minimum or maximum after
0N/A * <code>field</code> is changed, then its value is adjusted to be as close
0N/A * as possible to its expected value. A smaller field represents a
0N/A * smaller unit of time. <code>HOUR</code> is a smaller field than
0N/A * <code>DAY_OF_MONTH</code>. No adjustment is made to smaller fields
0N/A * that are not expected to be invariant. The calendar system
0N/A * determines what fields are expected to be invariant.</p>
0N/A *
0N/A * @param field the calendar field.
0N/A * @param amount the amount of date or time to be added to the field.
0N/A * @exception IllegalArgumentException if <code>field</code> is
0N/A * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
0N/A * or if any calendar fields have out-of-range values in
0N/A * non-lenient mode.
0N/A */
0N/A public void add(int field, int amount) {
0N/A // If amount == 0, do nothing even the given field is out of
0N/A // range. This is tested by JCK.
0N/A if (amount == 0) {
0N/A return; // Do nothing!
0N/A }
0N/A
0N/A if (field < 0 || field >= ZONE_OFFSET) {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A
0N/A // Sync the time and calendar fields.
0N/A complete();
0N/A
0N/A if (field == YEAR) {
0N/A LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
0N/A d.addYear(amount);
0N/A pinDayOfMonth(d);
0N/A set(ERA, getEraIndex(d));
0N/A set(YEAR, d.getYear());
0N/A set(MONTH, d.getMonth() - 1);
0N/A set(DAY_OF_MONTH, d.getDayOfMonth());
0N/A } else if (field == MONTH) {
0N/A LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
0N/A d.addMonth(amount);
0N/A pinDayOfMonth(d);
0N/A set(ERA, getEraIndex(d));
0N/A set(YEAR, d.getYear());
0N/A set(MONTH, d.getMonth() - 1);
0N/A set(DAY_OF_MONTH, d.getDayOfMonth());
0N/A } else if (field == ERA) {
0N/A int era = internalGet(ERA) + amount;
0N/A if (era < 0) {
0N/A era = 0;
0N/A } else if (era > eras.length - 1) {
0N/A era = eras.length - 1;
0N/A }
0N/A set(ERA, era);
0N/A } else {
0N/A long delta = amount;
0N/A long timeOfDay = 0;
0N/A switch (field) {
0N/A // Handle the time fields here. Convert the given
0N/A // amount to milliseconds and call setTimeInMillis.
0N/A case HOUR:
0N/A case HOUR_OF_DAY:
0N/A delta *= 60 * 60 * 1000; // hours to milliseconds
0N/A break;
0N/A
0N/A case MINUTE:
0N/A delta *= 60 * 1000; // minutes to milliseconds
0N/A break;
0N/A
0N/A case SECOND:
0N/A delta *= 1000; // seconds to milliseconds
0N/A break;
0N/A
0N/A case MILLISECOND:
0N/A break;
0N/A
0N/A // Handle week, day and AM_PM fields which involves
0N/A // time zone offset change adjustment. Convert the
0N/A // given amount to the number of days.
0N/A case WEEK_OF_YEAR:
0N/A case WEEK_OF_MONTH:
0N/A case DAY_OF_WEEK_IN_MONTH:
0N/A delta *= 7;
0N/A break;
0N/A
0N/A case DAY_OF_MONTH: // synonym of DATE
0N/A case DAY_OF_YEAR:
0N/A case DAY_OF_WEEK:
0N/A break;
0N/A
0N/A case AM_PM:
0N/A // Convert the amount to the number of days (delta)
0N/A // and +12 or -12 hours (timeOfDay).
0N/A delta = amount / 2;
0N/A timeOfDay = 12 * (amount % 2);
0N/A break;
0N/A }
0N/A
0N/A // The time fields don't require time zone offset change
0N/A // adjustment.
0N/A if (field >= HOUR) {
0N/A setTimeInMillis(time + delta);
0N/A return;
0N/A }
0N/A
0N/A // The rest of the fields (week, day or AM_PM fields)
0N/A // require time zone offset (both GMT and DST) change
0N/A // adjustment.
0N/A
0N/A // Translate the current time to the fixed date and time
0N/A // of the day.
0N/A long fd = cachedFixedDate;
0N/A timeOfDay += internalGet(HOUR_OF_DAY);
0N/A timeOfDay *= 60;
0N/A timeOfDay += internalGet(MINUTE);
0N/A timeOfDay *= 60;
0N/A timeOfDay += internalGet(SECOND);
0N/A timeOfDay *= 1000;
0N/A timeOfDay += internalGet(MILLISECOND);
0N/A if (timeOfDay >= ONE_DAY) {
0N/A fd++;
0N/A timeOfDay -= ONE_DAY;
0N/A } else if (timeOfDay < 0) {
0N/A fd--;
0N/A timeOfDay += ONE_DAY;
0N/A }
0N/A
0N/A fd += delta; // fd is the expected fixed date after the calculation
0N/A int zoneOffset = internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
0N/A setTimeInMillis((fd - EPOCH_OFFSET) * ONE_DAY + timeOfDay - zoneOffset);
0N/A zoneOffset -= internalGet(ZONE_OFFSET) + internalGet(DST_OFFSET);
0N/A // If the time zone offset has changed, then adjust the difference.
0N/A if (zoneOffset != 0) {
0N/A setTimeInMillis(time + zoneOffset);
0N/A long fd2 = cachedFixedDate;
0N/A // If the adjustment has changed the date, then take
0N/A // the previous one.
0N/A if (fd2 != fd) {
0N/A setTimeInMillis(time - zoneOffset);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A public void roll(int field, boolean up) {
0N/A roll(field, up ? +1 : -1);
0N/A }
0N/A
0N/A /**
0N/A * Adds a signed amount to the specified calendar field without changing larger fields.
0N/A * A negative roll amount means to subtract from field without changing
0N/A * larger fields. If the specified amount is 0, this method performs nothing.
0N/A *
0N/A * <p>This method calls {@link #complete()} before adding the
0N/A * amount so that all the calendar fields are normalized. If there
0N/A * is any calendar field having an out-of-range value in non-lenient mode, then an
0N/A * <code>IllegalArgumentException</code> is thrown.
0N/A *
0N/A * @param field the calendar field.
0N/A * @param amount the signed amount to add to <code>field</code>.
0N/A * @exception IllegalArgumentException if <code>field</code> is
0N/A * <code>ZONE_OFFSET</code>, <code>DST_OFFSET</code>, or unknown,
0N/A * or if any calendar fields have out-of-range values in
0N/A * non-lenient mode.
0N/A * @see #roll(int,boolean)
0N/A * @see #add(int,int)
0N/A * @see #set(int,int)
0N/A */
0N/A public void roll(int field, int amount) {
0N/A // If amount == 0, do nothing even the given field is out of
0N/A // range. This is tested by JCK.
0N/A if (amount == 0) {
0N/A return;
0N/A }
0N/A
0N/A if (field < 0 || field >= ZONE_OFFSET) {
0N/A throw new IllegalArgumentException();
0N/A }
0N/A
0N/A // Sync the time and calendar fields.
0N/A complete();
0N/A
0N/A int min = getMinimum(field);
0N/A int max = getMaximum(field);
0N/A
0N/A switch (field) {
0N/A case ERA:
0N/A case AM_PM:
0N/A case MINUTE:
0N/A case SECOND:
0N/A case MILLISECOND:
0N/A // These fields are handled simply, since they have fixed
0N/A // minima and maxima. Other fields are complicated, since
0N/A // the range within they must roll varies depending on the
0N/A // date, a time zone and the era transitions.
0N/A break;
0N/A
0N/A case HOUR:
0N/A case HOUR_OF_DAY:
0N/A {
0N/A int unit = max + 1; // 12 or 24 hours
0N/A int h = internalGet(field);
0N/A int nh = (h + amount) % unit;
0N/A if (nh < 0) {
0N/A nh += unit;
0N/A }
0N/A time += ONE_HOUR * (nh - h);
0N/A
0N/A // The day might have changed, which could happen if
0N/A // the daylight saving time transition brings it to
0N/A // the next day, although it's very unlikely. But we
0N/A // have to make sure not to change the larger fields.
0N/A CalendarDate d = jcal.getCalendarDate(time, getZone());
0N/A if (internalGet(DAY_OF_MONTH) != d.getDayOfMonth()) {
0N/A d.setEra(jdate.getEra());
0N/A d.setDate(internalGet(YEAR),
0N/A internalGet(MONTH) + 1,
0N/A internalGet(DAY_OF_MONTH));
0N/A if (field == HOUR) {
0N/A assert (internalGet(AM_PM) == PM);
0N/A d.addHours(+12); // restore PM
0N/A }
0N/A time = jcal.getTime(d);
0N/A }
0N/A int hourOfDay = d.getHours();
0N/A internalSet(field, hourOfDay % unit);
0N/A if (field == HOUR) {
0N/A internalSet(HOUR_OF_DAY, hourOfDay);
0N/A } else {
0N/A internalSet(AM_PM, hourOfDay / 12);
0N/A internalSet(HOUR, hourOfDay % 12);
0N/A }
0N/A
0N/A // Time zone offset and/or daylight saving might have changed.
0N/A int zoneOffset = d.getZoneOffset();
0N/A int saving = d.getDaylightSaving();
0N/A internalSet(ZONE_OFFSET, zoneOffset - saving);
0N/A internalSet(DST_OFFSET, saving);
0N/A return;
0N/A }
0N/A
0N/A case YEAR:
0N/A min = getActualMinimum(field);
0N/A max = getActualMaximum(field);
0N/A break;
0N/A
0N/A case MONTH:
0N/A // Rolling the month involves both pinning the final value to [0, 11]
0N/A // and adjusting the DAY_OF_MONTH if necessary. We only adjust the
0N/A // DAY_OF_MONTH if, after updating the MONTH field, it is illegal.
0N/A // E.g., <jan31>.roll(MONTH, 1) -> <feb28> or <feb29>.
0N/A {
0N/A if (!isTransitionYear(jdate.getNormalizedYear())) {
0N/A int year = jdate.getYear();
0N/A if (year == getMaximum(YEAR)) {
0N/A CalendarDate jd = jcal.getCalendarDate(time, getZone());
0N/A CalendarDate d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
0N/A max = d.getMonth() - 1;
0N/A int n = getRolledValue(internalGet(field), amount, min, max);
0N/A if (n == max) {
0N/A // To avoid overflow, use an equivalent year.
0N/A jd.addYear(-400);
0N/A jd.setMonth(n + 1);
0N/A if (jd.getDayOfMonth() > d.getDayOfMonth()) {
0N/A jd.setDayOfMonth(d.getDayOfMonth());
0N/A jcal.normalize(jd);
0N/A }
0N/A if (jd.getDayOfMonth() == d.getDayOfMonth()
0N/A && jd.getTimeOfDay() > d.getTimeOfDay()) {
0N/A jd.setMonth(n + 1);
0N/A jd.setDayOfMonth(d.getDayOfMonth() - 1);
0N/A jcal.normalize(jd);
0N/A // Month may have changed by the normalization.
0N/A n = jd.getMonth() - 1;
0N/A }
0N/A set(DAY_OF_MONTH, jd.getDayOfMonth());
0N/A }
0N/A set(MONTH, n);
0N/A } else if (year == getMinimum(YEAR)) {
0N/A CalendarDate jd = jcal.getCalendarDate(time, getZone());
0N/A CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
0N/A min = d.getMonth() - 1;
0N/A int n = getRolledValue(internalGet(field), amount, min, max);
0N/A if (n == min) {
0N/A // To avoid underflow, use an equivalent year.
0N/A jd.addYear(+400);
0N/A jd.setMonth(n + 1);
0N/A if (jd.getDayOfMonth() < d.getDayOfMonth()) {
0N/A jd.setDayOfMonth(d.getDayOfMonth());
0N/A jcal.normalize(jd);
0N/A }
0N/A if (jd.getDayOfMonth() == d.getDayOfMonth()
0N/A && jd.getTimeOfDay() < d.getTimeOfDay()) {
0N/A jd.setMonth(n + 1);
0N/A jd.setDayOfMonth(d.getDayOfMonth() + 1);
0N/A jcal.normalize(jd);
0N/A // Month may have changed by the normalization.
0N/A n = jd.getMonth() - 1;
0N/A }
0N/A set(DAY_OF_MONTH, jd.getDayOfMonth());
0N/A }
0N/A set(MONTH, n);
0N/A } else {
0N/A int mon = (internalGet(MONTH) + amount) % 12;
0N/A if (mon < 0) {
0N/A mon += 12;
0N/A }
0N/A set(MONTH, mon);
0N/A
0N/A // Keep the day of month in the range. We
0N/A // don't want to spill over into the next
0N/A // month; e.g., we don't want jan31 + 1 mo ->
0N/A // feb31 -> mar3.
0N/A int monthLen = monthLength(mon);
0N/A if (internalGet(DAY_OF_MONTH) > monthLen) {
0N/A set(DAY_OF_MONTH, monthLen);
0N/A }
0N/A }
0N/A } else {
0N/A int eraIndex = getEraIndex(jdate);
0N/A CalendarDate transition = null;
0N/A if (jdate.getYear() == 1) {
0N/A transition = eras[eraIndex].getSinceDate();
0N/A min = transition.getMonth() - 1;
0N/A } else {
0N/A if (eraIndex < eras.length - 1) {
0N/A transition = eras[eraIndex + 1].getSinceDate();
0N/A if (transition.getYear() == jdate.getNormalizedYear()) {
0N/A max = transition.getMonth() - 1;
0N/A if (transition.getDayOfMonth() == 1) {
0N/A max--;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (min == max) {
0N/A // The year has only one month. No need to
0N/A // process further. (Showa Gan-nen (year 1)
0N/A // and the last year have only one month.)
0N/A return;
0N/A }
0N/A int n = getRolledValue(internalGet(field), amount, min, max);
0N/A set(MONTH, n);
0N/A if (n == min) {
0N/A if (!(transition.getMonth() == BaseCalendar.JANUARY
0N/A && transition.getDayOfMonth() == 1)) {
0N/A if (jdate.getDayOfMonth() < transition.getDayOfMonth()) {
0N/A set(DAY_OF_MONTH, transition.getDayOfMonth());
0N/A }
0N/A }
0N/A } else if (n == max && (transition.getMonth() - 1 == n)) {
0N/A int dom = transition.getDayOfMonth();
0N/A if (jdate.getDayOfMonth() >= dom) {
0N/A set(DAY_OF_MONTH, dom - 1);
0N/A }
0N/A }
0N/A }
0N/A return;
0N/A }
0N/A
0N/A case WEEK_OF_YEAR:
0N/A {
0N/A int y = jdate.getNormalizedYear();
0N/A max = getActualMaximum(WEEK_OF_YEAR);
0N/A set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK)); // update stamp[field]
0N/A int woy = internalGet(WEEK_OF_YEAR);
0N/A int value = woy + amount;
0N/A if (!isTransitionYear(jdate.getNormalizedYear())) {
0N/A int year = jdate.getYear();
0N/A if (year == getMaximum(YEAR)) {
0N/A max = getActualMaximum(WEEK_OF_YEAR);
0N/A } else if (year == getMinimum(YEAR)) {
0N/A min = getActualMinimum(WEEK_OF_YEAR);
0N/A max = getActualMaximum(WEEK_OF_YEAR);
0N/A if (value > min && value < max) {
0N/A set(WEEK_OF_YEAR, value);
0N/A return;
0N/A }
0N/A
0N/A }
0N/A // If the new value is in between min and max
0N/A // (exclusive), then we can use the value.
0N/A if (value > min && value < max) {
0N/A set(WEEK_OF_YEAR, value);
0N/A return;
0N/A }
0N/A long fd = cachedFixedDate;
0N/A // Make sure that the min week has the current DAY_OF_WEEK
0N/A long day1 = fd - (7 * (woy - min));
0N/A if (year != getMinimum(YEAR)) {
0N/A if (gcal.getYearFromFixedDate(day1) != y) {
0N/A min++;
0N/A }
0N/A } else {
0N/A CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
0N/A if (day1 < jcal.getFixedDate(d)) {
0N/A min++;
0N/A }
0N/A }
0N/A
0N/A // Make sure the same thing for the max week
0N/A fd += 7 * (max - internalGet(WEEK_OF_YEAR));
0N/A if (gcal.getYearFromFixedDate(fd) != y) {
0N/A max--;
0N/A }
0N/A break;
0N/A }
0N/A
0N/A // Handle transition here.
0N/A long fd = cachedFixedDate;
0N/A long day1 = fd - (7 * (woy - min));
0N/A // Make sure that the min week has the current DAY_OF_WEEK
0N/A LocalGregorianCalendar.Date d = getCalendarDate(day1);
0N/A if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate.getYear())) {
0N/A min++;
0N/A }
0N/A
0N/A // Make sure the same thing for the max week
0N/A fd += 7 * (max - woy);
0N/A jcal.getCalendarDateFromFixedDate(d, fd);
0N/A if (!(d.getEra() == jdate.getEra() && d.getYear() == jdate.getYear())) {
0N/A max--;
0N/A }
0N/A // value: the new WEEK_OF_YEAR which must be converted
0N/A // to month and day of month.
0N/A value = getRolledValue(woy, amount, min, max) - 1;
0N/A d = getCalendarDate(day1 + value * 7);
0N/A set(MONTH, d.getMonth() - 1);
0N/A set(DAY_OF_MONTH, d.getDayOfMonth());
0N/A return;
0N/A }
0N/A
0N/A case WEEK_OF_MONTH:
0N/A {
0N/A boolean isTransitionYear = isTransitionYear(jdate.getNormalizedYear());
0N/A // dow: relative day of week from the first day of week
0N/A int dow = internalGet(DAY_OF_WEEK) - getFirstDayOfWeek();
0N/A if (dow < 0) {
0N/A dow += 7;
0N/A }
0N/A
0N/A long fd = cachedFixedDate;
0N/A long month1; // fixed date of the first day (usually 1) of the month
0N/A int monthLength; // actual month length
0N/A if (isTransitionYear) {
0N/A month1 = getFixedDateMonth1(jdate, fd);
0N/A monthLength = actualMonthLength();
0N/A } else {
0N/A month1 = fd - internalGet(DAY_OF_MONTH) + 1;
0N/A monthLength = jcal.getMonthLength(jdate);
0N/A }
0N/A
0N/A // the first day of week of the month.
0N/A long monthDay1st = jcal.getDayOfWeekDateOnOrBefore(month1 + 6,
0N/A getFirstDayOfWeek());
0N/A // if the week has enough days to form a week, the
0N/A // week starts from the previous month.
0N/A if ((int)(monthDay1st - month1) >= getMinimalDaysInFirstWeek()) {
0N/A monthDay1st -= 7;
0N/A }
0N/A max = getActualMaximum(field);
0N/A
0N/A // value: the new WEEK_OF_MONTH value
0N/A int value = getRolledValue(internalGet(field), amount, 1, max) - 1;
0N/A
0N/A // nfd: fixed date of the rolled date
0N/A long nfd = monthDay1st + value * 7 + dow;
0N/A
0N/A // Unlike WEEK_OF_YEAR, we need to change day of week if the
0N/A // nfd is out of the month.
0N/A if (nfd < month1) {
0N/A nfd = month1;
0N/A } else if (nfd >= (month1 + monthLength)) {
0N/A nfd = month1 + monthLength - 1;
0N/A }
0N/A set(DAY_OF_MONTH, (int)(nfd - month1) + 1);
0N/A return;
0N/A }
0N/A
0N/A case DAY_OF_MONTH:
0N/A {
0N/A if (!isTransitionYear(jdate.getNormalizedYear())) {
0N/A max = jcal.getMonthLength(jdate);
0N/A break;
0N/A }
0N/A
0N/A // TODO: Need to change the spec to be usable DAY_OF_MONTH rolling...
0N/A
0N/A // Transition handling. We can't change year and era
0N/A // values here due to the Calendar roll spec!
0N/A long month1 = getFixedDateMonth1(jdate, cachedFixedDate);
0N/A
0N/A // It may not be a regular month. Convert the date and range to
0N/A // the relative values, perform the roll, and
0N/A // convert the result back to the rolled date.
0N/A int value = getRolledValue((int)(cachedFixedDate - month1), amount,
0N/A 0, actualMonthLength() - 1);
0N/A LocalGregorianCalendar.Date d = getCalendarDate(month1 + value);
0N/A assert getEraIndex(d) == internalGetEra()
0N/A && d.getYear() == internalGet(YEAR) && d.getMonth()-1 == internalGet(MONTH);
0N/A set(DAY_OF_MONTH, d.getDayOfMonth());
0N/A return;
0N/A }
0N/A
0N/A case DAY_OF_YEAR:
0N/A {
0N/A max = getActualMaximum(field);
0N/A if (!isTransitionYear(jdate.getNormalizedYear())) {
0N/A break;
0N/A }
0N/A
0N/A // Handle transition. We can't change year and era values
0N/A // here due to the Calendar roll spec.
0N/A int value = getRolledValue(internalGet(DAY_OF_YEAR), amount, min, max);
0N/A long jan0 = cachedFixedDate - internalGet(DAY_OF_YEAR);
0N/A LocalGregorianCalendar.Date d = getCalendarDate(jan0 + value);
0N/A assert getEraIndex(d) == internalGetEra() && d.getYear() == internalGet(YEAR);
0N/A set(MONTH, d.getMonth() - 1);
0N/A set(DAY_OF_MONTH, d.getDayOfMonth());
0N/A return;
0N/A }
0N/A
0N/A case DAY_OF_WEEK:
0N/A {
0N/A int normalizedYear = jdate.getNormalizedYear();
0N/A if (!isTransitionYear(normalizedYear) && !isTransitionYear(normalizedYear - 1)) {
0N/A // If the week of year is in the same year, we can
0N/A // just change DAY_OF_WEEK.
0N/A int weekOfYear = internalGet(WEEK_OF_YEAR);
0N/A if (weekOfYear > 1 && weekOfYear < 52) {
0N/A set(WEEK_OF_YEAR, internalGet(WEEK_OF_YEAR));
0N/A max = SATURDAY;
0N/A break;
0N/A }
0N/A }
0N/A
0N/A // We need to handle it in a different way around year
0N/A // boundaries and in the transition year. Note that
0N/A // changing era and year values violates the roll
0N/A // rule: not changing larger calendar fields...
0N/A amount %= 7;
0N/A if (amount == 0) {
0N/A return;
0N/A }
0N/A long fd = cachedFixedDate;
0N/A long dowFirst = jcal.getDayOfWeekDateOnOrBefore(fd, getFirstDayOfWeek());
0N/A fd += amount;
0N/A if (fd < dowFirst) {
0N/A fd += 7;
0N/A } else if (fd >= dowFirst + 7) {
0N/A fd -= 7;
0N/A }
0N/A LocalGregorianCalendar.Date d = getCalendarDate(fd);
0N/A set(ERA, getEraIndex(d));
0N/A set(d.getYear(), d.getMonth() - 1, d.getDayOfMonth());
0N/A return;
0N/A }
0N/A
0N/A case DAY_OF_WEEK_IN_MONTH:
0N/A {
0N/A min = 1; // after having normalized, min should be 1.
0N/A if (!isTransitionYear(jdate.getNormalizedYear())) {
0N/A int dom = internalGet(DAY_OF_MONTH);
0N/A int monthLength = jcal.getMonthLength(jdate);
0N/A int lastDays = monthLength % 7;
0N/A max = monthLength / 7;
0N/A int x = (dom - 1) % 7;
0N/A if (x < lastDays) {
0N/A max++;
0N/A }
0N/A set(DAY_OF_WEEK, internalGet(DAY_OF_WEEK));
0N/A break;
0N/A }
0N/A
0N/A // Transition year handling.
0N/A long fd = cachedFixedDate;
0N/A long month1 = getFixedDateMonth1(jdate, fd);
0N/A int monthLength = actualMonthLength();
0N/A int lastDays = monthLength % 7;
0N/A max = monthLength / 7;
0N/A int x = (int)(fd - month1) % 7;
0N/A if (x < lastDays) {
0N/A max++;
0N/A }
0N/A int value = getRolledValue(internalGet(field), amount, min, max) - 1;
0N/A fd = month1 + value * 7 + x;
0N/A LocalGregorianCalendar.Date d = getCalendarDate(fd);
0N/A set(DAY_OF_MONTH, d.getDayOfMonth());
0N/A return;
0N/A }
0N/A }
0N/A
0N/A set(field, getRolledValue(internalGet(field), amount, min, max));
0N/A }
0N/A
0N/A public String getDisplayName(int field, int style, Locale locale) {
0N/A if (!checkDisplayNameParams(field, style, SHORT, LONG, locale,
0N/A ERA_MASK|YEAR_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
0N/A return null;
0N/A }
0N/A
0N/A // "GanNen" is supported only in the LONG style.
0N/A if (field == YEAR
0N/A && (style == SHORT || get(YEAR) != 1 || get(ERA) == 0)) {
0N/A return null;
0N/A }
0N/A
0N/A ResourceBundle rb = LocaleData.getDateFormatData(locale);
0N/A String name = null;
0N/A String key = getKey(field, style);
0N/A if (key != null) {
0N/A String[] strings = rb.getStringArray(key);
0N/A if (field == YEAR) {
0N/A if (strings.length > 0) {
0N/A name = strings[0];
0N/A }
0N/A } else {
0N/A int index = get(field);
0N/A // If the ERA value is out of range for strings, then
0N/A // try to get its name or abbreviation from the Era instance.
0N/A if (field == ERA && index >= strings.length && index < eras.length) {
0N/A Era era = eras[index];
0N/A name = (style == SHORT) ? era.getAbbreviation() : era.getName();
0N/A } else {
0N/A if (field == DAY_OF_WEEK)
0N/A --index;
0N/A name = strings[index];
0N/A }
0N/A }
0N/A }
0N/A return name;
0N/A }
0N/A
0N/A public Map<String,Integer> getDisplayNames(int field, int style, Locale locale) {
0N/A if (!checkDisplayNameParams(field, style, ALL_STYLES, LONG, locale,
0N/A ERA_MASK|YEAR_MASK|MONTH_MASK|DAY_OF_WEEK_MASK|AM_PM_MASK)) {
0N/A return null;
0N/A }
0N/A
0N/A if (style == ALL_STYLES) {
0N/A Map<String,Integer> shortNames = getDisplayNamesImpl(field, SHORT, locale);
0N/A if (field == AM_PM) {
0N/A return shortNames;
0N/A }
0N/A Map<String,Integer> longNames = getDisplayNamesImpl(field, LONG, locale);
0N/A if (shortNames == null) {
0N/A return longNames;
0N/A }
0N/A if (longNames != null) {
0N/A shortNames.putAll(longNames);
0N/A }
0N/A return shortNames;
0N/A }
0N/A
0N/A // SHORT or LONG
0N/A return getDisplayNamesImpl(field, style, locale);
0N/A }
0N/A
0N/A private Map<String,Integer> getDisplayNamesImpl(int field, int style, Locale locale) {
0N/A ResourceBundle rb = LocaleData.getDateFormatData(locale);
0N/A String key = getKey(field, style);
0N/A Map<String,Integer> map = new HashMap<String,Integer>();
0N/A if (key != null) {
0N/A String[] strings = rb.getStringArray(key);
0N/A if (field == YEAR) {
0N/A if (strings.length > 0) {
0N/A map.put(strings[0], 1);
0N/A }
0N/A } else {
0N/A int base = (field == DAY_OF_WEEK) ? 1 : 0;
0N/A for (int i = 0; i < strings.length; i++) {
0N/A map.put(strings[i], base + i);
0N/A }
0N/A // If strings[] has fewer than eras[], get more names from eras[].
0N/A if (field == ERA && strings.length < eras.length) {
0N/A for (int i = strings.length; i < eras.length; i++) {
0N/A Era era = eras[i];
0N/A String name = (style == SHORT) ? era.getAbbreviation() : era.getName();
0N/A map.put(name, i);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A return map.size() > 0 ? map : null;
0N/A }
0N/A
0N/A private String getKey(int field, int style) {
0N/A String className = JapaneseImperialCalendar.class.getName();
0N/A StringBuilder key = new StringBuilder();
0N/A switch (field) {
0N/A case ERA:
0N/A key.append(className);
0N/A if (style == SHORT) {
0N/A key.append(".short");
0N/A }
0N/A key.append(".Eras");
0N/A break;
0N/A
0N/A case YEAR:
0N/A key.append(className).append(".FirstYear");
0N/A break;
0N/A
0N/A case MONTH:
0N/A key.append(style == SHORT ? "MonthAbbreviations" : "MonthNames");
0N/A break;
0N/A
0N/A case DAY_OF_WEEK:
0N/A key.append(style == SHORT ? "DayAbbreviations" : "DayNames");
0N/A break;
0N/A
0N/A case AM_PM:
0N/A key.append("AmPmMarkers");
0N/A break;
0N/A }
0N/A return key.length() > 0 ? key.toString() : null;
0N/A }
0N/A
0N/A /**
0N/A * Returns the minimum value for the given calendar field of this
0N/A * <code>Calendar</code> instance. The minimum value is
0N/A * defined as the smallest value returned by the {@link
0N/A * Calendar#get(int) get} method for any possible time value,
0N/A * taking into consideration the current values of the
0N/A * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
0N/A * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
0N/A * and {@link Calendar#getTimeZone() getTimeZone} methods.
0N/A *
0N/A * @param field the calendar field.
0N/A * @return the minimum value for the given calendar field.
0N/A * @see #getMaximum(int)
0N/A * @see #getGreatestMinimum(int)
0N/A * @see #getLeastMaximum(int)
0N/A * @see #getActualMinimum(int)
0N/A * @see #getActualMaximum(int)
0N/A */
0N/A public int getMinimum(int field) {
0N/A return MIN_VALUES[field];
0N/A }
0N/A
0N/A /**
0N/A * Returns the maximum value for the given calendar field of this
0N/A * <code>GregorianCalendar</code> instance. The maximum value is
0N/A * defined as the largest value returned by the {@link
0N/A * Calendar#get(int) get} method for any possible time value,
0N/A * taking into consideration the current values of the
0N/A * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
0N/A * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
0N/A * and {@link Calendar#getTimeZone() getTimeZone} methods.
0N/A *
0N/A * @param field the calendar field.
0N/A * @return the maximum value for the given calendar field.
0N/A * @see #getMinimum(int)
0N/A * @see #getGreatestMinimum(int)
0N/A * @see #getLeastMaximum(int)
0N/A * @see #getActualMinimum(int)
0N/A * @see #getActualMaximum(int)
0N/A */
0N/A public int getMaximum(int field) {
0N/A switch (field) {
0N/A case YEAR:
0N/A {
0N/A // The value should depend on the time zone of this calendar.
0N/A LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
0N/A getZone());
0N/A return Math.max(LEAST_MAX_VALUES[YEAR], d.getYear());
0N/A }
0N/A }
0N/A return MAX_VALUES[field];
0N/A }
0N/A
0N/A /**
0N/A * Returns the highest minimum value for the given calendar field
0N/A * of this <code>GregorianCalendar</code> instance. The highest
0N/A * minimum value is defined as the largest value returned by
0N/A * {@link #getActualMinimum(int)} for any possible time value,
0N/A * taking into consideration the current values of the
0N/A * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
0N/A * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
0N/A * and {@link Calendar#getTimeZone() getTimeZone} methods.
0N/A *
0N/A * @param field the calendar field.
0N/A * @return the highest minimum value for the given calendar field.
0N/A * @see #getMinimum(int)
0N/A * @see #getMaximum(int)
0N/A * @see #getLeastMaximum(int)
0N/A * @see #getActualMinimum(int)
0N/A * @see #getActualMaximum(int)
0N/A */
0N/A public int getGreatestMinimum(int field) {
0N/A return field == YEAR ? 1 : MIN_VALUES[field];
0N/A }
0N/A
0N/A /**
0N/A * Returns the lowest maximum value for the given calendar field
0N/A * of this <code>GregorianCalendar</code> instance. The lowest
0N/A * maximum value is defined as the smallest value returned by
0N/A * {@link #getActualMaximum(int)} for any possible time value,
0N/A * taking into consideration the current values of the
0N/A * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
0N/A * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
0N/A * and {@link Calendar#getTimeZone() getTimeZone} methods.
0N/A *
0N/A * @param field the calendar field
0N/A * @return the lowest maximum value for the given calendar field.
0N/A * @see #getMinimum(int)
0N/A * @see #getMaximum(int)
0N/A * @see #getGreatestMinimum(int)
0N/A * @see #getActualMinimum(int)
0N/A * @see #getActualMaximum(int)
0N/A */
0N/A public int getLeastMaximum(int field) {
0N/A switch (field) {
0N/A case YEAR:
0N/A {
0N/A return Math.min(LEAST_MAX_VALUES[YEAR], getMaximum(YEAR));
0N/A }
0N/A }
0N/A return LEAST_MAX_VALUES[field];
0N/A }
0N/A
0N/A /**
0N/A * Returns the minimum value that this calendar field could have,
0N/A * taking into consideration the given time value and the current
0N/A * values of the
0N/A * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
0N/A * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
0N/A * and {@link Calendar#getTimeZone() getTimeZone} methods.
0N/A *
0N/A * @param field the calendar field
0N/A * @return the minimum of the given field for the time value of
0N/A * this <code>JapaneseImperialCalendar</code>
0N/A * @see #getMinimum(int)
0N/A * @see #getMaximum(int)
0N/A * @see #getGreatestMinimum(int)
0N/A * @see #getLeastMaximum(int)
0N/A * @see #getActualMaximum(int)
0N/A */
0N/A public int getActualMinimum(int field) {
0N/A if (!isFieldSet(YEAR_MASK|MONTH_MASK|WEEK_OF_YEAR_MASK, field)) {
0N/A return getMinimum(field);
0N/A }
0N/A
0N/A int value = 0;
0N/A JapaneseImperialCalendar jc = getNormalizedCalendar();
0N/A // Get a local date which includes time of day and time zone,
0N/A // which are missing in jc.jdate.
0N/A LocalGregorianCalendar.Date jd = jcal.getCalendarDate(jc.getTimeInMillis(),
0N/A getZone());
0N/A int eraIndex = getEraIndex(jd);
0N/A switch (field) {
0N/A case YEAR:
0N/A {
0N/A if (eraIndex > BEFORE_MEIJI) {
0N/A value = 1;
0N/A long since = eras[eraIndex].getSince(getZone());
0N/A CalendarDate d = jcal.getCalendarDate(since, getZone());
0N/A // Use the same year in jd to take care of leap
0N/A // years. i.e., both jd and d must agree on leap
0N/A // or common years.
0N/A jd.setYear(d.getYear());
0N/A jcal.normalize(jd);
0N/A assert jd.isLeapYear() == d.isLeapYear();
0N/A if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
0N/A value++;
0N/A }
0N/A } else {
0N/A value = getMinimum(field);
0N/A CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
0N/A // Use an equvalent year of d.getYear() if
0N/A // possible. Otherwise, ignore the leap year and
0N/A // common year difference.
0N/A int y = d.getYear();
0N/A if (y > 400) {
0N/A y -= 400;
0N/A }
0N/A jd.setYear(y);
0N/A jcal.normalize(jd);
0N/A if (getYearOffsetInMillis(jd) < getYearOffsetInMillis(d)) {
0N/A value++;
0N/A }
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case MONTH:
0N/A {
0N/A // In Before Meiji and Meiji, January is the first month.
0N/A if (eraIndex > MEIJI && jd.getYear() == 1) {
0N/A long since = eras[eraIndex].getSince(getZone());
0N/A CalendarDate d = jcal.getCalendarDate(since, getZone());
0N/A value = d.getMonth() - 1;
0N/A if (jd.getDayOfMonth() < d.getDayOfMonth()) {
0N/A value++;
0N/A }
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case WEEK_OF_YEAR:
0N/A {
0N/A value = 1;
0N/A CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
0N/A // shift 400 years to avoid underflow
0N/A d.addYear(+400);
0N/A jcal.normalize(d);
0N/A jd.setEra(d.getEra());
0N/A jd.setYear(d.getYear());
0N/A jcal.normalize(jd);
0N/A
0N/A long jan1 = jcal.getFixedDate(d);
0N/A long fd = jcal.getFixedDate(jd);
0N/A int woy = getWeekNumber(jan1, fd);
0N/A long day1 = fd - (7 * (woy - 1));
0N/A if ((day1 < jan1) ||
0N/A (day1 == jan1 &&
0N/A jd.getTimeOfDay() < d.getTimeOfDay())) {
0N/A value++;
0N/A }
0N/A }
0N/A break;
0N/A }
0N/A return value;
0N/A }
0N/A
0N/A /**
0N/A * Returns the maximum value that this calendar field could have,
0N/A * taking into consideration the given time value and the current
0N/A * values of the
0N/A * {@link Calendar#getFirstDayOfWeek() getFirstDayOfWeek},
0N/A * {@link Calendar#getMinimalDaysInFirstWeek() getMinimalDaysInFirstWeek},
0N/A * and
0N/A * {@link Calendar#getTimeZone() getTimeZone} methods.
0N/A * For example, if the date of this instance is Heisei 16February 1,
0N/A * the actual maximum value of the <code>DAY_OF_MONTH</code> field
0N/A * is 29 because Heisei 16 is a leap year, and if the date of this
0N/A * instance is Heisei 17 February 1, it's 28.
0N/A *
0N/A * @param field the calendar field
0N/A * @return the maximum of the given field for the time value of
0N/A * this <code>JapaneseImperialCalendar</code>
0N/A * @see #getMinimum(int)
0N/A * @see #getMaximum(int)
0N/A * @see #getGreatestMinimum(int)
0N/A * @see #getLeastMaximum(int)
0N/A * @see #getActualMinimum(int)
0N/A */
0N/A public int getActualMaximum(int field) {
0N/A final int fieldsForFixedMax = ERA_MASK|DAY_OF_WEEK_MASK|HOUR_MASK|AM_PM_MASK|
0N/A HOUR_OF_DAY_MASK|MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK|
0N/A ZONE_OFFSET_MASK|DST_OFFSET_MASK;
0N/A if ((fieldsForFixedMax & (1<<field)) != 0) {
0N/A return getMaximum(field);
0N/A }
0N/A
0N/A JapaneseImperialCalendar jc = getNormalizedCalendar();
0N/A LocalGregorianCalendar.Date date = jc.jdate;
0N/A int normalizedYear = date.getNormalizedYear();
0N/A
0N/A int value = -1;
0N/A switch (field) {
0N/A case MONTH:
0N/A {
0N/A value = DECEMBER;
0N/A if (isTransitionYear(date.getNormalizedYear())) {
0N/A // TODO: there may be multiple transitions in a year.
0N/A int eraIndex = getEraIndex(date);
0N/A if (date.getYear() != 1) {
0N/A eraIndex++;
0N/A assert eraIndex < eras.length;
0N/A }
0N/A long transition = sinceFixedDates[eraIndex];
0N/A long fd = jc.cachedFixedDate;
0N/A if (fd < transition) {
0N/A LocalGregorianCalendar.Date ldate
0N/A = (LocalGregorianCalendar.Date) date.clone();
0N/A jcal.getCalendarDateFromFixedDate(ldate, transition - 1);
0N/A value = ldate.getMonth() - 1;
0N/A }
0N/A } else {
0N/A LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
0N/A getZone());
0N/A if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
0N/A value = d.getMonth() - 1;
0N/A }
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case DAY_OF_MONTH:
0N/A value = jcal.getMonthLength(date);
0N/A break;
0N/A
0N/A case DAY_OF_YEAR:
0N/A {
0N/A if (isTransitionYear(date.getNormalizedYear())) {
0N/A // Handle transition year.
0N/A // TODO: there may be multiple transitions in a year.
0N/A int eraIndex = getEraIndex(date);
0N/A if (date.getYear() != 1) {
0N/A eraIndex++;
0N/A assert eraIndex < eras.length;
0N/A }
0N/A long transition = sinceFixedDates[eraIndex];
0N/A long fd = jc.cachedFixedDate;
0N/A CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
0N/A d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
0N/A if (fd < transition) {
0N/A value = (int)(transition - gcal.getFixedDate(d));
0N/A } else {
0N/A d.addYear(+1);
0N/A value = (int)(gcal.getFixedDate(d) - transition);
0N/A }
0N/A } else {
0N/A LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MAX_VALUE,
0N/A getZone());
0N/A if (date.getEra() == d.getEra() && date.getYear() == d.getYear()) {
0N/A long fd = jcal.getFixedDate(d);
0N/A long jan1 = getFixedDateJan1(d, fd);
0N/A value = (int)(fd - jan1) + 1;
0N/A } else if (date.getYear() == getMinimum(YEAR)) {
0N/A CalendarDate d1 = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
0N/A long fd1 = jcal.getFixedDate(d1);
0N/A d1.addYear(1);
0N/A d1.setMonth(BaseCalendar.JANUARY).setDayOfMonth(1);
0N/A jcal.normalize(d1);
0N/A long fd2 = jcal.getFixedDate(d1);
0N/A value = (int)(fd2 - fd1);
0N/A } else {
0N/A value = jcal.getYearLength(date);
0N/A }
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case WEEK_OF_YEAR:
0N/A {
0N/A if (!isTransitionYear(date.getNormalizedYear())) {
0N/A LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
0N/A getZone());
0N/A if (date.getEra() == jd.getEra() && date.getYear() == jd.getYear()) {
0N/A long fd = jcal.getFixedDate(jd);
0N/A long jan1 = getFixedDateJan1(jd, fd);
0N/A value = getWeekNumber(jan1, fd);
0N/A } else if (date.getEra() == null && date.getYear() == getMinimum(YEAR)) {
0N/A CalendarDate d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
0N/A // shift 400 years to avoid underflow
0N/A d.addYear(+400);
0N/A jcal.normalize(d);
0N/A jd.setEra(d.getEra());
0N/A jd.setDate(d.getYear() + 1, BaseCalendar.JANUARY, 1);
0N/A jcal.normalize(jd);
0N/A long jan1 = jcal.getFixedDate(d);
0N/A long nextJan1 = jcal.getFixedDate(jd);
0N/A long nextJan1st = jcal.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
0N/A getFirstDayOfWeek());
0N/A int ndays = (int)(nextJan1st - nextJan1);
0N/A if (ndays >= getMinimalDaysInFirstWeek()) {
0N/A nextJan1st -= 7;
0N/A }
0N/A value = getWeekNumber(jan1, nextJan1st);
0N/A } else {
0N/A // Get the day of week of January 1 of the year
0N/A CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
0N/A d.setDate(date.getNormalizedYear(), BaseCalendar.JANUARY, 1);
0N/A int dayOfWeek = gcal.getDayOfWeek(d);
0N/A // Normalize the day of week with the firstDayOfWeek value
0N/A dayOfWeek -= getFirstDayOfWeek();
0N/A if (dayOfWeek < 0) {
0N/A dayOfWeek += 7;
0N/A }
0N/A value = 52;
0N/A int magic = dayOfWeek + getMinimalDaysInFirstWeek() - 1;
0N/A if ((magic == 6) ||
0N/A (date.isLeapYear() && (magic == 5 || magic == 12))) {
0N/A value++;
0N/A }
0N/A }
0N/A break;
0N/A }
0N/A
0N/A if (jc == this) {
0N/A jc = (JapaneseImperialCalendar) jc.clone();
0N/A }
0N/A int max = getActualMaximum(DAY_OF_YEAR);
0N/A jc.set(DAY_OF_YEAR, max);
0N/A value = jc.get(WEEK_OF_YEAR);
0N/A if (value == 1 && max > 7) {
0N/A jc.add(WEEK_OF_YEAR, -1);
0N/A value = jc.get(WEEK_OF_YEAR);
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case WEEK_OF_MONTH:
0N/A {
0N/A LocalGregorianCalendar.Date jd = jcal.getCalendarDate(Long.MAX_VALUE,
0N/A getZone());
0N/A if (!(date.getEra() == jd.getEra() && date.getYear() == jd.getYear())) {
0N/A CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
0N/A d.setDate(date.getNormalizedYear(), date.getMonth(), 1);
0N/A int dayOfWeek = gcal.getDayOfWeek(d);
0N/A int monthLength = gcal.getMonthLength(d);
0N/A dayOfWeek -= getFirstDayOfWeek();
0N/A if (dayOfWeek < 0) {
0N/A dayOfWeek += 7;
0N/A }
0N/A int nDaysFirstWeek = 7 - dayOfWeek; // # of days in the first week
0N/A value = 3;
0N/A if (nDaysFirstWeek >= getMinimalDaysInFirstWeek()) {
0N/A value++;
0N/A }
0N/A monthLength -= nDaysFirstWeek + 7 * 3;
0N/A if (monthLength > 0) {
0N/A value++;
0N/A if (monthLength > 7) {
0N/A value++;
0N/A }
0N/A }
0N/A } else {
0N/A long fd = jcal.getFixedDate(jd);
0N/A long month1 = fd - jd.getDayOfMonth() + 1;
0N/A value = getWeekNumber(month1, fd);
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case DAY_OF_WEEK_IN_MONTH:
0N/A {
0N/A int ndays, dow1;
0N/A int dow = date.getDayOfWeek();
0N/A BaseCalendar.Date d = (BaseCalendar.Date) date.clone();
0N/A ndays = jcal.getMonthLength(d);
0N/A d.setDayOfMonth(1);
0N/A jcal.normalize(d);
0N/A dow1 = d.getDayOfWeek();
0N/A int x = dow - dow1;
0N/A if (x < 0) {
0N/A x += 7;
0N/A }
0N/A ndays -= x;
0N/A value = (ndays + 6) / 7;
0N/A }
0N/A break;
0N/A
0N/A case YEAR:
0N/A {
0N/A CalendarDate jd = jcal.getCalendarDate(jc.getTimeInMillis(), getZone());
0N/A CalendarDate d;
0N/A int eraIndex = getEraIndex(date);
0N/A if (eraIndex == eras.length - 1) {
0N/A d = jcal.getCalendarDate(Long.MAX_VALUE, getZone());
0N/A value = d.getYear();
0N/A // Use an equivalent year for the
0N/A // getYearOffsetInMillis call to avoid overflow.
0N/A if (value > 400) {
0N/A jd.setYear(value - 400);
0N/A }
0N/A } else {
0N/A d = jcal.getCalendarDate(eras[eraIndex + 1].getSince(getZone()) - 1,
0N/A getZone());
0N/A value = d.getYear();
0N/A // Use the same year as d.getYear() to be
0N/A // consistent with leap and common years.
0N/A jd.setYear(value);
0N/A }
0N/A jcal.normalize(jd);
0N/A if (getYearOffsetInMillis(jd) > getYearOffsetInMillis(d)) {
0N/A value--;
0N/A }
0N/A }
0N/A break;
0N/A
0N/A default:
0N/A throw new ArrayIndexOutOfBoundsException(field);
0N/A }
0N/A return value;
0N/A }
0N/A
0N/A /**
0N/A * Returns the millisecond offset from the beginning of the
0N/A * year. In the year for Long.MIN_VALUE, it's a pseudo value
0N/A * beyond the limit. The given CalendarDate object must have been
0N/A * normalized before calling this method.
0N/A */
0N/A private final long getYearOffsetInMillis(CalendarDate date) {
0N/A long t = (jcal.getDayOfYear(date) - 1) * ONE_DAY;
0N/A return t + date.getTimeOfDay() - date.getZoneOffset();
0N/A }
0N/A
0N/A public Object clone() {
0N/A JapaneseImperialCalendar other = (JapaneseImperialCalendar) super.clone();
0N/A
0N/A other.jdate = (LocalGregorianCalendar.Date) jdate.clone();
0N/A other.originalFields = null;
0N/A other.zoneOffsets = null;
0N/A return other;
0N/A }
0N/A
0N/A public TimeZone getTimeZone() {
0N/A TimeZone zone = super.getTimeZone();
0N/A // To share the zone by the CalendarDate
0N/A jdate.setZone(zone);
0N/A return zone;
0N/A }
0N/A
0N/A public void setTimeZone(TimeZone zone) {
0N/A super.setTimeZone(zone);
0N/A // To share the zone by the CalendarDate
0N/A jdate.setZone(zone);
0N/A }
0N/A
0N/A /**
0N/A * The fixed date corresponding to jdate. If the value is
0N/A * Long.MIN_VALUE, the fixed date value is unknown.
0N/A */
0N/A transient private long cachedFixedDate = Long.MIN_VALUE;
0N/A
0N/A /**
0N/A * Converts the time value (millisecond offset from the <a
0N/A * href="Calendar.html#Epoch">Epoch</a>) to calendar field values.
0N/A * The time is <em>not</em>
0N/A * recomputed first; to recompute the time, then the fields, call the
0N/A * <code>complete</code> method.
0N/A *
0N/A * @see Calendar#complete
0N/A */
0N/A protected void computeFields() {
0N/A int mask = 0;
0N/A if (isPartiallyNormalized()) {
0N/A // Determine which calendar fields need to be computed.
0N/A mask = getSetStateFields();
0N/A int fieldMask = ~mask & ALL_FIELDS;
0N/A if (fieldMask != 0 || cachedFixedDate == Long.MIN_VALUE) {
0N/A mask |= computeFields(fieldMask,
0N/A mask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK));
0N/A assert mask == ALL_FIELDS;
0N/A }
0N/A } else {
0N/A // Specify all fields
0N/A mask = ALL_FIELDS;
0N/A computeFields(mask, 0);
0N/A }
0N/A // After computing all the fields, set the field state to `COMPUTED'.
0N/A setFieldsComputed(mask);
0N/A }
0N/A
0N/A /**
0N/A * This computeFields implements the conversion from UTC
0N/A * (millisecond offset from the Epoch) to calendar
0N/A * field values. fieldMask specifies which fields to change the
0N/A * setting state to COMPUTED, although all fields are set to
0N/A * the correct values. This is required to fix 4685354.
0N/A *
0N/A * @param fieldMask a bit mask to specify which fields to change
0N/A * the setting state.
0N/A * @param tzMask a bit mask to specify which time zone offset
0N/A * fields to be used for time calculations
0N/A * @return a new field mask that indicates what field values have
0N/A * actually been set.
0N/A */
0N/A private int computeFields(int fieldMask, int tzMask) {
0N/A int zoneOffset = 0;
0N/A TimeZone tz = getZone();
0N/A if (zoneOffsets == null) {
0N/A zoneOffsets = new int[2];
0N/A }
0N/A if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
0N/A if (tz instanceof ZoneInfo) {
0N/A zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
0N/A } else {
0N/A zoneOffset = tz.getOffset(time);
0N/A zoneOffsets[0] = tz.getRawOffset();
0N/A zoneOffsets[1] = zoneOffset - zoneOffsets[0];
0N/A }
0N/A }
0N/A if (tzMask != 0) {
0N/A if (isFieldSet(tzMask, ZONE_OFFSET)) {
0N/A zoneOffsets[0] = internalGet(ZONE_OFFSET);
0N/A }
0N/A if (isFieldSet(tzMask, DST_OFFSET)) {
0N/A zoneOffsets[1] = internalGet(DST_OFFSET);
0N/A }
0N/A zoneOffset = zoneOffsets[0] + zoneOffsets[1];
0N/A }
0N/A
0N/A // By computing time and zoneOffset separately, we can take
0N/A // the wider range of time+zoneOffset than the previous
0N/A // implementation.
0N/A long fixedDate = zoneOffset / ONE_DAY;
0N/A int timeOfDay = zoneOffset % (int)ONE_DAY;
0N/A fixedDate += time / ONE_DAY;
0N/A timeOfDay += (int) (time % ONE_DAY);
0N/A if (timeOfDay >= ONE_DAY) {
0N/A timeOfDay -= ONE_DAY;
0N/A ++fixedDate;
0N/A } else {
0N/A while (timeOfDay < 0) {
0N/A timeOfDay += ONE_DAY;
0N/A --fixedDate;
0N/A }
0N/A }
0N/A fixedDate += EPOCH_OFFSET;
0N/A
0N/A // See if we can use jdate to avoid date calculation.
0N/A if (fixedDate != cachedFixedDate || fixedDate < 0) {
0N/A jcal.getCalendarDateFromFixedDate(jdate, fixedDate);
0N/A cachedFixedDate = fixedDate;
0N/A }
0N/A int era = getEraIndex(jdate);
0N/A int year = jdate.getYear();
0N/A
0N/A // Always set the ERA and YEAR values.
0N/A internalSet(ERA, era);
0N/A internalSet(YEAR, year);
0N/A int mask = fieldMask | (ERA_MASK|YEAR_MASK);
0N/A
0N/A int month = jdate.getMonth() - 1; // 0-based
0N/A int dayOfMonth = jdate.getDayOfMonth();
0N/A
0N/A // Set the basic date fields.
0N/A if ((fieldMask & (MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK))
0N/A != 0) {
0N/A internalSet(MONTH, month);
0N/A internalSet(DAY_OF_MONTH, dayOfMonth);
0N/A internalSet(DAY_OF_WEEK, jdate.getDayOfWeek());
0N/A mask |= MONTH_MASK|DAY_OF_MONTH_MASK|DAY_OF_WEEK_MASK;
0N/A }
0N/A
0N/A if ((fieldMask & (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
0N/A |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK)) != 0) {
0N/A if (timeOfDay != 0) {
0N/A int hours = timeOfDay / ONE_HOUR;
0N/A internalSet(HOUR_OF_DAY, hours);
0N/A internalSet(AM_PM, hours / 12); // Assume AM == 0
0N/A internalSet(HOUR, hours % 12);
0N/A int r = timeOfDay % ONE_HOUR;
0N/A internalSet(MINUTE, r / ONE_MINUTE);
0N/A r %= ONE_MINUTE;
0N/A internalSet(SECOND, r / ONE_SECOND);
0N/A internalSet(MILLISECOND, r % ONE_SECOND);
0N/A } else {
0N/A internalSet(HOUR_OF_DAY, 0);
0N/A internalSet(AM_PM, AM);
0N/A internalSet(HOUR, 0);
0N/A internalSet(MINUTE, 0);
0N/A internalSet(SECOND, 0);
0N/A internalSet(MILLISECOND, 0);
0N/A }
0N/A mask |= (HOUR_OF_DAY_MASK|AM_PM_MASK|HOUR_MASK
0N/A |MINUTE_MASK|SECOND_MASK|MILLISECOND_MASK);
0N/A }
0N/A
0N/A if ((fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) != 0) {
0N/A internalSet(ZONE_OFFSET, zoneOffsets[0]);
0N/A internalSet(DST_OFFSET, zoneOffsets[1]);
0N/A mask |= (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
0N/A }
0N/A
0N/A if ((fieldMask & (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK
0N/A |WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK)) != 0) {
0N/A int normalizedYear = jdate.getNormalizedYear();
0N/A // If it's a year of an era transition, we need to handle
0N/A // irregular year boundaries.
0N/A boolean transitionYear = isTransitionYear(jdate.getNormalizedYear());
0N/A int dayOfYear;
0N/A long fixedDateJan1;
0N/A if (transitionYear) {
0N/A fixedDateJan1 = getFixedDateJan1(jdate, fixedDate);
0N/A dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
0N/A } else if (normalizedYear == MIN_VALUES[YEAR]) {
0N/A CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
0N/A fixedDateJan1 = jcal.getFixedDate(dx);
0N/A dayOfYear = (int)(fixedDate - fixedDateJan1) + 1;
0N/A } else {
0N/A dayOfYear = (int) jcal.getDayOfYear(jdate);
0N/A fixedDateJan1 = fixedDate - dayOfYear + 1;
0N/A }
0N/A long fixedDateMonth1 = transitionYear ?
0N/A getFixedDateMonth1(jdate, fixedDate) : fixedDate - dayOfMonth + 1;
0N/A
0N/A internalSet(DAY_OF_YEAR, dayOfYear);
0N/A internalSet(DAY_OF_WEEK_IN_MONTH, (dayOfMonth - 1) / 7 + 1);
0N/A
0N/A int weekOfYear = getWeekNumber(fixedDateJan1, fixedDate);
0N/A
0N/A // The spec is to calculate WEEK_OF_YEAR in the
0N/A // ISO8601-style. This creates problems, though.
0N/A if (weekOfYear == 0) {
0N/A // If the date belongs to the last week of the
0N/A // previous year, use the week number of "12/31" of
0N/A // the "previous" year. Again, if the previous year is
0N/A // a transition year, we need to take care of it.
0N/A // Usually the previous day of the first day of a year
0N/A // is December 31, which is not always true in the
0N/A // Japanese imperial calendar system.
0N/A long fixedDec31 = fixedDateJan1 - 1;
0N/A long prevJan1;
0N/A LocalGregorianCalendar.Date d = getCalendarDate(fixedDec31);
0N/A if (!(transitionYear || isTransitionYear(d.getNormalizedYear()))) {
0N/A prevJan1 = fixedDateJan1 - 365;
0N/A if (d.isLeapYear()) {
0N/A --prevJan1;
0N/A }
0N/A } else if (transitionYear) {
0N/A if (jdate.getYear() == 1) {
0N/A // As of Heisei (since Meiji) there's no case
0N/A // that there are multiple transitions in a
0N/A // year. Historically there was such
0N/A // case. There might be such case again in the
0N/A // future.
0N/A if (era > HEISEI) {
0N/A CalendarDate pd = eras[era - 1].getSinceDate();
0N/A if (normalizedYear == pd.getYear()) {
0N/A d.setMonth(pd.getMonth()).setDayOfMonth(pd.getDayOfMonth());
0N/A }
0N/A } else {
0N/A d.setMonth(jcal.JANUARY).setDayOfMonth(1);
0N/A }
0N/A jcal.normalize(d);
0N/A prevJan1 = jcal.getFixedDate(d);
0N/A } else {
0N/A prevJan1 = fixedDateJan1 - 365;
0N/A if (d.isLeapYear()) {
0N/A --prevJan1;
0N/A }
0N/A }
0N/A } else {
0N/A CalendarDate cd = eras[getEraIndex(jdate)].getSinceDate();
0N/A d.setMonth(cd.getMonth()).setDayOfMonth(cd.getDayOfMonth());
0N/A jcal.normalize(d);
0N/A prevJan1 = jcal.getFixedDate(d);
0N/A }
0N/A weekOfYear = getWeekNumber(prevJan1, fixedDec31);
0N/A } else {
0N/A if (!transitionYear) {
0N/A // Regular years
0N/A if (weekOfYear >= 52) {
0N/A long nextJan1 = fixedDateJan1 + 365;
0N/A if (jdate.isLeapYear()) {
0N/A nextJan1++;
0N/A }
0N/A long nextJan1st = jcal.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
0N/A getFirstDayOfWeek());
0N/A int ndays = (int)(nextJan1st - nextJan1);
0N/A if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
0N/A // The first days forms a week in which the date is included.
0N/A weekOfYear = 1;
0N/A }
0N/A }
0N/A } else {
0N/A LocalGregorianCalendar.Date d = (LocalGregorianCalendar.Date) jdate.clone();
0N/A long nextJan1;
0N/A if (jdate.getYear() == 1) {
0N/A d.addYear(+1);
0N/A d.setMonth(jcal.JANUARY).setDayOfMonth(1);
0N/A nextJan1 = jcal.getFixedDate(d);
0N/A } else {
0N/A int nextEraIndex = getEraIndex(d) + 1;
0N/A CalendarDate cd = eras[nextEraIndex].getSinceDate();
0N/A d.setEra(eras[nextEraIndex]);
0N/A d.setDate(1, cd.getMonth(), cd.getDayOfMonth());
0N/A jcal.normalize(d);
0N/A nextJan1 = jcal.getFixedDate(d);
0N/A }
0N/A long nextJan1st = jcal.getDayOfWeekDateOnOrBefore(nextJan1 + 6,
0N/A getFirstDayOfWeek());
0N/A int ndays = (int)(nextJan1st - nextJan1);
0N/A if (ndays >= getMinimalDaysInFirstWeek() && fixedDate >= (nextJan1st - 7)) {
0N/A // The first days forms a week in which the date is included.
0N/A weekOfYear = 1;
0N/A }
0N/A }
0N/A }
0N/A internalSet(WEEK_OF_YEAR, weekOfYear);
0N/A internalSet(WEEK_OF_MONTH, getWeekNumber(fixedDateMonth1, fixedDate));
0N/A mask |= (DAY_OF_YEAR_MASK|WEEK_OF_YEAR_MASK|WEEK_OF_MONTH_MASK|DAY_OF_WEEK_IN_MONTH_MASK);
0N/A }
0N/A return mask;
0N/A }
0N/A
0N/A /**
0N/A * Returns the number of weeks in a period between fixedDay1 and
0N/A * fixedDate. The getFirstDayOfWeek-getMinimalDaysInFirstWeek rule
0N/A * is applied to calculate the number of weeks.
0N/A *
0N/A * @param fixedDay1 the fixed date of the first day of the period
0N/A * @param fixedDate the fixed date of the last day of the period
0N/A * @return the number of weeks of the given period
0N/A */
0N/A private final int getWeekNumber(long fixedDay1, long fixedDate) {
0N/A // We can always use `jcal' since Julian and Gregorian are the
0N/A // same thing for this calculation.
0N/A long fixedDay1st = jcal.getDayOfWeekDateOnOrBefore(fixedDay1 + 6,
0N/A getFirstDayOfWeek());
0N/A int ndays = (int)(fixedDay1st - fixedDay1);
0N/A assert ndays <= 7;
0N/A if (ndays >= getMinimalDaysInFirstWeek()) {
0N/A fixedDay1st -= 7;
0N/A }
0N/A int normalizedDayOfPeriod = (int)(fixedDate - fixedDay1st);
0N/A if (normalizedDayOfPeriod >= 0) {
0N/A return normalizedDayOfPeriod / 7 + 1;
0N/A }
0N/A return CalendarUtils.floorDivide(normalizedDayOfPeriod, 7) + 1;
0N/A }
0N/A
0N/A /**
0N/A * Converts calendar field values to the time value (millisecond
0N/A * offset from the <a href="Calendar.html#Epoch">Epoch</a>).
0N/A *
0N/A * @exception IllegalArgumentException if any calendar fields are invalid.
0N/A */
0N/A protected void computeTime() {
0N/A // In non-lenient mode, perform brief checking of calendar
0N/A // fields which have been set externally. Through this
0N/A // checking, the field values are stored in originalFields[]
0N/A // to see if any of them are normalized later.
0N/A if (!isLenient()) {
0N/A if (originalFields == null) {
0N/A originalFields = new int[FIELD_COUNT];
0N/A }
0N/A for (int field = 0; field < FIELD_COUNT; field++) {
0N/A int value = internalGet(field);
0N/A if (isExternallySet(field)) {
0N/A // Quick validation for any out of range values
0N/A if (value < getMinimum(field) || value > getMaximum(field)) {
0N/A throw new IllegalArgumentException(getFieldName(field));
0N/A }
0N/A }
0N/A originalFields[field] = value;
0N/A }
0N/A }
0N/A
0N/A // Let the super class determine which calendar fields to be
0N/A // used to calculate the time.
0N/A int fieldMask = selectFields();
0N/A
0N/A int year;
0N/A int era;
0N/A
0N/A if (isSet(ERA)) {
0N/A era = internalGet(ERA);
0N/A year = isSet(YEAR) ? internalGet(YEAR) : 1;
0N/A } else {
0N/A if (isSet(YEAR)) {
0N/A era = eras.length - 1;
0N/A year = internalGet(YEAR);
0N/A } else {
0N/A // Equivalent to 1970 (Gregorian)
0N/A era = SHOWA;
0N/A year = 45;
0N/A }
0N/A }
0N/A
0N/A // Calculate the time of day. We rely on the convention that
0N/A // an UNSET field has 0.
0N/A long timeOfDay = 0;
0N/A if (isFieldSet(fieldMask, HOUR_OF_DAY)) {
0N/A timeOfDay += (long) internalGet(HOUR_OF_DAY);
0N/A } else {
0N/A timeOfDay += internalGet(HOUR);
0N/A // The default value of AM_PM is 0 which designates AM.
0N/A if (isFieldSet(fieldMask, AM_PM)) {
0N/A timeOfDay += 12 * internalGet(AM_PM);
0N/A }
0N/A }
0N/A timeOfDay *= 60;
0N/A timeOfDay += internalGet(MINUTE);
0N/A timeOfDay *= 60;
0N/A timeOfDay += internalGet(SECOND);
0N/A timeOfDay *= 1000;
0N/A timeOfDay += internalGet(MILLISECOND);
0N/A
0N/A // Convert the time of day to the number of days and the
0N/A // millisecond offset from midnight.
0N/A long fixedDate = timeOfDay / ONE_DAY;
0N/A timeOfDay %= ONE_DAY;
0N/A while (timeOfDay < 0) {
0N/A timeOfDay += ONE_DAY;
0N/A --fixedDate;
0N/A }
0N/A
0N/A // Calculate the fixed date since January 1, 1 (Gregorian).
0N/A fixedDate += getFixedDate(era, year, fieldMask);
0N/A
0N/A // millis represents local wall-clock time in milliseconds.
0N/A long millis = (fixedDate - EPOCH_OFFSET) * ONE_DAY + timeOfDay;
0N/A
0N/A // Compute the time zone offset and DST offset. There are two potential
0N/A // ambiguities here. We'll assume a 2:00 am (wall time) switchover time
0N/A // for discussion purposes here.
0N/A // 1. The transition into DST. Here, a designated time of 2:00 am - 2:59 am
0N/A // can be in standard or in DST depending. However, 2:00 am is an invalid
0N/A // representation (the representation jumps from 1:59:59 am Std to 3:00:00 am DST).
0N/A // We assume standard time.
0N/A // 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am
0N/A // can be in standard or DST. Both are valid representations (the rep
0N/A // jumps from 1:59:59 DST to 1:00:00 Std).
0N/A // Again, we assume standard time.
0N/A // We use the TimeZone object, unless the user has explicitly set the ZONE_OFFSET
0N/A // or DST_OFFSET fields; then we use those fields.
0N/A TimeZone zone = getZone();
0N/A if (zoneOffsets == null) {
0N/A zoneOffsets = new int[2];
0N/A }
0N/A int tzMask = fieldMask & (ZONE_OFFSET_MASK|DST_OFFSET_MASK);
0N/A if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
0N/A if (zone instanceof ZoneInfo) {
0N/A ((ZoneInfo)zone).getOffsetsByWall(millis, zoneOffsets);
0N/A } else {
0N/A zone.getOffsets(millis - zone.getRawOffset(), zoneOffsets);
0N/A }
0N/A }
0N/A if (tzMask != 0) {
0N/A if (isFieldSet(tzMask, ZONE_OFFSET)) {
0N/A zoneOffsets[0] = internalGet(ZONE_OFFSET);
0N/A }
0N/A if (isFieldSet(tzMask, DST_OFFSET)) {
0N/A zoneOffsets[1] = internalGet(DST_OFFSET);
0N/A }
0N/A }
0N/A
0N/A // Adjust the time zone offset values to get the UTC time.
0N/A millis -= zoneOffsets[0] + zoneOffsets[1];
0N/A
0N/A // Set this calendar's time in milliseconds
0N/A time = millis;
0N/A
0N/A int mask = computeFields(fieldMask | getSetStateFields(), tzMask);
0N/A
0N/A if (!isLenient()) {
0N/A for (int field = 0; field < FIELD_COUNT; field++) {
0N/A if (!isExternallySet(field)) {
0N/A continue;
0N/A }
0N/A if (originalFields[field] != internalGet(field)) {
0N/A int wrongValue = internalGet(field);
0N/A // Restore the original field values
0N/A System.arraycopy(originalFields, 0, fields, 0, fields.length);
0N/A throw new IllegalArgumentException(getFieldName(field) + "=" + wrongValue
0N/A + ", expected " + originalFields[field]);
0N/A }
0N/A }
0N/A }
0N/A setFieldsNormalized(mask);
0N/A }
0N/A
0N/A /**
0N/A * Computes the fixed date under either the Gregorian or the
0N/A * Julian calendar, using the given year and the specified calendar fields.
0N/A *
0N/A * @param cal the CalendarSystem to be used for the date calculation
0N/A * @param year the normalized year number, with 0 indicating the
0N/A * year 1 BCE, -1 indicating 2 BCE, etc.
0N/A * @param fieldMask the calendar fields to be used for the date calculation
0N/A * @return the fixed date
0N/A * @see Calendar#selectFields
0N/A */
0N/A private long getFixedDate(int era, int year, int fieldMask) {
0N/A int month = JANUARY;
0N/A int firstDayOfMonth = 1;
0N/A if (isFieldSet(fieldMask, MONTH)) {
0N/A // No need to check if MONTH has been set (no isSet(MONTH)
0N/A // call) since its unset value happens to be JANUARY (0).
0N/A month = internalGet(MONTH);
0N/A
0N/A // If the month is out of range, adjust it into range.
0N/A if (month > DECEMBER) {
0N/A year += month / 12;
0N/A month %= 12;
0N/A } else if (month < JANUARY) {
0N/A int[] rem = new int[1];
0N/A year += CalendarUtils.floorDivide(month, 12, rem);
0N/A month = rem[0];
0N/A }
0N/A } else {
0N/A if (year == 1 && era != 0) {
0N/A CalendarDate d = eras[era].getSinceDate();
0N/A month = d.getMonth() - 1;
0N/A firstDayOfMonth = d.getDayOfMonth();
0N/A }
0N/A }
0N/A
0N/A // Adjust the base date if year is the minimum value.
0N/A if (year == MIN_VALUES[YEAR]) {
0N/A CalendarDate dx = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
0N/A int m = dx.getMonth() - 1;
0N/A if (month < m)
0N/A month = m;
0N/A if (month == m)
0N/A firstDayOfMonth = dx.getDayOfMonth();
0N/A }
0N/A
0N/A LocalGregorianCalendar.Date date = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
0N/A date.setEra(era > 0 ? eras[era] : null);
0N/A date.setDate(year, month + 1, firstDayOfMonth);
0N/A jcal.normalize(date);
0N/A
0N/A // Get the fixed date since Jan 1, 1 (Gregorian). We are on
0N/A // the first day of either `month' or January in 'year'.
0N/A long fixedDate = jcal.getFixedDate(date);
0N/A
0N/A if (isFieldSet(fieldMask, MONTH)) {
0N/A // Month-based calculations
0N/A if (isFieldSet(fieldMask, DAY_OF_MONTH)) {
0N/A // We are on the "first day" of the month (which may
0N/A // not be 1). Just add the offset if DAY_OF_MONTH is
0N/A // set. If the isSet call returns false, that means
0N/A // DAY_OF_MONTH has been selected just because of the
0N/A // selected combination. We don't need to add any
0N/A // since the default value is the "first day".
0N/A if (isSet(DAY_OF_MONTH)) {
0N/A // To avoid underflow with DAY_OF_MONTH-firstDayOfMonth, add
0N/A // DAY_OF_MONTH, then subtract firstDayOfMonth.
0N/A fixedDate += internalGet(DAY_OF_MONTH);
0N/A fixedDate -= firstDayOfMonth;
0N/A }
0N/A } else {
0N/A if (isFieldSet(fieldMask, WEEK_OF_MONTH)) {
0N/A long firstDayOfWeek = jcal.getDayOfWeekDateOnOrBefore(fixedDate + 6,
0N/A getFirstDayOfWeek());
0N/A // If we have enough days in the first week, then
0N/A // move to the previous week.
0N/A if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
0N/A firstDayOfWeek -= 7;
0N/A }
0N/A if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
0N/A firstDayOfWeek = jcal.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
0N/A internalGet(DAY_OF_WEEK));
0N/A }
0N/A // In lenient mode, we treat days of the previous
0N/A // months as a part of the specified
0N/A // WEEK_OF_MONTH. See 4633646.
0N/A fixedDate = firstDayOfWeek + 7 * (internalGet(WEEK_OF_MONTH) - 1);
0N/A } else {
0N/A int dayOfWeek;
0N/A if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
0N/A dayOfWeek = internalGet(DAY_OF_WEEK);
0N/A } else {
0N/A dayOfWeek = getFirstDayOfWeek();
0N/A }
0N/A // We are basing this on the day-of-week-in-month. The only
0N/A // trickiness occurs if the day-of-week-in-month is
0N/A // negative.
0N/A int dowim;
0N/A if (isFieldSet(fieldMask, DAY_OF_WEEK_IN_MONTH)) {
0N/A dowim = internalGet(DAY_OF_WEEK_IN_MONTH);
0N/A } else {
0N/A dowim = 1;
0N/A }
0N/A if (dowim >= 0) {
0N/A fixedDate = jcal.getDayOfWeekDateOnOrBefore(fixedDate + (7 * dowim) - 1,
0N/A dayOfWeek);
0N/A } else {
0N/A // Go to the first day of the next week of
0N/A // the specified week boundary.
0N/A int lastDate = monthLength(month, year) + (7 * (dowim + 1));
0N/A // Then, get the day of week date on or before the last date.
0N/A fixedDate = jcal.getDayOfWeekDateOnOrBefore(fixedDate + lastDate - 1,
0N/A dayOfWeek);
0N/A }
0N/A }
0N/A }
0N/A } else {
0N/A // We are on the first day of the year.
0N/A if (isFieldSet(fieldMask, DAY_OF_YEAR)) {
0N/A if (isTransitionYear(date.getNormalizedYear())) {
0N/A fixedDate = getFixedDateJan1(date, fixedDate);
0N/A }
0N/A // Add the offset, then subtract 1. (Make sure to avoid underflow.)
0N/A fixedDate += internalGet(DAY_OF_YEAR);
0N/A fixedDate--;
0N/A } else {
0N/A long firstDayOfWeek = jcal.getDayOfWeekDateOnOrBefore(fixedDate + 6,
0N/A getFirstDayOfWeek());
0N/A // If we have enough days in the first week, then move
0N/A // to the previous week.
0N/A if ((firstDayOfWeek - fixedDate) >= getMinimalDaysInFirstWeek()) {
0N/A firstDayOfWeek -= 7;
0N/A }
0N/A if (isFieldSet(fieldMask, DAY_OF_WEEK)) {
0N/A int dayOfWeek = internalGet(DAY_OF_WEEK);
0N/A if (dayOfWeek != getFirstDayOfWeek()) {
0N/A firstDayOfWeek = jcal.getDayOfWeekDateOnOrBefore(firstDayOfWeek + 6,
0N/A dayOfWeek);
0N/A }
0N/A }
0N/A fixedDate = firstDayOfWeek + 7 * ((long)internalGet(WEEK_OF_YEAR) - 1);
0N/A }
0N/A }
0N/A return fixedDate;
0N/A }
0N/A
0N/A /**
0N/A * Returns the fixed date of the first day of the year (usually
0N/A * January 1) before the specified date.
0N/A *
0N/A * @param date the date for which the first day of the year is
0N/A * calculated. The date has to be in the cut-over year.
0N/A * @param fixedDate the fixed date representation of the date
0N/A */
0N/A private final long getFixedDateJan1(LocalGregorianCalendar.Date date, long fixedDate) {
0N/A Era era = date.getEra();
0N/A if (date.getEra() != null && date.getYear() == 1) {
0N/A for (int eraIndex = getEraIndex(date); eraIndex > 0; eraIndex--) {
0N/A CalendarDate d = eras[eraIndex].getSinceDate();
0N/A long fd = gcal.getFixedDate(d);
0N/A // There might be multiple era transitions in a year.
0N/A if (fd > fixedDate) {
0N/A continue;
0N/A }
0N/A return fd;
0N/A }
0N/A }
0N/A CalendarDate d = gcal.newCalendarDate(TimeZone.NO_TIMEZONE);
0N/A d.setDate(date.getNormalizedYear(), gcal.JANUARY, 1);
0N/A return gcal.getFixedDate(d);
0N/A }
0N/A
0N/A /**
0N/A * Returns the fixed date of the first date of the month (usually
0N/A * the 1st of the month) before the specified date.
0N/A *
0N/A * @param date the date for which the first day of the month is
0N/A * calculated. The date must be in the era transition year.
0N/A * @param fixedDate the fixed date representation of the date
0N/A */
0N/A private final long getFixedDateMonth1(LocalGregorianCalendar.Date date,
0N/A long fixedDate) {
0N/A int eraIndex = getTransitionEraIndex(date);
0N/A if (eraIndex != -1) {
0N/A long transition = sinceFixedDates[eraIndex];
0N/A // If the given date is on or after the transition date, then
0N/A // return the transition date.
0N/A if (transition <= fixedDate) {
0N/A return transition;
0N/A }
0N/A }
0N/A
0N/A // Otherwise, we can use the 1st day of the month.
0N/A return fixedDate - date.getDayOfMonth() + 1;
0N/A }
0N/A
0N/A /**
0N/A * Returns a LocalGregorianCalendar.Date produced from the specified fixed date.
0N/A *
0N/A * @param fd the fixed date
0N/A */
0N/A private static final LocalGregorianCalendar.Date getCalendarDate(long fd) {
0N/A LocalGregorianCalendar.Date d = jcal.newCalendarDate(TimeZone.NO_TIMEZONE);
0N/A jcal.getCalendarDateFromFixedDate(d, fd);
0N/A return d;
0N/A }
0N/A
0N/A /**
0N/A * Returns the length of the specified month in the specified
0N/A * Gregorian year. The year number must be normalized.
0N/A *
0N/A * @see #isLeapYear(int)
0N/A */
0N/A private final int monthLength(int month, int gregorianYear) {
0N/A return CalendarUtils.isGregorianLeapYear(gregorianYear) ?
0N/A GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
0N/A }
0N/A
0N/A /**
0N/A * Returns the length of the specified month in the year provided
0N/A * by internalGet(YEAR).
0N/A *
0N/A * @see #isLeapYear(int)
0N/A */
0N/A private final int monthLength(int month) {
0N/A assert jdate.isNormalized();
0N/A return jdate.isLeapYear() ?
0N/A GregorianCalendar.LEAP_MONTH_LENGTH[month] : GregorianCalendar.MONTH_LENGTH[month];
0N/A }
0N/A
0N/A private final int actualMonthLength() {
0N/A int length = jcal.getMonthLength(jdate);
0N/A int eraIndex = getTransitionEraIndex(jdate);
0N/A if (eraIndex == -1) {
0N/A long transitionFixedDate = sinceFixedDates[eraIndex];
0N/A CalendarDate d = eras[eraIndex].getSinceDate();
0N/A if (transitionFixedDate <= cachedFixedDate) {
0N/A length -= d.getDayOfMonth() - 1;
0N/A } else {
0N/A length = d.getDayOfMonth() - 1;
0N/A }
0N/A }
0N/A return length;
0N/A }
0N/A
0N/A /**
0N/A * Returns the index to the new era if the given date is in a
0N/A * transition month. For example, if the give date is Heisei 1
0N/A * (1989) January 20, then the era index for Heisei is
0N/A * returned. Likewise, if the given date is Showa 64 (1989)
0N/A * January 3, then the era index for Heisei is returned. If the
0N/A * given date is not in any transition month, then -1 is returned.
0N/A */
0N/A private static final int getTransitionEraIndex(LocalGregorianCalendar.Date date) {
0N/A int eraIndex = getEraIndex(date);
0N/A CalendarDate transitionDate = eras[eraIndex].getSinceDate();
0N/A if (transitionDate.getYear() == date.getNormalizedYear() &&
0N/A transitionDate.getMonth() == date.getMonth()) {
0N/A return eraIndex;
0N/A }
0N/A if (eraIndex < eras.length - 1) {
0N/A transitionDate = eras[++eraIndex].getSinceDate();
0N/A if (transitionDate.getYear() == date.getNormalizedYear() &&
0N/A transitionDate.getMonth() == date.getMonth()) {
0N/A return eraIndex;
0N/A }
0N/A }
0N/A return -1;
0N/A }
0N/A
0N/A private final boolean isTransitionYear(int normalizedYear) {
0N/A for (int i = eras.length - 1; i > 0; i--) {
0N/A int transitionYear = eras[i].getSinceDate().getYear();
0N/A if (normalizedYear == transitionYear) {
0N/A return true;
0N/A }
0N/A if (normalizedYear > transitionYear) {
0N/A break;
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A private static final int getEraIndex(LocalGregorianCalendar.Date date) {
0N/A Era era = date.getEra();
0N/A for (int i = eras.length - 1; i > 0; i--) {
0N/A if (eras[i] == era) {
0N/A return i;
0N/A }
0N/A }
0N/A return 0;
0N/A }
0N/A
0N/A /**
0N/A * Returns this object if it's normalized (all fields and time are
0N/A * in sync). Otherwise, a cloned object is returned after calling
0N/A * complete() in lenient mode.
0N/A */
0N/A private final JapaneseImperialCalendar getNormalizedCalendar() {
0N/A JapaneseImperialCalendar jc;
0N/A if (isFullyNormalized()) {
0N/A jc = this;
0N/A } else {
0N/A // Create a clone and normalize the calendar fields
0N/A jc = (JapaneseImperialCalendar) this.clone();
0N/A jc.setLenient(true);
0N/A jc.complete();
0N/A }
0N/A return jc;
0N/A }
0N/A
0N/A /**
0N/A * After adjustments such as add(MONTH), add(YEAR), we don't want the
0N/A * month to jump around. E.g., we don't want Jan 31 + 1 month to go to Mar
0N/A * 3, we want it to go to Feb 28. Adjustments which might run into this
0N/A * problem call this method to retain the proper month.
0N/A */
0N/A private final void pinDayOfMonth(LocalGregorianCalendar.Date date) {
0N/A int year = date.getYear();
0N/A int dom = date.getDayOfMonth();
0N/A if (year != getMinimum(YEAR)) {
0N/A date.setDayOfMonth(1);
0N/A jcal.normalize(date);
0N/A int monthLength = jcal.getMonthLength(date);
0N/A if (dom > monthLength) {
0N/A date.setDayOfMonth(monthLength);
0N/A } else {
0N/A date.setDayOfMonth(dom);
0N/A }
0N/A jcal.normalize(date);
0N/A } else {
0N/A LocalGregorianCalendar.Date d = jcal.getCalendarDate(Long.MIN_VALUE, getZone());
0N/A LocalGregorianCalendar.Date realDate = jcal.getCalendarDate(time, getZone());
0N/A long tod = realDate.getTimeOfDay();
0N/A // Use an equivalent year.
0N/A realDate.addYear(+400);
0N/A realDate.setMonth(date.getMonth());
0N/A realDate.setDayOfMonth(1);
0N/A jcal.normalize(realDate);
0N/A int monthLength = jcal.getMonthLength(realDate);
0N/A if (dom > monthLength) {
0N/A realDate.setDayOfMonth(monthLength);
0N/A } else {
0N/A if (dom < d.getDayOfMonth()) {
0N/A realDate.setDayOfMonth(d.getDayOfMonth());
0N/A } else {
0N/A realDate.setDayOfMonth(dom);
0N/A }
0N/A }
0N/A if (realDate.getDayOfMonth() == d.getDayOfMonth() && tod < d.getTimeOfDay()) {
0N/A realDate.setDayOfMonth(Math.min(dom + 1, monthLength));
0N/A }
0N/A // restore the year.
0N/A date.setDate(year, realDate.getMonth(), realDate.getDayOfMonth());
0N/A // Don't normalize date here so as not to cause underflow.
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the new value after 'roll'ing the specified value and amount.
0N/A */
0N/A private static final int getRolledValue(int value, int amount, int min, int max) {
0N/A assert value >= min && value <= max;
0N/A int range = max - min + 1;
0N/A amount %= range;
0N/A int n = value + amount;
0N/A if (n > max) {
0N/A n -= range;
0N/A } else if (n < min) {
0N/A n += range;
0N/A }
0N/A assert n >= min && n <= max;
0N/A return n;
0N/A }
0N/A
0N/A /**
0N/A * Returns the ERA. We need a special method for this because the
0N/A * default ERA is the current era, but a zero (unset) ERA means before Meiji.
0N/A */
0N/A private final int internalGetEra() {
0N/A return isSet(ERA) ? internalGet(ERA) : eras.length - 1;
0N/A }
0N/A
0N/A /**
0N/A * Updates internal state.
0N/A */
0N/A private void readObject(ObjectInputStream stream)
0N/A throws IOException, ClassNotFoundException {
0N/A stream.defaultReadObject();
0N/A if (jdate == null) {
0N/A jdate = jcal.newCalendarDate(getZone());
0N/A cachedFixedDate = Long.MIN_VALUE;
0N/A }
0N/A }
0N/A}