Javaで新元号に対応する

  • 74
    いいね
  • 0
    コメント

新元号に対応した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)で可能かどうか要件に応じて決める必要がある。