1

SimpleDateFormat を処理する必要がありますが、年の値に問題があります

問題を絞り込むために、以下の簡単な Java コードを書いたところ、明らかに同じ設定で 2 つの異なる結果が返されることがわかりました (コマンド ラインで local を強制しただけです)。問題は Windows (US 構成) マシンのみです。Linux (CentOS) マシンで同じテストを実行すると、すべて問題ありません。

Windows 上の JVM は zulu8 1.8.0_282 openjdk ですが (ただし、Oracle 8 jdk と同じ動作をしているようです)、Linux 上の Red Hat 1.8.0_272 openjdk です。

ソースコードは次のとおりです。

import java.util.Locale;
import java.util.Calendar;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.text.ParseException;

import java.time.LocalDate;
import java.time.temporal.WeekFields;

public class TestDate {
    public static void main(String args[]) throws ParseException {
        Locale currentLocale = Locale.getDefault();

        System.out.println(System.getProperty("java.vendor"));
        System.out.println(System.getProperty("java.version"));
        System.out.println("==============");
        System.out.printf("%20s = %s%n", "getDisplayLanguage", currentLocale.getDisplayLanguage());
        System.out.printf("%20s = %s%n", "getDisplayCountry", currentLocale.getDisplayCountry());
        System.out.printf("%20s = %s%n", "getDisplayVariant", currentLocale.getDisplayVariant());

        System.out.printf("%20s = %s%n", "getLanguage", currentLocale.getLanguage());
        System.out.printf("%20s = %s%n", "getCountry", currentLocale.getCountry());

        System.out.printf("%20s = %s%n", "user.country", System.getProperty("user.country"));
        System.out.printf("%20s = %s%n", "user.language", System.getProperty("user.language"));
        System.out.printf("%20s = %s%n", "user.variant", System.getProperty("user.variant"));

        System.out.println("==============");

        Calendar c = Calendar.getInstance();
        System.out.println("1st day of week / minimal days in 1st week : " + c.getFirstDayOfWeek() + " / " + c.getMinimalDaysInFirstWeek());

        System.out.println("==============");

        LocalDate date1 = LocalDate.of(2020, 12, 31);
        LocalDate date2 = LocalDate.of(2021, 1, 1);

        DateFormat df_date = new java.text.SimpleDateFormat("dd/MM/yyyy");
        DateFormat df_week = new java.text.SimpleDateFormat("YYYY-ww");

        System.out.printf("%20s | %10s | %10s%n", "", df_date.format(java.sql.Date.valueOf(date1)), df_date.format(java.sql.Date.valueOf(date2)));
        System.out.printf("%20s | %10s | %10s%n", "SimpleDateFormat", df_week.format(java.sql.Date.valueOf(date1)), df_week.format(java.sql.Date.valueOf(date2)));

        System.out.printf("%20s | %7d-%02d | %7d-%02d%n", "WeekFields",
                                        date1.get(WeekFields.ISO.weekBasedYear()), date1.get(WeekFields.ISO.weekOfWeekBasedYear()),
                                        date2.get(WeekFields.ISO.weekBasedYear()), date2.get(WeekFields.ISO.weekOfWeekBasedYear()));

    }
}

そして、ここに結果があります(2番目のものは予想されるものです):

>java TestDate
Azul Systems, Inc.
1.8.0_282
==============
  getDisplayLanguage = English
   getDisplayCountry = United States
   getDisplayVariant =
         getLanguage = en
          getCountry = US
        user.country = US
       user.language = en
        user.variant =
==============
1st day of week / minimal days in 1st week : 2 / 4
==============
                     | 31/12/2020 | 01/01/2021
    SimpleDateFormat |    2020-53 |    2020-53
          WeekFields |    2020-53 |    2020-53

