--- old/src/share/classes/java/util/JapaneseImperialCalendar.java 2014-07-22 14:44:19.496074318 +0900
+++ new/src/share/classes/java/util/JapaneseImperialCalendar.java 2014-07-22 14:44:19.363440112 +0900
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -38,20 +38,20 @@
import sun.util.calendar.ZoneInfo;
/**
- * JapaneseImperialCalendar implements a Japanese
+ * {@code JapaneseImperialCalendar} 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.
- *
+ *
* 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
+ * 1 Meiji 1868-01-01T00:00:00 local time
+ * 2 Taisho 1912-07-30T00:00:00 local time
+ * 3 Showa 1926-12-25T00:00:00 local time
+ * 4 Heisei 1989-01-08T00:00:00 local time
* ------------------------------------------------------
- *
+ *
*
*
ERA value 0 specifies the years before Meiji and
* the Gregorian year values are used. Unlike {@link
@@ -63,6 +63,31 @@
* with time differences for applying the era transitions. This
* calendar implementation assumes local time for all transitions.
*
+ *
A new era can be specified using property
+ * jdk.calendar.japanese.supplemental.era. The new era is added to the
+ * predefined eras. The syntax of the property is as follows.
+ *
+ * {@code name=,abbr=,since=
+ * where
+ *
+ *
{@code :}
the full name of the new era (non-ASCII characters allowed)
+ *
{@code :}
the abbreviation of the new era (non-ASCII characters allowed)
+ *
{@code
+ *
+ *
If the given era is invalid, such as the since value before the
+ * beginning of the last predefined era, the given era will be
+ * ignored.
+ *
+ *
The following is an example of the property usage.
+ *
+ * The property specifies an era change to NewEra at 9999-02-11T00:00:00 local time.
+ *
* @author Masayoshi Okutsu
* @since 1.6
*/
@@ -102,7 +127,6 @@
public static final int HEISEI = 4;
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
@@ -111,7 +135,6 @@
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
@@ -217,6 +240,7 @@
};
// Proclaim serialization compatibility with JDK 1.6
+ @SuppressWarnings("FieldNameHidesFieldInSuperclass")
private static final long serialVersionUID = -3364572813905467929L;
static {
@@ -340,6 +364,7 @@
* false otherwise.
* @see Calendar#compareTo(Calendar)
*/
+ @Override
public boolean equals(Object obj) {
return obj instanceof JapaneseImperialCalendar &&
super.equals(obj);
@@ -349,6 +374,7 @@
* Generates the hash code for this
* JapaneseImperialCalendar object.
*/
+ @Override
public int hashCode() {
return super.hashCode() ^ jdate.hashCode();
}
@@ -381,6 +407,7 @@
* or if any calendar fields have out-of-range values in
* non-lenient mode.
*/
+ @Override
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.
@@ -509,6 +536,7 @@
}
}
+ @Override
public void roll(int field, boolean up) {
roll(field, up ? +1 : -1);
}
@@ -533,6 +561,7 @@
* @see #add(int,int)
* @see #set(int,int)
*/
+ @Override
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.
--- old/src/share/classes/sun/util/calendar/Era.java 2014-07-22 14:44:19.917489071 +0900
+++ new/src/share/classes/sun/util/calendar/Era.java 2014-07-22 14:44:19.785623964 +0900
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -41,20 +41,15 @@
* CalendarDate.
*
*
The following era names are defined in this release.
- *
- *
+ *
* Calendar system Era name Since (in Gregorian)
* -----------------------------------------------------------------------
- * Japanese calendar Meiji 1868-01-01 midnight local time
- * Taisho 1912-07-30 midnight local time
- * Showa 1926-12-26 midnight local time
- * Heisei 1989-01-08 midnight local time
- * Julian calendar BeforeCommonEra -292275055-05-16T16:47:04.192Z
- * CommonEra 0000-12-30 midnight local time
- * Taiwanese calendar MinGuo 1911-01-01 midnight local time
- * Thai Buddhist calendar BuddhistEra -543-01-01 midnight local time
+ * Japanese calendar Meiji 1868-01-01T00:00:00 local time
+ * Taisho 1912-07-30T00:00:00 local time
+ * Showa 1926-12-25T00:00:00 local time
+ * Heisei 1989-01-08T00:00:00 local time
* -----------------------------------------------------------------------
- *
+ *
*
* @author Masayoshi Okutsu
* @since 1.5
--- old/src/share/classes/sun/util/calendar/LocalGregorianCalendar.java 2014-07-22 14:44:20.299431396 +0900
+++ new/src/share/classes/sun/util/calendar/LocalGregorianCalendar.java 2014-07-22 14:44:20.167348733 +0900
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,10 +25,7 @@
package sun.util.calendar;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
+import java.security.AccessController;
import java.util.StringTokenizer;
import java.util.TimeZone;
@@ -39,6 +36,28 @@
*/
public class LocalGregorianCalendar extends BaseCalendar {
+ private static final Era[] JAPANESE_ERAS = {
+ new Era("Meiji", "M", -3218832000000L, true),
+ new Era("Taisho", "T", -1812153600000L, true),
+ new Era("Showa", "S", -1357603200000L, true),
+ new Era("Heisei", "H", 600220800000L, true),
+ };
+
+ private static boolean isValidEra(Era newEra, Era[] eras) {
+ Era last = eras[eras.length - 1];
+ if (last.getSinceDate().getYear() >= newEra.getSinceDate().getYear()) {
+ return false;
+ }
+ // The new era name should be unique. Its abbr may not.
+ String newName = newEra.getName();
+ for (Era era : eras) {
+ if (era.getName().equals(newName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private String name;
private Era[] eras;
@@ -118,58 +137,72 @@
}
static LocalGregorianCalendar getLocalGregorianCalendar(String name) {
- Properties calendarProps;
- try {
- calendarProps = CalendarSystem.getCalendarProperties();
- } catch (IOException | IllegalArgumentException e) {
- throw new InternalError(e);
- }
- // Parse calendar.*.eras
- String props = calendarProps.getProperty("calendar." + name + ".eras");
- if (props == null) {
+ // Only the Japanese calendar is supported.
+ if (!"japanese".equals(name)) {
return null;
}
- List eras = new ArrayList<>();
- StringTokenizer eraTokens = new StringTokenizer(props, ";");
- while (eraTokens.hasMoreTokens()) {
- String items = eraTokens.nextToken().trim();
- StringTokenizer itemTokens = new StringTokenizer(items, ",");
- String eraName = null;
- boolean localTime = true;
- long since = 0;
- String abbr = null;
-
- while (itemTokens.hasMoreTokens()) {
- String item = itemTokens.nextToken();
- int index = item.indexOf('=');
- // it must be in the key=value form.
- if (index == -1) {
- return null;
- }
- String key = item.substring(0, index);
- String value = item.substring(index + 1);
- if ("name".equals(key)) {
- eraName = value;
- } else if ("since".equals(key)) {
- if (value.endsWith("u")) {
- localTime = false;
- since = Long.parseLong(value.substring(0, value.length() - 1));
- } else {
- since = Long.parseLong(value);
- }
- } else if ("abbr".equals(key)) {
- abbr = value;
- } else {
- throw new RuntimeException("Unknown key word: " + key);
+
+ // Append an era to the predefined eras if it's given by the property.
+ String prop = AccessController.doPrivileged(
+ new sun.security.action.GetPropertyAction("jdk.calendar.japanese.supplemental.era"));
+ if (prop != null) {
+ Era era = parseEraEntry(prop);
+ if (era != null) {
+ if (isValidEra(era, JAPANESE_ERAS)) {
+ int length = JAPANESE_ERAS.length;
+ Era[] eras = new Era[length + 1];
+ System.arraycopy(JAPANESE_ERAS, 0, eras, 0, length);
+ eras[length] = era;
+ return new LocalGregorianCalendar(name, eras);
}
}
- Era era = new Era(eraName, abbr, since, localTime);
- eras.add(era);
}
- Era[] eraArray = new Era[eras.size()];
- eras.toArray(eraArray);
+ return new LocalGregorianCalendar(name, JAPANESE_ERAS);
+ }
- return new LocalGregorianCalendar(name, eraArray);
+ private static Era parseEraEntry(String entry) {
+ StringTokenizer itemTokens = new StringTokenizer(entry, ",");
+ String eraName = null;
+ boolean localTime = true;
+ long since = 0;
+ String abbr = null;
+
+ while (itemTokens.hasMoreTokens()) {
+ String item = itemTokens.nextToken();
+ int index = item.indexOf('=');
+ // it must be in the key=value form.
+ if (index == -1) {
+ throw new RuntimeException("Syntax error: " + item);
+ }
+ String key = item.substring(0, index).trim();
+ String value = item.substring(index + 1).trim();
+ switch (key) {
+ case "name":
+ eraName = value;
+ break;
+ case "since":
+ if (value.endsWith("u")) {
+ localTime = false;
+ value = value.substring(0, value.length() - 1);
+ }
+ try {
+ since = Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ break;
+ case "abbr":
+ abbr = value;
+ break;
+ default:
+ return null;
+ }
+ }
+ if (eraName == null || eraName.isEmpty()
+ || abbr == null || abbr.isEmpty()) {
+ return null;
+ }
+ return new Era(eraName, abbr, since, localTime);
}
private LocalGregorianCalendar(String name, Era[] eras) {
@@ -262,9 +295,8 @@
}
private boolean validateEra(Era era) {
- // Validate the era
- for (int i = 0; i < eras.length; i++) {
- if (era == eras[i]) {
+ for (Era era1 : eras) {
+ if (era == era1) {
return true;
}
}
@@ -333,6 +365,7 @@
}
if (i >= 0) {
ldate.setLocalEra(era);
+ @SuppressWarnings("null")
int y = ldate.getNormalizedYear() - era.getSinceDate().getYear() + 1;
ldate.setLocalYear(y);
} else {
--- old/src/share/lib/calendars.properties 2014-07-22 14:44:20.697443783 +0900
+++ new/src/share/lib/calendars.properties 2014-07-22 14:44:20.557496169 +0900
@@ -1,4 +1,4 @@
-# Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -23,37 +23,6 @@
#
#
-# Japanese imperial calendar
-#
-# Meiji since 1868-01-01 00:00:00 local time (Gregorian)
-# Taisho since 1912-07-30 00:00:00 local time (Gregorian)
-# Showa since 1926-12-25 00:00:00 local time (Gregorian)
-# Heisei since 1989-01-08 00:00:00 local time (Gregorian)
-calendar.japanese.type: LocalGregorianCalendar
-calendar.japanese.eras: \
- name=Meiji,abbr=M,since=-3218832000000; \
- name=Taisho,abbr=T,since=-1812153600000; \
- name=Showa,abbr=S,since=-1357603200000; \
- name=Heisei,abbr=H,since=600220800000
-
-#
-# Taiwanese calendar
-# Minguo since 1911-01-01 00:00:00 local time (Gregorian)
-calendar.taiwanese.type: LocalGregorianCalendar
-calendar.taiwanese.eras: \
- name=MinGuo,since=-1830384000000
-
-#
-# Thai Buddhist calendar
-# Buddhist Era since -542-01-01 00:00:00 local time (Gregorian)
-calendar.thai-buddhist.type: LocalGregorianCalendar
-calendar.thai-buddhist.eras: \
- name=BuddhistEra,abbr=B.E.,since=-79302585600000
-calendar.thai-buddhist.year-boundary: \
- day1=4-1,since=-79302585600000; \
- day1=1-1,since=-915148800000
-
-#
# Hijrah calendars
#
calendar.hijrah.Hijrah-umalqura: hijrah-config-umalqura.properties
--- /dev/null 2014-07-17 12:15:34.526867305 +0900
+++ new/test/java/util/Calendar/Bug8048123.java 2014-07-22 14:44:20.946389611 +0900
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import static java.util.GregorianCalendar.*;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/*
+ * Usage:
+ * java Bug8048123
+ *
+ * -s prints start time for a test era
+ * -e prints the English name of the last predefined era
+ *
+ * java -Djdk.calendar.japanese.supplemental.era=... Bug8048123
+ * -t executes tests with a valid property value
+ * -b
+ * executes tests with an invalid property value
+ * must be the output with -e
+ */
+
+public class Bug8048123 {
+ private static final Locale WAREKI_LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese");
+
+ private static int errors = 0;
+
+ public static void main(String[] args) {
+ // args[0] is a flag.
+ switch (args[0]) {
+ case "-s":
+ // print the start time of the new era for testing
+ Calendar cal = new Calendar.Builder()
+ .setCalendarType("japanese")
+ .setTimeZone(TimeZone.getTimeZone("GMT"))
+ .setDate(200, FEBRUARY, 11)
+ .build();
+ System.out.println(cal.getTimeInMillis());
+ break;
+
+ case "-e":
+ // print the current era name in English
+ Calendar jcal = new Calendar.Builder()
+ .setCalendarType("japanese")
+ .setFields(YEAR, 1, DAY_OF_YEAR, 1)
+ .build();
+ System.out.println(jcal.getDisplayName(ERA, LONG, Locale.US));
+ break;
+
+ case "-t":
+ // test with a valid property value
+ testProperty();
+ break;
+
+ case "-b":
+ // test with an invalid property value
+ // args[1] is the current era name given by -e.
+ testValidation(args[1].replace("\r", "")); // remove any CR for Cygwin
+ break;
+ }
+ if (errors != 0) {
+ throw new RuntimeException("test failed");
+ }
+ }
+
+ private static void testProperty() {
+ Calendar jcal = new Calendar.Builder()
+ .setCalendarType("japanese")
+ .setFields(YEAR, 1, DAY_OF_YEAR, 1)
+ .build();
+ Date firstDayOfEra = jcal.getTime();
+
+ jcal.set(ERA, jcal.get(ERA) - 1); // previous era
+ jcal.set(YEAR, 1);
+ jcal.set(DAY_OF_YEAR, 1);
+ Calendar cal = new GregorianCalendar();
+ cal.setTimeInMillis(jcal.getTimeInMillis());
+ cal.add(YEAR, 199);
+ int year = cal.get(YEAR);
+
+ SimpleDateFormat sdf;
+ String s;
+
+ // test long era name
+ sdf = new SimpleDateFormat("GGGG y-MM-dd", WAREKI_LOCALE);
+ s = sdf.format(firstDayOfEra);
+ if (!"NewEra 1-02-11".equals(s)) {
+ System.err.printf("GGGG y-MM-dd: got=\"%s\", expected=\"NewEra 1-02-11\"%n", s);
+ errors++;
+ }
+
+ // test era abbreviation
+ sdf = new SimpleDateFormat("G y-MM-dd", WAREKI_LOCALE);
+ s = sdf.format(firstDayOfEra);
+ if (!"N.E. 1-02-11".equals(s)) {
+ System.err.printf("GGGG y-MM-dd: got=\"%s\", expected=\"N.E. 1-02-11\"%n", s);
+ errors++;
+ }
+
+ // confirm the gregorian year
+ sdf = new SimpleDateFormat("y", Locale.US);
+ int y = Integer.parseInt(sdf.format(firstDayOfEra));
+ if (y != year) {
+ System.err.printf("Gregorian year: got=%d, expected=%d%n", y, year);
+ errors++;
+ }
+ }
+
+ private static void testValidation(String eraName) {
+ Calendar jcal = new Calendar.Builder()
+ .setCalendarType("japanese")
+ .setFields(YEAR, 1, DAY_OF_YEAR, 1)
+ .build();
+ if (!jcal.getDisplayName(ERA, LONG, Locale.US).equals(eraName)) {
+ errors++;
+ String prop = System.getProperty("jdk.calendar.japanese.supplemental.era");
+ System.err.println("Era changed with invalid property: " + prop);
+ }
+ }
+}
--- /dev/null 2014-07-17 12:15:34.526867305 +0900
+++ new/test/java/util/Calendar/Bug8048123.sh 2014-07-22 14:44:21.362892841 +0900
@@ -0,0 +1,73 @@
+#
+# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+# @test
+# @bug 8048123
+# @summary Test for jdk.calendar.japanese.supplemental.era support
+# @build Bug8048123
+# @run shell Bug8048123.sh
+
+PROPERTY=jdk.calendar.japanese.supplemental.era
+STATUS=0
+
+# get the start time of the fictional next era
+SINCE=`${TESTJAVA}/bin/java -cp "${TESTCLASSES}" Bug8048123 -s`
+
+echo "Tests with valid property values..."
+for P in "name=NewEra,abbr=N.E.,since=$SINCE" \
+ "name = NewEra, abbr = N.E., since = $SINCE"
+do
+ if ${TESTJAVA}/bin/java ${TESTVMOPTS} -cp "${TESTCLASSES}" \
+ -D$PROPERTY="$P" Bug8048123 -t; then
+ echo "$P: passed"
+ else
+ echo "$P: failed"
+ STATUS=1
+ fi
+done
+
+# get the name of the current era to be used to confirm that
+# invalid property values are ignored.
+ERA=`${TESTJAVA}/bin/java -cp "${TESTCLASSES}" Bug8048123 -e`
+
+echo "Tests with invalid property values..."
+for P in "foo=Bar,name=NewEra,abbr=N.E.,since=$SINCE" \
+ "=NewEra,abbr=N.E.,since=$SINCE" \
+ "=,abbr=N.E.,since=$SINCE" \
+ "abbr=N.E.,since=$SINCE" \
+ "name=NewEra,since=$SINCE" \
+ "name=,abbr=N.E.,since=$SINCE" \
+ "name=NewEra,abbr=,since=$SINCE" \
+ "name=NewEra,abbr=N.E." \
+ "name=NewEra,abbr=N.E.,since=0" \
+ "name=NewEra,abbr=N.E.,since=9223372036854775808" # Long.MAX_VALUE+1
+do
+ if ${TESTJAVA}/bin/java ${TESTVMOPTS} -cp "${TESTCLASSES}" \
+ -D$PROPERTY="$P" Bug8048123 -b "$ERA"; then
+ echo "$P: passed"
+ else
+ echo "$P: failed"
+ STATUS=1
+ fi
+done
+exit $STATUS