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