Summary of revisions (new era) support by Java version

Introduction

The revised era was announced on May 1, 2019, and the new era was announced as "Reiwa" on April 1. ~~ The announcement of the new era is scheduled for April 1st, a month ago ~~ ** (4/1 review) ** Related articles include "Supporting the new era in Java", but Java SE 8 (hereinafter referred to simply as Java 8) Since it does not support the Date and Time API from Java, and there is a lot of old information in other blogs, I decided to summarize it again so that it is as comprehensive and practical as possible.

** (3/24 postscript) ** Oracle's blog post "A new (Japanese) era for Java! on March 21, 2019 (US time) -era-for-java) "(Japanese translation) has been released. It has been announced that the update release scheduled for April 16, 2019 (US time) will support the new era. This article explains how to deal with it as much as possible even if you cannot upgrade. ** (Reviewed on 4/27) ** Oracle JDK 7u221, 8u211, 11.0.3, which requires a commercial license, has been released, and OpenJDK 7u221, 8u212-b03, 11.0.3 + 7 also reflects the support of the new era "Reiwa". It has also been reflected in Oracle JDK / OpenJDK 12.0.1, which is currently the latest version and is non-LTS (short-term support). Therefore, by upgrading after these, it should be possible to deal with it without much consideration.

Premise

** (Reviewed on 4/17) **

