web-dev-qa-db-ja.com

ドイツ語ロケールのSimpleDateFormat-Java 8 vs Java 10+

legacyアプリケーションにコードとテストケースがあり、次のように要約できます。

@Test
public void testParseDate() throws ParseException {
    String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
    String pattern = "EEE MMM dd HH:mm:ss z Z yyyy";

    DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY);
    Date date = dateFormatter.parse(toParse);

    //skipped assumptions
}

このテストは、Java 8以下に合格します。ただし、Java 10以上の場合、Java.text.ParseException: Unparseable date: "Mo Aug 18 11:25:26 MESZ +0200 2014"になります。

レコードの場合de_DEに加えて、ロケールde_CHde_ATde_LUの例外もスローされます。

日付のフォーマットが JDK 9で変更JEP 252 )であったという事実を知っています。ただし、これは後方互換性を壊す破壊的な変更であると考えています。抜粋:

JDK 9では、Unicodeコンソーシアムの共通ロケールデータリポジトリ(CLDR)データがデフォルトのロケールデータとして有効になっているため、標準のロケールデータをそのまま使用できます。

JDK 8では、CLDRロケールデータはJREにバンドルされていますが、デフォルトでは有効になっていません。

日付、時刻、数値のフォーマットなど、ロケールに依存するサービスを使用するコードは、CLDRロケールデータで異なる結果を生成する場合があります。

曜日に.を追加すると(Mo.)、これを補正し、テストに合格します。ただし、これは古いデータ(XMLなどのシリアル形式)の実際のソリューションではありません。

これを確認すると stackoverflow post 、ドイツ語のロケールではこの動作が意図的であり、Java.locale.providersCOMPATモードで指定することで軽減できるようです。ただし、次の2つの理由から、システムプロパティの値に依存するという考えは好きではありません。

  1. jDKの次のリリースでの変更。
  2. さまざまな環境で忘れられる。

私の質問は:

  • 既存のシリアル化されたデータを書き換え/変更したり、システム環境(Java.locale.providersなど)を書き換えたり変更したりすることなく、異なる環境(アプリケーションサーバー、スタンドアロンjar、...)?
49
rzo

私はそれがニースのソリューションだとは言いませんが、それは方法のようです。

_    Map<Long, String> dayOfWeekTexts = Map.of(1L, "Mo", 2L, "Di", 
            3L, "Mi", 4L, "Do", 5L, "Fr", 6L, "Sa", 7L, "So");
    Map<Long, String> monthTexts = Map.ofEntries(Map.entry(1L, "Jan"), 
            Map.entry(2L, "Feb"), Map.entry(3L, "Mär"), Map.entry(4L, "Apr"),
            Map.entry(5L, "Mai"), Map.entry(6L, "Jun"), Map.entry(7L, "Jul"),
            Map.entry(8L, "Aug"), Map.entry(9L, "Sep"), Map.entry(10L, "Okt"),
            Map.entry(11L, "Nov"), Map.entry(12L, "Dez"));

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendText(ChronoField.DAY_OF_WEEK, dayOfWeekTexts)
            .appendLiteral(' ')
            .appendText(ChronoField.MONTH_OF_YEAR, monthTexts)
            .appendPattern(" dd HH:mm:ss z Z yyyy")
            .toFormatter(Locale.GERMANY);

    String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
    OffsetDateTime odt = OffsetDateTime.parse(toParse, formatter);
    System.out.println(odt);
    ZonedDateTime zdt = ZonedDateTime.parse(toParse, formatter);
    System.out.println(zdt);
_

Oracle JDK 10.0.1で実行されている出力:

_2014-08-18T11:25:26+02:00
2014-08-18T11:25:26+02:00[Europe/Berlin]
_

この場合も、ニースのソリューションは存在しない可能性があります。

_Java.time_、最新のJava日時APIを使用すると、フォーマットと解析の両方のフィールドに使用するテキストを指定できます。そこで、これを悪用して、曜日と月の両方で、古いCOMPATまたはJREロケールデータで使用されていたドットなしの省略形を指定します。必要なマップの作成にJava 9 _Map.of_および_Map.ofEntries_を使用しました。これがJava 8でも機能する場合、2つのマップにデータを入力する他の方法を見つける必要があります。そうすることを信頼しています。

旧式の_Java.util.Date_が必要な場合(おそらくレガシーコードベースで)、次のように変換します。

_    Date date = Date.from(odt.toInstant());
    System.out.println("As legacy Date: " + date);
_

私のタイムゾーンでの出力(ヨーロッパ/コペンハーゲン、おそらくおおよそあなたのものと一致):

_As legacy Date: Mon Aug 18 11:25:26 CEST 2014
_

戦略の提案

それが私なら、この方法で進めることを検討すると思います。

  1. 待機。関連するシステムプロパティをJava内から設定します:System.setProperty("Java.locale.providers", "COMPAT,CLDR");どの環境でも忘れないようにします。 COMPATロケールデータは1.0以降(少なくとも近いと思います)、そこにある多くのコードは(あなただけではなく)それに依存しています。名前はJava 9でJREからCOMPATに変更されました。私にとっては、これはかなり長い間データを保持する計画のように思えます。 アーリーアクセスドキュメント によれば、Java 11(次の「長期サポート」Javaバージョン)で引き続き使用でき、廃止の警告もありません。など。また、将来のJavaバージョンで削除された場合、アップグレードする前に問題に対処できるほど早く見つけることができるでしょう。
  2. 上記のソリューションを使用してください
  3. ロケールサービスプロバイダーインターフェイスを使用して、Basil Bourqueをリンクします。 COMPATデータを将来的に未知の時間に削除する必要がある場合、これがニースのソリューションであることは間違いありません。 COMPATロケールデータを自分のファイルにコピーして、持ち去ることができないようにすることもできます。コピーする前に著作権の問題があるかどうかのみを確認してください。最後にニースのソリューションについて言及する理由は、プログラムを実行できるあらゆる環境でシステムプロパティを設定する必要があることに満足していないということです。私が知る限り、ロケールサービスプロバイダーインターフェイスを介して独自のロケールデータを使用するには、同じシステムプロパティを(異なる値にのみ)設定する必要があります。
19
Ole V.V.

SimpleDateFormatは、BTWがスレッドセーフではない日付をフォーマットする古い方法です。 Java 8以降、Java.timeおよびJava.time.formatと呼ばれる新しいパッケージがあり、これらを使用して日付を処理する必要があります。目的に合わせて、クラス DateTimeFormatter を使用する必要があります。

2
Michael Gantman

Java 8はFr Juni 15 00:20:21 MESZ +0900 2018でしたが、Fr。Juni 15 00:20:21 MESZ +0900 2018 EEEが含まれます。 IS COMPATIBILITY ISSUE。古いバージョンのコードが新しいバージョンで動作しないことは問題ではありません。(翻訳者に申し訳ありません)日付文字列があなたの場合、新しいバージョンのユーザーにはドットを追加する必要があります。ユーザーはJava 8を使用してソフトウェアを使用します。

サブストリング方式を使用することもソフトウェアを遅くすることができます。

    String toParse = "Mo Aug 18 11:25:26 MESZ +0200 2014";
    String str = toParse.substring(0, 2) + "." + toParse.substring(2);
    String pattern = "EEE MMM dd HH:mm:ss z Z yyyy";

    DateFormat dateFormatter = new SimpleDateFormat(pattern, Locale.GERMANY);
    System.out.println(dateFormatter.format(System.currentTimeMillis()));
    Date date = dateFormatter.parse(str);

悪い英語でごめんね。

0
dhkim0800