>java -Duser.language=en -Duser.country=US -Duser.variant= TestDate
Azul Systems, Inc.
1.8.0_282
==============
  getDisplayLanguage = English
   getDisplayCountry = United States
   getDisplayVariant =
         getLanguage = en
          getCountry = US
        user.country = US
       user.language = en
        user.variant =
==============
1st day of week / minimal days in 1st week : 1 / 1
==============
                     | 31/12/2020 | 01/01/2021
    SimpleDateFormat |    2021-01 |    2021-01
          WeekFields |    2020-53 |    2020-53

どちらも同じロケール設定を使用しているようですが、SimpleDateFormat は異なる週/年を返します。いくつかのロケール設定がありませんか?

ご協力ありがとうございました。

Oracle JDK で編集:

>java TestDate
Oracle Corporation
1.8.0_202
==============
  getDisplayLanguage = English
   getDisplayCountry = United States
   getDisplayVariant =
         getLanguage = en
          getCountry = US
        user.country = US
       user.language = en
        user.variant =
==============
1st day of week / minimal days in 1st week : 2 / 4
==============
                     | 31/12/2020 | 01/01/2021
    SimpleDateFormat |    2020-53 |    2020-53
          WeekFields |    2020-53 |    2020-53

>java -Duser.language=en -Duser.country=US -Duser.variant= TestDate
Oracle Corporation
1.8.0_202
==============
  getDisplayLanguage = English
   getDisplayCountry = United States
   getDisplayVariant =
         getLanguage = en
          getCountry = US
        user.country = US
       user.language = en
        user.variant =
==============
1st day of week / minimal days in 1st week : 1 / 1
==============
                     | 31/12/2020 | 01/01/2021
    SimpleDateFormat |    2021-01 |    2021-01
          WeekFields |    2020-53 |    2020-53

EDIT Calendar default Locale : Scratte が指摘したように、Calendar と SimpleDateFormat はデフォルトの Locale を使用します。SimpleDateFormat のソース コードを調べたところ、デフォルトの Local として使用されていましたが、コードで使用Locale.getDefault(Locale.Category.FORMAT)したものとは異なることが判明しましたLocale.getDefault()

両方のコード間で 2 つの異なる動作があった理由を最終的に理解しました。正しい Locale を表示しませんでした (3 つの異なる Locale を認識していませんでした。これを明確にしてくれて、Ole VV に感謝します)。

TL;DR

SimpleDateFormatを使用してLocale.getDefault(Locale.Category.FORMAT)おり、私の Java コードは の値を表示していLocale.getDefault()ました。後者は常にen_USでしたが、前者は使用したコマンドラインに応じてfr_FRorでした。en_USそのため、週/年に 2 つの異なる出力がありました。

最後に、JVM パラメータ-Duser.language= / -Duser.country= / -Duser.variant=が解決策です (これらは 3 つの異なるロケールすべてを強制します)。

この新しいコードは、3 つの異なるロケールの違いを示しています。

import java.sql.Date;
import java.util.Locale;
import java.util.Calendar;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.text.ParseException;

import java.time.LocalDate;
import java.time.temporal.WeekFields;

