Edited at

Javaで新元号に対応する

More than 3 years have passed since last update.

新元号に対応したjdkは今後リリースされるはずだけど、そんな簡単にアップデートできない方(自分含む)に。


前提


  • Oracle JDK 8u92

  • $JAVA_HOME設定済み

  • 諸々文字コードはutf-8


確認用コード


JapaneseEra.java

import java.text.*;

import java.util.*;

public class JapaneseEra {
public static void main(String[] args) throws ParseException {
DateFormat df = new SimpleDateFormat("GGGGy年M月d日", new Locale("ja", "JP", "JP"));
System.out.println(df.format(new Date(System.currentTimeMillis())));
System.out.println(df.parse("平成28年7月14日"));
df.setLenient(false);
System.out.println(df.parse("平成28年7月14日"));
}
}


対応前に実行すると、

$ javac JapaneseEra.java; java JapaneseEra

平成28年7月14日
Thu Jul 14 00:00:00 JST 2016
Thu Jul 14 00:00:00 JST 2016

まあそうですね、と。


新元号に対応する


仮に


  • 新元号は「機板」

  • 2014/03/15 09:00:00+0900から


その1. 新元号の開始日時をエポックミリ秒にする

1394841600000。

「unix timestamp」なんかでググるか、java.util.Date#getTime()なんかでも変換できるはず。


その2. 新元号をUnicodeに変換する

$ echo "機板" | native2ascii

\u6a5f\u677f


その3. $JAVA_HOME/jre/lib/calendars.properties を編集

calendar.japanese.erasに平成までの定義があるから、そこに新元号を追加。


  • nameはその2.のUnicode

  • abbrは略称

  • sinceはその1.のエポックミリ秒
    編集前後のdiffは

        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=Heisei,abbr=H,since=600220800000; \
+ name=\u6a5f\u677f,abbr=K,since=1394841600000

#
# Taiwanese calendar


その4. 確認用コードを再実行する

$ javac JapaneseEra.java; java JapaneseEra

機板3年7月14日
Thu Jul 14 00:00:00 JST 2016
Exception in thread "main" java.text.ParseException: Unparseable date: "平成28年7月14日"
at java.text.DateFormat.parse(DateFormat.java:366)
at JapaneseEra.main(JapaneseEra.java:10)


  • 現在のエポックミリ秒は新元号でフォーマットされる

  • 平成28年は存在しない(ことになった)から、不正な日付を厳密にチェックするとこける


その他


  • Java6でも同じ方法で大丈夫

  • Tomcat等起動中のプロセスに反映するのは再起動が必要だった気がする

  • 文字化けする場合はnative2asciiの時に-encodingつけるとか


追記

確認用コードでは元号の妥当性を確認するのにDateFormat#setLenient(false)ってしてるけど、つまり今の元号で書かれた未来の日付は改元後にパースできなくなる可能性がある。

パース対象によって厳密なチェックが必要か、あるいはDateFormat#setLenient(false)で可能かどうか要件に応じて決める必要がある。