--The standard API, java.util.Date/Calendar, and the [Date and Time API] introduced in Java 8 (https://www.oracle.com/technetwork/jp/articles/java/jf14-date) -time-2125367-ja.html) is handled (third-party libraries are not handled) --For Java 6 or later (Japanese calendar is supported from Java 6 Because of .html) --OpenJDK based distribution (Oracle JDK, AdoptOpenJDK, Azul Zulu, Amazon Corretto /) etc.)

Conclusion (response policy)

Since it will be longer after that, I think that the standard API response policy for revisions will be as follows for each version. ** (Reviewed on 4/17) **

--Java 6, 7 _ (fixed after 7u221) _ --Modify calendars.properties when using java.util.Date / Calendar --Java 8 _ (Fixed after Oracle JDK 8u211, OpenJDK 8u212-b03) _ --Modify calendars.properties when using java.util.Date / Calendar --When using Date and Time API, use the latest version so that the modification of calendars.properties is reflected (first year notation is implemented separately)

** (Reviewed on 4/17) ** Actually, not only the standard API, but also, for example, "During April (even on dates after May), do not print the new era! ”, Etc., may need to be implemented independently according to business requirements, but in principle, this article does not cover it.

Execution example

Of the Japanese calendar correspondence, we aim to basically be able to format and analyze dates that are likely to be commonly used. ** (4/1 review) ** The new era was announced as "Reiwa" and the alphabet was announced as "R", so we reviewed it including execution examples based on this.

$echo Reiwa| native2ascii
\u4ee4\u548c

Therefore, this is basically specified in {JRE_HOME} /lib/calendars.properties.

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;   \
	name=\u4ee4\u548c,abbr=R,since=1556668800000

java.util.Date/Calendarを使った例

It can be said that it is a traditional difficult to use implementation [java.util.Date](https://docs.oracle. com / javase / jp / 8 / docs / api / java / util / Date.html) and java.util.Calendar An example using (/util/Calendar.html) is as follows.

DateCalendarTest.java


import java.text.*;
import java.util.*;

public class DateCalendarTest {

	public static void main(String... args) throws ParseException {
		Locale locale = new Locale("ja", "JP", "JP");
		DateFormat kanjiFormat = new SimpleDateFormat("GGGGy year M month d day", locale);
		DateFormat asciiFormat = new SimpleDateFormat("Gyy.MM.dd", locale);

		//Current date output
		Calendar now = Calendar.getInstance();
		System.out.println(kanjiFormat.format(now.getTime()));
		System.out.println(asciiFormat.format(now.getTime()));

		Calendar cal = Calendar.getInstance();
		cal.clear();

		//Date output before revision
		cal.set(2019, Calendar.APRIL, 30, 23, 59, 59);
		Date lastHeiseiDate = cal.getTime();
		System.out.println(kanjiFormat.format(lastHeiseiDate));
		System.out.println(asciiFormat.format(lastHeiseiDate));

		//Date output after revision
		cal.set(2019, Calendar.MAY, 1, 0, 0, 0);
		Date firstNewEraDate = cal.getTime();
		System.out.println(kanjiFormat.format(firstNewEraDate));
		System.out.println(asciiFormat.format(firstNewEraDate));

		//Non-strict date analysis
		System.out.println(kanjiFormat.parse("May 1, 2019"));
		System.out.println(asciiFormat.parse("H31.05.01"));

		//Strictly an error will occur during analysis
		kanjiFormat.setLenient(false);
		try {
			kanjiFormat.parse("May 1, 2019");
		} catch (ParseException e) {
			System.err.println(e.getMessage());
		}
		asciiFormat.setLenient(false);
		try {
			asciiFormat.parse("H31.05.01");
		} catch (ParseException e) {
			System.err.println(e.getMessage());
		}
	}
}

** (3/26 postscript) ** For reference, Locale ("ja "," JP "," JP ") is the locale that represents the Japanese calendar, but this is a special case for maintaining compatibility, and from Java 7 Locale.forLanguageTag ("ja-JP-u-" It can be specified as ca-japanese ").

The expected behavior at this time should be as follows.

April 1, 2019
H31.04.01
April 30, 2019
H31.04.30
May 1, 1st year of Reiwa
R01.05.01
Wed May 01 00:00:00 JST 2019
Wed May 01 00:00:00 JST 2019
Unparseable date: "May 1, 2019"
Unparseable date: "H31.05.01"

In addition, non-strict date analysis (default) allows even obviously strange date fields such as "13 months" and "-1 day", so depending on the business requirements, as described above, DateFormat # setLenient (false) ) Or you need to check the scope for each field separately. There will be.

Let's see the operation and notes for each version. Java 6 Basically any minor version works as expected. However, even if Locale ("ja "," JP "," JP ") is specified, Kanji characters may be garbled depending on the OS. As far as I tried, it worked correctly when the Windows language setting was Japanese. The language setting of Windows is set to English, and in the macOS X environment, it becomes something like ?? 31? 4? 30?. This seems to be a problem when outputting Japanese to the console in general, but please be careful. Java 7, 8 Basically any minor version works as expected. ** (Reviewed on 4/27) ** Officially supported by Oracle JDK 7u221 / 8u211, OpenJDK 7u221, 8u212-b03 or later.

Java 9 or later

Due to the change by JDK-8048123, calendars.properties disappears and the system property jdk.calendar.japanese.supplemental.era is set. It is designed to be an alternative. Do the following:

$ java -Djdk.calendar.japanese.supplemental.era="name=Reiwa,abbr=R,since=1556668800000" DateCalendarTest

** (Reviewed on 4/27) ** Similar to the Date and Time API described later, Java 11 and later ignores the above system property specifications. In addition, Oracle JDK 11.0.3 and OpenJDK 11.0.3 + 7 or later are officially supported, so you do not need to specify them.

When executed, the result will be as follows.

April 1, 2019
2019.04.01
April 30, 2019
2019.04.30
May 1, 1st year of Reiwa
R01.05.01
Wed May 01 00:00:00 JST 2019
Exception in thread "main" java.text.ParseException: Unparseable date: "H31.05.01"
        at java.base/java.text.DateFormat.parse(DateFormat.java:388)
        at DateCalendarTest.main(DateCalendarTest.java:33)

You've got an exception. Looking at the output before that, the part that should be "H31.04.30" is "Heisei 31.04.30". ("R01.05.01" with explicitly specified system properties is output)

** (1/8 review) ** ~~ I tried Java 12-ea + 25 and 13-ea + 1 (up to the build number in Early Access), but the result is the same, so Report as Bug .com / bugreport /), but at present, it is in a format like "NYY.MM.DD" (JIS X 0301 notation % 97% A5% E6% 9C% AC_ (JIS_X_0301))) is [SimpleDateFormat](https://docs.oracle.com/javase/jp/8/docs/api/java/text/SimpleDateFormat] after Java 9 It is better to think that .html) cannot be formatted and analyzed. ~~ ~~ There is no problem if you use only the kanji of the era. Also, using the following Date and Time API may be a workaround for the time being. ~~ "[[JDK-8216204] Wrong SimpleDateFormat behavior with Japanese Imperial Calendar](https://bugs.openjdk.java.net/browse/JDK-8216204?focusedCommentId=14234290&page=com.atlassian.jira.plugin.system.issuetabpanels: I received a comment in comment-tabpanel # comment-14234290) ", but it was not a bug but a specification change from Java 9. If you want the output to be the same as Java 8 or earlier, you can set the java.locale.providers system property to something like "COMPAT, CLDR".

$ java -Djava.locale.providers=COMPAT,CLDR -Djdk.calendar.japanese.supplemental.era="name=Reiwa,abbr=R,since=1556668800000" DateCalendarTest

** (1/13 postscript) ** CLDR is not enabled by default in Java 8 and the compatibility setting is -Djava.locale.providers = COMPAT, SPI -3704069.html # JDK-8008577), so if you are interested, you can set it as such.

Reference information

Example using Date and Time API

The Date and Time API introduced in Java 8 is designed based on ISO 8601 to replace or migrate the old java.util.Date and java.util.Calendar. .jp / note / 2019/01/basis-of-iso8601 /). An example using this is as follows. ** (6/15 review) **

DateAndTime8Test.java


import java.util.Locale;
import java.time.LocalDate;
import java.time.chrono.*;
import java.time.format.*;

public class DateAndTime8Test {

	public static void main(String... args) {
		DateTimeFormatter kanjiFormat = DateTimeFormatter.ofPattern("GGGGy year M month d day", Locale.JAPAN);
		DateTimeFormatter asciiFormat = DateTimeFormatter.ofPattern("GGGGGyy.MM.dd", Locale.JAPAN);

		//Current date output
		JapaneseDate today = JapaneseDate.now();
		System.out.println(kanjiFormat.format(today));
		System.out.println(asciiFormat.format(today));

		//Date output before revision
		JapaneseDate lastHeiseiDate = JapaneseDate.of(2019, 4, 30);
		System.out.println(kanjiFormat.format(lastHeiseiDate));
		System.out.println(asciiFormat.format(lastHeiseiDate));

		//Date output after revision
		JapaneseDate firstNewEraDate = JapaneseDate.of(2019, 5, 1);
		System.out.println(kanjiFormat.format(firstNewEraDate));
		System.out.println(asciiFormat.format(firstNewEraDate));

		//Non-strict date analysis
		DateTimeFormatter kanjiParseFormat = kanjiFormat.withChronology(JapaneseChronology.INSTANCE)
				.withResolverStyle(ResolverStyle.LENIENT);
		System.out.println(kanjiParseFormat.parse("May 1, 2019", LocalDate::from));
		//* In English notation"y"If you do not parse with one digit, the era will be up to 11 years+Treated for 100 years
		DateTimeFormatter asciiParseFormat = DateTimeFormatter.ofPattern("GGGGGy.MM.dd", Locale.JAPAN)
				.withChronology(JapaneseChronology.INSTANCE)
				.withResolverStyle(ResolverStyle.LENIENT);
		System.out.println(asciiParseFormat.parse("H31.05.01", LocalDate::from));
		// System.out.println(asciiParseFormat.parse("R01.05.01", LocalDate::from)); //Only after official release

		//Strictly an error will occur during analysis
		kanjiParseFormat = kanjiParseFormat.withResolverStyle(ResolverStyle.STRICT);
		try {
			kanjiParseFormat.parse("May 1, 2019");
		} catch (DateTimeParseException e) {
			System.err.println(e.getMessage());
		}
		asciiParseFormat = asciiParseFormat.withResolverStyle(ResolverStyle.STRICT);
		try {
			asciiParseFormat.parse("H31.05.01");
		} catch (DateTimeParseException e) {
			System.err.println(e.getMessage());
		}
	}
}

Just in case, it is hard-coded like JapaneseEra.HEISEI This part cannot be supported until the release when the revision support is officially entered. ** (Reviewed on 9/27) ** JapaneseEra.REIWA is now public Is from Java 13](https://bugs.openjdk.java.net/browse/JDK-8193826) released in September. By the way, Locale can be omitted in DateTimeFormatter, but it depends on the OS and language settings. Since "Heisei" is output instead of "Heisei", it is recommended to specify it explicitly.

** (6/15 postscript) ** For alphabetical patterns like GGGGGyy.MM.dd, formatting works correctly, but parsing must be released after April, when it was officially released, for dates in the new era DateTimeParseException will occur. Also, do not make GGGGGy.MM.dd and **" y "single digit as explained by @ kazuki43zoo in the comments and article. Please note that up to the 11th year of the era will be treated as +100 years **. It's counterintuitive, but I've confirmed with experts that it's not a bug but [IsoChronology](https://docs.oracle.com/javase/jp/8/docs/api/java/time/ It was concluded that it was probably because the specifications were formulated based on chrono / IsoChronology.html). If you need strict analysis including the number of digits, it may be better to use regular expressions as well.

The expected behavior at this time should be as follows.

April 1, 2019
H31.04.01
April 30, 2019
H31.04.30
May 1, 1st year of Reiwa
R01.05.01
2019-05-01
2019-05-01
Text 'May 1, 2019' could not be parsed: year, month, and day not valid for Era
Text 'H31.05.01' could not be parsed: year, month, and day not valid for Era

こちらも先のjava.util.Date/Calendarの例と同様に、解析には注意が必要です。 The default is ResolverStyle.SMART, "13 March" and "- We do not allow "1 day" etc., but we will not be able to analyze "2020" etc., so I think that it often does not meet the business requirements. Only non-strict date analysis (LENIENT) and strict date analysis (STRICT) are specified here, but you may still need more detailed checks depending on the situation.

Let's see the operation and notes for each version. Note that calendars.properties and jdk.calendar.japanese.supplemental.era system properties of Java 9 or later shall be specified as appropriate in the same way as the example using java.util.Date / Calendar.

Java 8 ** (Reviewed on 4/27) ** Officially supported by Oracle JDK 8u221 and OpenJDK 8u222-b03. 8GA~8u31

Exception in thread "main" java.lang.NullPointerException
        at java.time.chrono.JapaneseEra.privateEraFrom(JapaneseEra.java:271)
        at java.time.chrono.JapaneseDate.toPrivateJapaneseDate(JapaneseDate.java:502)
        at java.time.chrono.JapaneseDate.<init>(JapaneseDate.java:333)
        at java.time.chrono.JapaneseDate.now(JapaneseDate.java:197)
        at java.time.chrono.JapaneseDate.now(JapaneseDate.java:166)
        at DateAndTime8Test.main(DateAndTime8Test.java:13)

Suddenly an exception has occurred. ** (1/15 review) ** ~~ It seems that the Japanese calendar could not be handled correctly at the beginning of Java 8 release. (NullPointerException also occurs in JapaneseDate.of and JapaneseDate.from.) ~~ At the beginning of Java 8 release, due to a bug in JDK-8044671, adding the era name to calendars.properties makes it impossible to handle the Japanese calendar correctly. It seems that it will end up.

8u40~8u151

April 1, 2019
H31.04.01
April 30, 2019
H31.04.30
May 1, 31
301.05.01
2019-05-01
2019-05-01
Text 'May 1, 2019' could not be parsed: year, month, and day not valid for Era
Text 'H31.05.01' could not be parsed: year, month, and day not valid for Era

I want it to be "May 1, 1st year of Reiwa" but it is "May 1st, 31st", and I want it to be "R01.05.01" but it is "301.05.01". It seems that since in calendars.properties is used, but other than that, it cannot be read correctly and the era is treated as "3". ** (1/15 review) ** Due to a bug in JDK-8054214, the added era is output as a numerical value. And it seems.

8u152~8u212

April 1, 2019
H31.04.01
April 30, 2019
H31.04.30
May 1, 1st year of Reiwa
R01.05.01
2019-05-01
2019-05-01
Text 'May 1, 2019' could not be parsed: year, month, and day not valid for Era
Text 'H31.05.01' could not be parsed: year, month, and day not valid for Era

The output was as expected! Therefore, in order to handle the Japanese calendar with Java 8's Date and Time API, it seems better to use a newer version as much as possible.

However, there is one caveat. In the sample code, DateTimeFormatter.ofPattern ("GGGGy year M month d day") and "G" are four [full format](https://docs.oracle.com/javase/jp/8/docs/api/ It was specified by java / time / format / TextStyle.html # FULL), and it was output as "May 1, 1st year of Reiwa". If there are up to 3 "G", it will be treated as short format. However, "R1 May 1st" is output. (No problem until "Heisei")

8u221~ ** (6/15 postscript) ** Officially supported by Oracle JDK 8u221 and OpenJDK 8u222-b03. Therefore, when using the Japanese calendar with the Date and Time API, it is basically better to use later versions. This is because parsing with alphabetical patterns such as GGGGGy.MM.dd will not work correctly for dates in the new era (eg R01.05.01) until later releases.

Let's see how it works in Java 9 and above. Do the following:

$ java -Djdk.calendar.japanese.supplemental.era="name=Reiwa,abbr=R,since=1556668800000" DateAndTime8Test

Java 9 Basically any minor version works as expected. However, Java 9 has the same precautions as 8u152 ~ above, so when writing in Kanji, specify four "GGGG" in the DateTimeFormatter.

Java 10 Basically any minor version works as expected. Even if the DateTimeFormatter is shortened to 3 "G", it has been modified to output "May 1, 1st year of Reiwa".

Java 11, 12, 13 ** (Reviewed 4/27) ** Officially supported by Oracle JDK 11.0.3 / 12.0.1, OpenJDK 11.0.3 + 7 / 12.0.1, and placeholder implementation has been replaced, so the following does not apply ..

April 1, 2019
H31.04.01
April 30, 2019
H31.04.30
May 1, 1st year of the era
N01.05.01
2019-05-01
2019-05-01
Text 'May 1, 2019' could not be parsed: year, month, and day not valid for Era
Text 'H31.05.01' could not be parsed: year, month, and day not valid for Era

"Era" and "N" are output instead of "Reiwa" and "R" specified in the system properties. This is because it is overridden by what was implemented as a placeholder (http://hg.openjdk.java.net/jdk/jdk/rev/6a037fd949e4). (Conversely, it is the same even if you do not specify the jdk.calendar.japanese.supplemental.era system property.) ** (Reviewed on 4/27) ** The new era was revised after it was announced and officially supported by Oracle JDK 11.0.3 / 12.0.1 and OpenJDK 11.0.3 + 7 / 12.0.1. Therefore, the following tips are also unnecessary.

Tips (Tips?)

Until now, it was assumed that the system property jdk.calendar.japanese.supplemental.era was set to "1556668800000", which is considered to start on May 1, 2019, but if you specify a destination even in 1 millisecond, it will be placed. I was able to further override the holder implementation. ** (4/27 postscript) ** We received a comment from @mazkasa, and the display is [CalendarNameProvider](https://docs.oracle.com/javase/jp/8/docs/api/java/util/spi /CalendarNameProvider.html) is a straightforward approach that also considers the locale, and the placeholder implementation will also be replaced.

Let's see how it works in JShell. Start as follows.

$ jshell -s -R-Djdk.calendar.japanese.supplemental.era="name=Reiwa,abbr=R,since=1556668800001"
-> import java.time.chrono.*
-> import java.time.format.*
-> var kanjiFormat = DateTimeFormatter.ofPattern("GGGGy year M month d day", Locale.JAPAN)
-> var asciiFormat = DateTimeFormatter.ofPattern("GGGGGyy.MM.dd", Locale.JAPAN)
-> var firstNewEraDate = JapaneseDate.of(2019, 5, 1)
-> System.out.println(kanjiFormat.format(firstNewEraDate))
May 1, 1st year of Reiwa
-> System.out.println(asciiFormat.format(firstNewEraDate))
R01.05.01
-> /exit

なお、旧来のjava.util.Date/Calendarではオーバーライドできず、プレースホルダ実装のままとなりました。(もちろん翌日の5月2日を指定すれば動きますが意味はありません) The Date and Time API shouldn't even consider specifying such irregular system properties. Therefore, basically, it is better to wait for the fix release after the official issue is announced, but if you really want to use a release that is not officially supported (and Java 11 or later), it is a first aid measure. It may be possible to use it for. ** (Reviewed on 4/27) ** Officially supported by Oracle JDK 11.0.3 / 12.0.1 and OpenJDK 11.0.3 + 7 / 12.0.1, so it is not necessary when using this or later.

Supplement

About the first year notation

In the previous execution example, it was output as "Reiwa 1 year". However, when outputting in Kanji, I think that there are many cases where you want to output like "Reiwa first year".

java.util.Date/Calendarの場合

In the old java.text.DateFormat, use the date formatter that specifies the FULL style. It can be realized with. ** (4/17 postscript) ** Also, if you specify 4 or more "y" like GGGGyyyy year, it will be the same as FULL style.

FirstYearTest.java


import java.text.*;
import java.util.Calendar;
import java.util.Locale;

public class FirstYearTest {
	public static void main(String... args) throws ParseException {
		DateFormat fullFormat = DateFormat.getDateInstance(DateFormat.FULL, new Locale("ja", "JP", "JP"));
		Calendar cal = Calendar.getInstance();
		cal.clear();
		cal.set(2019, Calendar.MAY, 1);
		System.out.println(fullFormat.format(cal.getTime()));
		fullFormat.parse("January 8, 1989"); //There is no problem with analysis, but Heisei is used for convenience.
	}
}

When the above is executed, the output will be something like "May 1, 1st year of Reiwa". ** (Reviewed on 4/27) ** In Java 11 and later, as in the execution example shown above, placeholders were prioritized and became "May 1, 1st year of the era", but Oracle JDK 11.0.3 / 12.0.1, OpenJDK 11.0.3 Officially supported on +7 / 12.0.1.

By the way, if you specify SHORT style etc., the year will be one digit as "R1.05.01", so [JIS X 0301 notation](https://ja.wikipedia.org/wiki/ISO_8601#%E6%97%A5%E6 It is different from% 9C% AC_ (JIS_X_0301)).

For Date and Time API

** (1/13 review) ** It is possible to specify the FULL style with the Date and Time API from Java 8, but unfortunately it does not currently support the "first year" and the output varies depending on the version. ~~ JDK-8068571 does not support "first year", instead DateTimeFormatterBuilder .oracle.com/javase/jp/8/docs/api/java/time/format/DateTimeFormatterBuilder.html) will support it. ** (Reviewed on 4/27) ** Basically, Shaping is Java 11 or later, Analysis is Java 12 or later / blowse / JDK-8210633) will be supported. ~~ It is likely to be backported after 8u221 and 11.0.4, which will be released in mid-July 2019. ~~ Both were backported with Oracle JDK 8u211 / 11.0.3 and OpenJDK 8u212-b03 / 11.0.3 + 7.

FirstYear8Test.java


import java.util.*;
import java.time.chrono.*;
import java.time.format.*;
import java.time.LocalDate;
import java.time.temporal.ChronoField;

public class FirstYear8Test {
	public static void main(String... args) {
		DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
		builder.appendText(ChronoField.ERA, TextStyle.FULL);
		builder.appendText(ChronoField.YEAR_OF_ERA, Collections.singletonMap(1L, "Former"));
		builder.appendLiteral("Year");
		builder.appendValue(ChronoField.MONTH_OF_YEAR);
		builder.appendLiteral("Month");
		builder.appendValue(ChronoField.DAY_OF_MONTH);
		builder.appendLiteral("Day");
		DateTimeFormatter formatter = builder.toFormatter(Locale.JAPAN)
				.withChronology(JapaneseChronology.INSTANCE);

		System.out.println(formatter.format(JapaneseDate.of(2019, 5, 1)));
		formatter.parse("January 8, 1989", LocalDate::from);
	}
}

** (Reviewed on 4/27) ** Therefore, when simply executed, the placeholder implementation of Java 11 or later output "May 1, 1st year of the era", but Oracle JDK 11.0.3 It is officially supported by / 12.0.1, OpenJDK 11.0.3 + 7 / 12.0.1, and "May 1, 1st year of Reiwa" is output. In older versions, the Date and Time API alone cannot support the "first year", so I think it would be easier to replace patterns such as "May 1, 1st year of Reiwa" with regular expressions. I will.

** (3/24 postscript) ** The above specifies that only the first year is converted to Collections.singletonMap (1L," original "). When dealing with other than the first year (such as "2019"), this is not a problem at the time of shaping, but it is necessary to add that amount to the map at the time of analysis. For example, change the relevant part as follows. (It is assumed that the required import statement is specified separately.)

Map<Long, String> yearMap = LongStream.rangeClosed(1, 100).boxed()
        .collect(Collectors.toMap(Function.identity(), String::valueOf));
yearMap.put(Long.valueOf(1), "Former");
builder.appendText(ChronoField.YEAR_OF_ERA, yearMap);

Correspondence to Chinese numerals

** (3/3 postscript) ** If you change the part of DateTimeFormatterBuilder in the above code example, you can support Chinese numerals other than the first year. If you want to support the 100th year of the era, you can replace it as follows.

// 0(〇)Is not used in this example, but set for convenience
String[] kanjiNumBase = {"〇", "one", "two", "three", "four", "Five", "Six", "Seven", "Eight", "Nine"};

// 1(one)From 99(Ninety-nine)Set Chinese numerals up to
Map<Long, String> kanjiNumMap = new HashMap<>();
for (int num = 1; num < 100; num++) {
    if (num < 10) {
        kanjiNumMap.put(Long.valueOf(num), kanjiNumBase[num]);
        continue;
    }

    int tens = num / 10;
    int ones = num % 10;
    StringBuilder kanjiNum = new StringBuilder();
    if (tens > 1) {
        kanjiNum.append(kanjiNumBase[tens]);
    }
    kanjiNum.append("Ten");
    if (ones > 0) {
        kanjiNum.append(kanjiNumBase[ones]);
    }
    kanjiNumMap.put(Long.valueOf(num), kanjiNum.toString());
}

//The year corresponds to the first year and 100 years
Map<Long, String> yearMap = new HashMap<>(kanjiNumMap);
yearMap.put(Long.valueOf(1), "Former");
yearMap.put(Long.valueOf(100), "hundred");

DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendText(ChronoField.ERA, TextStyle.FULL);
builder.appendText(ChronoField.YEAR_OF_ERA, yearMap);
builder.appendLiteral("Year");
builder.appendText(ChronoField.MONTH_OF_YEAR, kanjiNumMap);
builder.appendLiteral("Month");
builder.appendText(ChronoField.DAY_OF_MONTH, kanjiNumMap);
builder.appendLiteral("Day");

DateTimeFormatter formatter = builder.toFormatter(Locale.JAPAN)
        .withChronology(JapaneseChronology.INSTANCE)
        .withResolverStyle(ResolverStyle.LENIENT);

If it is Java 11 or later, you can use this to output something like "April 30, 1991". Also, if it is Java 12 or later, in addition to normal dates such as "January 8, 1989" and "April 30, 1991", when converting to DateTimeFormatter, withResolverStyle (ResolverStyle.LENIENT) By specifying) , non-existent dates such as" December 31, 1997 "and" January 1, 1945 "can be analyzed.

Backport to Java 8/11

** (Reviewed on 4/17) ** With the release on April 16, 2019 (US time), a backport for revisions has been implemented. Therefore, if you can upgrade to a later release, you should be able to support the revision without much consideration.

Correspondence of ligatures

** (Reviewed on 9/28) ** In the new issue, a letter such as "㍻" is assigned Unicode code point "U + 32FF". , Java 13 or later with Unicode 12.1 support, [Unicode normalization](https://docs.oracle.com/javase/ jp / 8 / docs / api / java / text / Normalizer.html) is also supported, and you can check it with the following code.

LigatureNormalizeTest.java


import java.text.Normalizer;

public class LigatureNormalizeTest {
	public static void main(String... args) {
		System.out.println("\u337b"); // ㍻
		System.out.println(Normalizer.normalize("\u337b", Normalizer.Form.NFKD));
		System.out.println(Normalizer.normalize("\u337b", Normalizer.Form.NFKC));

		System.out.println("\u32ff"); // ㋿ (Reiwa ligature)
		System.out.println(Normalizer.normalize("\u32ff", Normalizer.Form.NFKD));
		System.out.println(Normalizer.normalize("\u32ff", Normalizer.Form.NFKC));
	}
}

The result of executing with Oracle OpenJDK 13 is as follows.

$ java -version
openjdk version "13" 2019-09-17
OpenJDK Runtime Environment (build 13+33)
OpenJDK 64-Bit Server VM (build 13+33, mixed mode, sharing)

$ java LigatureNormalizeTest
㍻
Heisei
Heisei
㋿
Reiwa
Reiwa

In my environment macOS Mojave 10.14.6, I was able to confirm that it is normalized and displayed correctly. Ultimately, the display of ligatures depends on OS updates and font support.

Also in Windows 10 (1903), after receiving a comment from @mazkasa, change the code page to "65001" (UTF-8) at the command prompt, and add -Dfile.encoding = UTF-8 to display it. It came to be done. The execution example when using Liberica JDK is as follows.

> chcp 65001
Active code page: 65001

> java -version
openjdk version "13-BellSoft" 2019-09-17
OpenJDK Runtime Environment (build 13-BellSoft+33)
OpenJDK 64-Bit Server VM (build 13-BellSoft+33, mixed mode, sharing)

> java -Dfile.encoding=UTF-8 LigatureNormalizeTest
㍻
Heisei
Heisei
㋿
Reiwa
Reiwa

Note that Unicode 12.1 support, including ligatures, may be backported to the LTS release JDK 8/11, but it's unclear what will happen at the moment.

Summary

The table below, including notes on previous versions above, is as follows. ** (Reviewed on 4/18) **

version java.util.Date/Calendar Date and Time API Common remarks / notes
6 ○:Kanji characters are garbled depending on the OS N/A (incompatible) Releases prior to April 2019 are calendars.properties need modification
7 N/A (incompatible) Releases prior to April 2019 are calendars.properties need modification
8 △:If you use the latest version ◎ Releases prior to April 2019 are calendars.Properties need to be modified, first year notation is implemented separately
9 ◎:Alphabet notation is java.locale.providers required ○:Kanji notation is"GGGG"And pattern specification, first year notation is implemented separately jdk.calendar.japanese.supplemental.era required
10 ◎:Alphabet notation is java.locale.providers required ○:First year notation is implemented separately jdk.calendar.japanese.supplemental.era required
11 or later ○:Initially placeholder implementation, alphabetical notation is java.locale.providers required ○:Supports first year notation, placeholder implementation can be overwritten If you upgrade to a release after April 2019, ◎

** (Reviewed on 9/28) ** Please use JDK 13 released in September 2019 to support Unicode normalization of the ligature "㋿" of Reiwa.

in conclusion

The main idea is to upgrade your JDK / JRE to the latest version after the revision, but if you are using Oracle JDK / JRE 8, Public Updates for commercial users ended in January 2019. Therefore, it is assumed that it may be necessary to switch to Oracle Java SE Subscription or another JDK distribution. Will be done. If the new era was announced sooner (at the latest in early 2019), it might have been officially revised by January 2019 Public Updates, and it might not have been necessary to write this article. .. It is not limited to Java, but we hope that the system that handles the Japanese calendar will be successfully revised.

Recommended Posts

Summary of revisions (new era) support by Java version
Summary of Java support 2018
Java 12 new feature summary
Java 13 new feature summary
Java 10 new feature summary
Java 14 new feature summary
Display View on other apps on Android (Summary of support methods by API version)
[Java] Basic summary of Java not covered by Progate ~ Part 1 ~
[Final version] Check the operation of the Japanese calendar using ICU to support the new era.
[Java] Summary of regular expressions
[Java] Summary of operators (operator)
Object-oriented summary by beginners (Java)
Summary of Java language basics
[Java] Summary of for statements
Summary of Java Math class
[Java] Summary of control syntax
Summary of java error processing
[Java] Summary of design patterns
[Java] Summary of mathematical operations
[Java] Basic summary of Java not covered by Progate ~ Part 2 · List ~
Summary of values returned by the Spliterator characteristics method #java
Switch the version of java installed by SDKMAN when moving directories
[For beginners] Summary of java constructor
[Java version] The story of serialization
Summary of [Java silver study] package
[Java] Output by FormatStyle of DateTimeFormatter
Java "pass by reference" problem summary
Java EE 8 (Jakarta EE 8) New feature summary
Summary of object-oriented programming using Java
[Java Silver] Summary of access modifier points
Summary of in-house newcomer study session [Java]
Memorandum of new graduate SES [Java basics]
[java] Summary of how to handle char
Summary of Docker understanding by beginners ② ~ docker-compose ~
I tried the new era in Java
[Java] Personal summary of conditional statements (basic)
Installation of OMC APM Agent --Java version-
[Java] [Maven3] Summary of how to use Maven3
Introduction of New Generation Java Programming Guide (Java 10)
Java Summary of frequently searched type conversions
Introduction of New Generation Java Programming Guide (Java 11)
Summary of Java Math.random and import (Calendar)
Introduction of New Generation Java Programming Guide (Java 12)
[java] Summary of how to handle character strings
Summary of Java environment settings for myself [mac]
[Java] Dynamic method call by reflection of enum (enum)
[Java] Summary of how to abbreviate lambda expressions
[Java] Access the signed URL of s3 (signed version 2)
Summary of new features added in Deeplearning4J 1.0.0-beta4
Set Java version 11 of Docker Minecraft Paper server
Memorandum of new graduate SES [Java object-oriented edition]
Try the free version of Progate [Java II]
[Note] Java: Speed of List processing by purpose
I touched on the new features of Java 15
Summary of Docker understanding by beginners ① ~ docker run -p ~
Try the free version of Progate [Java I]