public class TestDate {
    public static void main(String args[]) throws ParseException {
        Locale cL = Locale.getDefault();
        Locale cLD = Locale.getDefault(Locale.Category.DISPLAY);
        Locale cLF = Locale.getDefault(Locale.Category.FORMAT);

        System.out.println(System.getProperty("java.vendor"));
        System.out.println(System.getProperty("java.version"));
        System.out.println("==============");
        System.out.printf("%20s | %15s | %15s | %15s%n", "Locale.getDefault(.)", "", "DISPLAY", "FORMAT");
        System.out.printf("%20s | %15s | %15s | %15s%n", "getDisplayLanguage", cL.getDisplayLanguage(), cLD.getDisplayLanguage(), cLF.getDisplayLanguage());
        System.out.printf("%20s | %15s | %15s | %15s%n", "getDisplayCountry", cL.getDisplayCountry(), cLD.getDisplayCountry(), cLF.getDisplayCountry());
        System.out.printf("%20s | %15s | %15s | %15s%n", "getDisplayVariant", cL.getDisplayVariant(), cLD.getDisplayVariant(), cLF.getDisplayVariant());
        System.out.printf("%20s | %15s | %15s | %15s%n", "getLanguage", cL.getLanguage(), cLD.getLanguage(), cLF.getLanguage());
        System.out.printf("%20s | %15s | %15s | %15s%n", "getCountry", cL.getCountry(), cLD.getCountry(), cLF.getCountry());
        System.out.printf("%20s | %15s | %15s | %15s%n", "getVariant", cL.getVariant(), cLD.getVariant(), cLF.getVariant());

        System.out.printf("%20s = %s%n", "user.country", System.getProperty("user.country"));
        System.out.printf("%20s = %s%n", "user.language", System.getProperty("user.language"));
        System.out.printf("%20s = %s%n", "user.variant", System.getProperty("user.variant"));

        System.out.println("==============");

        Calendar c = Calendar.getInstance();
        System.out.println("1st day of week / minimal days in 1st week : " + c.getFirstDayOfWeek() + " / " + c.getMinimalDaysInFirstWeek());

        System.out.println("==============");

        LocalDate date1 = LocalDate.of(2020, 12, 31);
        LocalDate date2 = LocalDate.of(2021, 1, 1);

        DateFormat df_date = new java.text.SimpleDateFormat("dd/MM/yyyy");
        DateFormat df_week = new java.text.SimpleDateFormat("YYYY-ww");

        System.out.printf("%20s | %10s | %10s%n", "", df_date.format(java.sql.Date.valueOf(date1)), df_date.format(java.sql.Date.valueOf(date2)));
        System.out.printf("%20s | %10s | %10s%n", "SimpleDateFormat", df_week.format(java.sql.Date.valueOf(date1)), df_week.format(java.sql.Date.valueOf(date2)));

        System.out.printf("%20s | %7d-%02d | %7d-%02d%n", "WeekFields",
                                        date1.get(WeekFields.ISO.weekBasedYear()), date1.get(WeekFields.ISO.weekOfWeekBasedYear()),
                                        date2.get(WeekFields.ISO.weekBasedYear()), date2.get(WeekFields.ISO.weekOfWeekBasedYear()));

    }
}

対応する出力:

>java TestDate
Azul Systems, Inc.
1.8.0_282
==============
Locale.getDefault(.) |                 |         DISPLAY |          FORMAT
  getDisplayLanguage |         English |         English |          French
   getDisplayCountry |   United States |   United States |          France
   getDisplayVariant |                 |                 |
         getLanguage |              en |              en |              fr
          getCountry |              US |              US |              FR
          getVariant |                 |                 |
        user.country = US
       user.language = en
        user.variant =
==============
1st day of week / minimal days in 1st week : 2 / 4
==============
                     | 31/12/2020 | 01/01/2021
    SimpleDateFormat |    2020-53 |    2020-53
          WeekFields |    2020-53 |    2020-53
>java -Duser.language=en -Duser.country=US -Duser.variant= TestDate
Azul Systems, Inc.
1.8.0_282
==============
Locale.getDefault(.) |                 |         DISPLAY |          FORMAT
  getDisplayLanguage |         English |         English |         English
   getDisplayCountry |   United States |   United States |   United States
   getDisplayVariant |                 |                 |
         getLanguage |              en |              en |              en
          getCountry |              US |              US |              US
          getVariant |                 |                 |
        user.country = US
       user.language = en
        user.variant =
==============
1st day of week / minimal days in 1st week : 1 / 1
==============
                     | 31/12/2020 | 01/01/2021
    SimpleDateFormat |    2021-01 |    2021-01
          WeekFields |    2020-53 |    2020-53
4

1 に答える 1