【问题标题】:Inconsistent locale case sensitivity in Java compared to Bash与 Bash 相比,Java 中的区域设置区分大小写不一致
【发布时间】:2018-05-22 14:19:03
【问题描述】:

当我尝试用波兰语格式化日期时,我得到了一致的格式:

new SimpleDateFormat("EEEE", Locale.forLanguageTag("pl-PL")).format(new Date())

结果

wtorek

bash 中的结果相同:

LC_ALL=pl_PL
$ date +"%A %b %d"
wtorek maj 22

注意wtorek 中的两个小写w

当我为捷克语这样做时,结果不一致:

new SimpleDateFormat("EEEE", Locale.forLanguageTag("cs-CZ").format(new Date())

结果

Pondělí

在 bash 中运行时:

$ LC_ALL=cs_CZ 
$ date +"%A %b %d"
pondělí kvě 21

注意 Java 结果中的大写 P。这是怎么发生的?这是否意味着SimpleDateFormat 不使用系统上安装的标准语言环境?

【问题讨论】:

  • 对于它的价值:在 Java 8 上,使用 Java 自己的语言环境数据,称为 COMPAT,以前称为 JRE,我得到 Úterý,带有重音符号的大写 U。在默认使用 CLDR 的 Java 9 上,我得到带有小 u 的 úterý。没有直接关系,我建议你停止使用SimpleDateFormat,它已经过时并且出了名的麻烦。请改用现代 Java 日期和时间 API java.time
  • 我尝试使用java.time,但不幸的是它遇到了同样的问题。
  • 是的,Vojtěch,java.time 使用相同的语言环境数据,所以如果有任何差异,我会感到惊讶。 SimpleDateFormat 还有很多其他问题,我看不出你为什么要使用它。

标签: java bash date locale simpledateformat


【解决方案1】:

Java 从最多四个来源获取其语言环境数据(包括不同语言环境中星期几的名称)。是的,主机操作系统就是其中之一,但不是默认的。引用the LocaleServiceProvider documentation

Java 运行时环境提供以下四个语言环境提供程序:

  • “CLDR”:基于 Unicode 联盟的 CLDR 项目的提供者。
  • “COMPAT”:表示与以前的 JDK 版本兼容的区域设置敏感服务,直到 JDK8(与 JDK8 相同) “JRE”)。
  • “SPI”:表示实现此 LocaleServiceProvider 类的子类的区域设置敏感服务。
  • “HOST”:在底层操作系统中反映用户自定义设置的提供程序。该提供者可能不是 可用,取决于 Java 运行时环境 实施。
  • “JRE”:表示“COMPAT”的同义词。此名称已弃用,将在 JDK 的未来版本中删除。

Java 8 JRE 是默认设置。我正在使用java.time,因为没有人应该为过时的SimpleDateFormat而烦恼:

    DateTimeFormatter dayOfWeekFormatter 
            = DateTimeFormatter.ofPattern("EEEE", Locale.forLanguageTag("cs-CZ"));
    LocalDate date = LocalDate.now(ZoneId.of("Europe/Prague"));
    System.out.println(date.format(dayOfWeekFormatter));

在我的 Oracle jdk1.8.0_131 上运行的输出与您的结果一致(大写 S):

斯特热达

我们可以通过系统属性控制使用的区域设置数据。例如,要更喜欢 CLDR,可以使用 VM 命令行选项 -Djava.locale.providers=CLDR,COMPAT 运行程序,或者在程序开头插入以下行:

    System.setProperty("java.locale.providers", "CLDR,COMPAT");

斯特热达

现在我们得到小写的s

我在 macOS Sierra 10.12.6 上的 shell 只给出了Wednesday,所以显然我的操作系统没有获得捷克语区域设置数据(这听起来很奇怪;可能问题出在其他地方),因此不适合我。您可以尝试将HOST 放在上述语言环境提供者字符串前面,看看您得到的内容是否与您的bash 一致。

在 Java 9 及更高版本中,CLDR 是默认设置。因此,在 jdk9.0.4 上运行相同的 sn-p 而不设置任何系统属性也会以小写形式显示星期几:

斯特热达

【讨论】:

    【解决方案2】:

    这是否意味着 SimpleDateFormat 不使用系统上安装的标准语言环境

    是的,不使用系统语言环境,可用的语言环境取决于 JVM/JRE 供应商。例如检查 JRE 目录中的lib\ext\localedata.jar。解压后可以找到文件:sun\text\resources\cs\FormatData_cs_CZ.class,反编译成:

    public class FormatData_cs extends ParallelListResourceBundle
    {
        @Override
        protected final Object[][] getContents() {
            return new Object[][] { { "MonthNames", 
            { "ledna", "\u00fanora", "b\u0159ezna", "dubna", "kv\u011btna", "\u010dervna", "\u010dervence", "srpna", "z\u00e1\u0159\u00ed", "\u0159\u00edjna", "listopadu", "prosince", "" } }, 
            { "standalone.MonthNames", { "leden", "\u00fanor", "b\u0159ezen", "duben", "kv\u011bten", "\u010derven", "\u010dervenec", "srpen", "z\u00e1\u0159\u00ed", "\u0159\u00edjen", "listopad", "prosinec", "" } }, 
            { "MonthAbbreviations", { "Led", "\u00dano", "B\u0159e", "Dub", "Kv\u011b", "\u010cer", "\u010cvc", "Srp", "Z\u00e1\u0159", "\u0158\u00edj", "Lis", "Pro", "" } }, 
            { "standalone.MonthAbbreviations", { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "" } }, 
            { "MonthNarrows", { "l", "\u00fa", "b", "d", "k", "\u010d", "\u010d", "s", "z", "\u0159", "l", "p", "" } },
            { "standalone.MonthNarrows", { "l", "\u00fa", "b", "d", "k", "\u010d", "\u010d", "s", "z", "\u0159", "l", "p", "" } }, 
            { "DayNames", { "Ned\u011ble", "Pond\u011bl\u00ed", "\u00dater\u00fd", "St\u0159eda", "\u010ctvrtek", "P\u00e1tek", "Sobota" } }, 
            { "standalone.DayNames", { "ned\u011ble", "pond\u011bl\u00ed", "\u00fater\u00fd", "st\u0159eda", "\u010dtvrtek", "p\u00e1tek", "sobota" } }, 
            { "DayAbbreviations", { "Ne", "Po", "\u00dat", "St", "\u010ct", "P\u00e1", "So" } }, 
            { "standalone.DayAbbreviations", { "ne", "po", "\u00fat", "st", "\u010dt", "p\u00e1", "so" } }, 
            { "DayNarrows", { "N", "P", "\u00da", "S", "\u010c", "P", "S" } }, 
            { "standalone.DayNarrows", { "N", "P", "\u00da", "S", "\u010c", "P", "S" } },
            { "AmPmMarkers", { "dop.", "odp." } }, 
            { "Eras", { "p\u0159.Kr.", "po Kr." } }, 
            { "short.Eras", { "p\u0159. n. l.", "n. l." } }, 
            { "narrow.Eras", { "p\u0159.n.l.", "n. l." } }, 
            { "NumberElements", { ",", " ", ";", "%", "0", "#", "-", "E", "\u2030", "\u221e", "\ufffd" } }, 
            { "TimePatterns", { "H:mm:ss z", "H:mm:ss z", "H:mm:ss", "H:mm" } }, 
            { "DatePatterns", { "EEEE, d. MMMM yyyy", "d. MMMM yyyy", "d.M.yyyy", "d.M.yy" } }, 
            { "DateTimePatterns", { "{1} {0}" } }, 
            { "DateTimePatternChars", "GuMtkHmsSEDFwWahKzZ" } };
        }
    }
    

    并且在“DayNames”中包含“Pond\u011bl\u00ed”。

    【讨论】:

    • 你的源码是来自Oracle还是OpenJDK?
    猜你喜欢
    • 2013-03-11
    • 1970-01-01
    • 2012-11-30
    • 1970-01-01
    • 2014-08-15
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    相关资源
    最近更新 更多