[Java] A new (Japanese) era for Java!

原文はこちら。
https://blogs.oracle.com/java-platform-group/a-new-japanese-era-for-java

2016年に今上天皇が皇太子さまに譲位する予定であることを発表されました。今上天皇は2019年4月30日(平成31年4月30日)に退位される予定で、2019年4月1日に日本の内閣が、2019年5月1日から始まる新しい元号を発表することになっています。
この新しい元号に備えるための手順をとってきましたが、新元号の発表までこのアップデートの最終化は待つ必要があります。
特にJDKにおいてはいくつかの変更があります。
  • グレゴリオ暦の日付を和暦に相当する日付に変換してテキストとして表示する場合、正しい元号名を知っておく必要があります。
  • 逆変換、つまり文字列表記の日付を正しい日時に変換する場合には、元号名称を解析し、どの西暦に相当するのかを知っておく必要があります。
  • Unicodeは、新しい元号名称を表現する合字を新たに追加します。
JDK 8以降、Java APIには新しいDate and Time API(JSR 310を参照)のほか、古いjava.util.Calendarクラスに日付を処理するためのメソッドが追加されました。すべての関連APIは、新しい時代を正しく処理するように更新されてきました。
JSR 310: Date and Time API
https://jcp.org/en/jsr/detail?id=310
新元号に備えて、JDK 12では新元号( “NewEra”)のプレースホルダー名を使用します。JDK 12を使用すると、新元号発表後にリリースされるアップデートで、新元号名がどのように処理されるのかがわかります。
新元号の発表後の次のアップデートリリースは2019年4月16日(PDT)を予定しています。すべてのサポート対象のJDKである7、8、11 LTS、そして12が新しい元号を扱えるようにアップデートされます。
これらのすべてのバージョンの中で、java.util.Calendarクラスは正しい値を理解し、返すためにアップデートされます。
import java.util.*;
import java.text.*;

...

  new SimpleDateFormat("GGGGy年M月d日", 
        Locale.forLanguageTag("ja-JP-u-ca-japanese")).
        format(new Calendar.Builder().
        setDate(2019, Calendar.MAY, 1).build().getTime());

    // before the update: 平成31年5月1日, 
    // after the update: 元号1年5月1日

    // On versions released after new announcement the 元号 will be replaced with the new era name
そしてjava.text.SimpleDateFormatを使った日付のパースは正しく動作します(訳注:現時点ではDataFormatのFULLスタイルを使わなければ変換できません)。
import java.text.*;

...

var sdf = new SimpleDateFormat("GGGGy年M月d日",
            Locale.forLanguageTag("ja-JP-u-ca-japanese"));  
            sdf.setLenient(false);
            new SimpleDateFormat("Y-M-d", Locale.US).format(sdf.parse("元号2年2月1日"));
    // => "2020-2-1"
    // with 元号 replaced with the new era name
JDK 8以後については、JSR 310 Data and Time APIもアップデートします (このAPIはJDK 7には含まれていません)。
import java.time.chrono.*;
import java.time.format.*;
import java.time.temporal.*;

...

    DateTimeFormatter.ofPattern("GGGGy年M月d日").
          withChronology(JapaneseChronology.INSTANCE).
          withLocale(Locale.JAPAN).
          format(JapaneseDate.of(2020, 2, 1));
    // => “元号2年2月1日”

    DateTimeFormatter.ofPattern("u-M-d").format(
          DateTimeFormatter.ofPattern("GGGGy年M月d日").
          withChronology(JapaneseChronology.INSTANCE).
          withLocale(Locale.JAPAN).
          withResolverStyle(ResolverStyle.STRICT).
          parse("元号2年2月1日"));
     // => “2020-2-1”


    JapaneseEra.of(3).getDisplayName(TextStyle.FULL,
          Locale.forLanguageTag("ja-JP-u-ca-japanese"));
    // => “元号”
    // with 元号  replaced with the new era name
JDK 8以後では、Unicodeコードポイント U+32FF がjava.util.Calendar クラスに入ります。このコードポイントは新元号の合字用に予約されています。

まとめると、以下の通りです。
  • 日本の新元号の発表後リリースされるJDKのアップデートで、新元号を正しく取り扱えるようにする
  • 新元号発表までは、JDK 12(そしてJDK 11も)は将来の挙動と同様ではあるものの、元号をプレースホルダー名(*)として使用する。
(*)文字列形式の日付のパースに問題があるため、パースが正しく機能するための4月のアップデートまで待つ必要があります。
[JDK-8217609] New era placeholder not recognized by java.text.SimpleDateFormat
https://bugs.openjdk.java.net/browse/JDK-8217609
(2019/04/01追記)
新元号は令和に決まりました。4月16日のCPU/PSUで反映されていることを確かめましょう…

(2019/04/17追記)
JDK 8u211/212、JDK 11.0.3、JDK 12.0.1のいずれでも、上記コードが正しく動作することを確認しました。プレースホルダーの箇所はもちろん「令和」に置き換わっています。

2 件のコメント:

  1. 元記事にコメントしたのですが、なかなか反映されないのでこちらでも。
    "GGGGy 年M ⽉d ⽇"の"月"や"日"が常用漢字でないので "GGGGy年M月d日" で見直したほうがよいかもしれません。
    あと、元年の例は誤解をしそうなので、"GGGGy年"は"元号1年"とフォーマットされるなど、訳注を追加したほうがよいかもです。
    この辺りのツイートも参考までに。https://twitter.com/yamadamn/status/1109840523404042240

    返信削除
    返信
    1. コメントありがとうございます。修正しておきました。

      削除