【问题标题】:When I format a LocalDate to dd.MMM.YYYY I get 01.Jan..2000 with two dots当我将 LocalDate 格式化为 dd.MMM.YYYY 我得到 01.Jan..2000 有两个点
【发布时间】:2021-11-06 02:22:11
【问题描述】:

我正在尝试将 LocalDate 变量格式化为 dd.MMM.YYYY

DateTimeFormatter.ofPattern("dd.MMM.yyyy")

问题是我有一半以上的时间得到两个点。例如01-01-2000 转到01.Jan..2000

我知道为什么我有这个问题,因为三个Ms。当我使用dd.MM.yyyy 时,我可以毫无问题地使用01.01.2000。第三个M就是问题所在。

我该如何解决这个问题?

【问题讨论】:

  • LocalDate 没有关联格式,所以 "它们都是 dd-MM-yyyy 格式" 实际上是一个无效语句。你得到.. 因为MMM 格式使用Jan.,这可能来自本地化
  • 好的,我该如何解决?当我制作 dd.MMMyyyy 时,我的一些变量得到了一个点,例如 01.Jan.2000 和其他例如 02.Feb2000 他们没有得到一个点:(
  • System.out.println(DateTimeFormatter.ofPattern("dd.MMM.yyyy").format(LocalDate.now())); 打印 06.Nov.2021 所以我怀疑它来自默认本地化
  • DateTimeFormatter.ofPattern("dd.MMM.yyyy", Locale.CANADA).format(LocalDate.now()) 将打印 06.Nov..2021 是否来自 JVM 使用的本地化。我发现使用Locale.ENGLISH 会打印06.Nov.2021,但这可能并不可取,具体取决于您要实现的目标,因为您将自己锁定在

标签: java date-formatting java-time


【解决方案1】:

问题的原因是月份的缩写是特定于语言环境的:

  • 在某些语言环境中,有一个点(句点)表示缩写1Locale.CANADA 例如。在其他人中没有;以Locale.ENGLISH 为例。
  • 在点表示缩写的语言环境中,当月份名称不需要缩写时,您可能会发现也可能不会发现点。例如五月的名字只有三个字母,所以May.表示这是一个缩写是不合逻辑的。

有多种方法可以解决这个问题,包括:

  1. 不要修复它。在某些情况下,带有双点的输出在逻辑上是正确的(根据某种逻辑)2,尽管它看起来很奇怪。

  2. 我的首选方法是使用不同的输出格式。 不要使用点作为分隔符。

    使用点字符作为分隔符......非常规......当你将它与缩写的月份名称结合使用时,你会遇到这种尴尬的边缘情况。

    当然有办法解决这个问题,但考虑到如果其他人需要在 他们的 代码库中解析 your 日期,他们可能会遇到同样的问题.

  3. 将您的 DateTimeFormatter 硬连接到现有的 Locale,其中缩写名称中没有点。

    理论上存在他们可能决定更改标准Locale 中的缩写的风险。但怀疑他们会这样做,因为这样的更改可能会破坏隐式依赖于语言环境的客户代码......就像你的那样。

  4. 创建一个自定义 Locale 并在创建 DateTimeFormatter 时使用它。

  5. 使用DateTimeFormatterBuilder 创建格式化程序。要处理月份,请将appendText(TemporalField field, Map<Long,String> textLookup) 与包含准确您要使用的缩写的查找表一起使用。

    根据您“附加”其他字段的方式,您的格式化程序可以完全或部分独立于区域设置。


在我看来,其中 2. 和 5. 是最“正确”的。 Ole 的answer 用代码说明了这些选项。


1 - 见这篇关于美式英语语法的文章 - When you need periods after abbreviations.
2 - 问题在于说服人们“看起来很奇怪但合乎逻辑”比“看起来不错但不合逻辑”更好。我不认为你会赢得这场争论......

【讨论】:

    【解决方案2】:

    Stephen C. 写了一个很好地涵盖了您的选择的答案。作为补充,因为我同意选项 2 和 5 是最正确的,所以我想把这两个拼写出来。

    选项 2:使用不同的格式

    大多数可用语言环境的本地化日期格式都内置在 Java 中。这些通常使用不足。如果依靠 Java 知道如何为我们的受众和他们的语言环境设置日期格式,我们可以为自己节省很多时间。我以德语为例,因为它是在日期部分和缩写之间始终包含点的语言环境之一。以下内容也适用于您的语言环境,即使它不是德语(如果您替换 Locale.getDefault(Locale.Category.FORMAT) 或您用户的语言环境)。

    private static final Locale LOCALE = Locale.GERMAN;
    
    private static final DateTimeFormatter DATE_FORMATTER
            = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
                    .withLocale(LOCALE);
    

    为了演示,我正在格式化当年每个月的一天:

        LocalDate date = LocalDate.of(2021, Month.JANUARY, 16);
        for (int i = 0; i < 12; i++) {
            System.out.println(date.format(DATE_FORMATTER));
            date = date.plusMonths(1).minusDays(1);
        }
    

    输出是:

    16.01.2021
    15.02.2021
    14.03.2021
    13.04.2021
    12.05.2021
    11.06.2021
    10.07.2021
    09.08.2021
    08.09.2021
    07.10.2021
    06.11.2021
    05.12.2021
    

    对于德语语言环境,我们在这里得到数字月份。其他语言环境可能会给出其他结果,例如月份缩写。

    如果您想要不使用数字月份的较长格式,请指定例如 FormatStyle.LONG 而不是 FormatStyle.MEDIUM

    private static final DateTimeFormatter DATE_FORMATTER
            = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)
                    .withLocale(LOCALE);
    
    16. Januar 2021
    15. Februar 2021
    14. März 2021
    13. April 2021
    12. Mai 2021
    11. Juni 2021
    10. Juli 2021
    9. August 2021
    8. September 2021
    7. Oktober 2021
    6. November 2021
    5. Dezember 2021
    

    我建议您的用户对上述其中一项感到满意。

    选项 5:DateTimeFormatterBuilder.appendText(TemporalField, Map)

    如果您的用户告诉您他们不想要上面的本地化格式,并且他们确实想要带有月份缩写和单点的格式 - 它会越来越长,但结果很漂亮,每个人都会很高兴。

    private static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder()
            .appendPattern("dd.")
            .appendText(ChronoField.MONTH_OF_YEAR, getMonthAbbreviations())
            .appendPattern(".uuuu")
            .toFormatter(LOCALE);
    
    private static Map<Long, String> getMonthAbbreviations() {
        return Arrays.stream(Month.values())
                .collect(Collectors.toMap(m -> Long.valueOf(m.getValue()),
                        MyClass::getDisplayNameWithoutDot));
    }
    
    private static String getDisplayNameWithoutDot(Month m) {
        return m.getDisplayName(TextStyle.SHORT, LOCALE)
                .replaceFirst("\\.$", "");
    }
    

    来自与上述相同循环的输出:

    16.Jan.2021
    15.Feb.2021
    14.März.2021
    13.Apr.2021
    12.Mai.2021
    11.Juni.2021
    10.Juli.2021
    09.Aug.2021
    08.Sep.2021
    07.Okt.2021
    06.Nov.2021
    05.Dez.2021
    

    每次一个点。核心技巧是使用 Java 的月份缩写,如果有一个点(Jan. 变为 Jan),则从其中删除点,如果没有点则按原样使用它(Mai 保持 Mai)。我的getDisplayNameWithoutDot 方法就是这样做的。我反过来使用这种方法来构建两个参数appendText(TemporalField, Map&lt;Long, String&gt;) 方法需要并用于格式化的映射。

    【讨论】:

      猜你喜欢
      • 2023-02-22
      • 2011-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-17
      • 1970-01-01
      相关资源
      最近更新 更多