使用区域设置格式化 LocalDateTime 实例时获取 java.time.DateTimeException

Getting java.time.DateTimeException when formatting LocalDateTime instance with Locale

我不清楚。出于某种原因,当我尝试使用 DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG).withLocale(...) 格式化 LocalDateTime 实例时,出现异常:

java.time.DateTimeException: Unable to extract value: class java.time.LocalDateTime

仅当我使用 FormatStyle.LONG 时才会发生,例如 FormatStyle.MEDIUM 时效果很好。

这是我的测试:

@Test
public void dateTest() {
    LocalDateTime now = LocalDateTime.now();

    // this is ok. prints a value
    System.out.println("LocalDateTime now (formatted with locale): "
            + now.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
                                                       .withLocale(new Locale("it"))));

    // this fails with java.time.DateTimeException: Unable to extract value: class java.time.LocalDateTime
    // only if FormatStyle.LONG (as it is now)
    System.out.println("LocalDateTime now (formatted with locale): "
            + now.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
                                                       .withLocale(new Locale("it"))));
}

有什么好的解释吗?

使用 FormatStyle.LONG 你必须使用 :

ZonedDateTime.now()

而不是:

LocalDateTime.now()

因为 ZonedDateTime 给了你很多细节,不像 LocalDateTime

当您使用 FormatStyle.LONG 时,格式化程序会搜索 ZoneId 等在 LocalDateTime 中找不到的其他信息,因此您会得到异常

tl;博士

Is there any good explanation for that?

是的。

LONGFULL 格式 需要时区 或与 UTC 的偏移量。您的 LocalDateTime 缺少任何区域 或偏移量。

您对 LocalDateTime.now 的使用不正确。您应该只使用 Instant(或 OffsetDateTime/ZonedDateTime)捕捉当前时刻。

Instant.now()  // Capture the current moment as seen in UTC.

为了更灵活地生成字符串,请使用 OffsetDateTimeZonedDateTime

ZonedDateTime.now( 
    ZoneId.of( "Pacific/Auckland" )
)
.format(
    DateTimeFormatter.ofLocalizedDateTime(
        FormatStyle.LONG   // Or `FULL`.
    )
    .withLocale( Locale.ITALY ) 
)

6 marzo 2019 10:22:23 NZDT

并且,FormatStyle.FULL

mercoledì 6 marzo 2019 10:23:25 Ora legale della Nuova Zelanda

LocalDateTime 不是片刻

LocalDateTime class 只是一个日期和时间。它故意缺少任何时区或与 UTC 的偏移量的概念。所以,根据定义,它不能代表一个时刻。

从不调用 LocalDateTime.now()

LocalDateTime.now();

永远不要这样做,永远不要在 LocalDateTime 上调用 now。我想不出任何实际情况会要求这样做。

永远不要在跟踪时刻时使用 LocalDateTimeLocalDateTime 只是一个日期和一天中的时间,仅此而已。没有 time zone or offset-from-UTC 的上下文,LocalDateTime 不能代表一个时刻。它代表了大约 26-27 小时范围内的 潜在 时刻,即全球当前的时区范围。

一个LocalDateTime就像在说“今年1月23日中午”。你是说日本东京还是印度加尔各答的中午?或者法国巴黎?蒙特利尔魁北克?在这些不同的地方,中午发生在不同的时刻,每个时刻都经过了几个小时。

LocalDateTime中的“本地”是指任何地区,或每个地区,但 not 表示任何 特定的 地点。

捕捉当下

要跟踪某个时刻,请使用以下 classes 之一:

  • Instant
    UTC 时刻,始终为 UTC
  • OffsetDateTime
    A moment with an offset-from-UTC, that is, a number of hours-minutes-seconds ahead of or behind the baseline of UTC (the meridian at Royal Observatory 在格林威治。
  • ZonedDateTime
    通过特定地区(时区)人们使用的挂钟时间看到的时刻。

通常,最佳做法是在 UTC 中工作,而忘记您自己的狭隘时区。

Instant instant = Instant.now() ;

如果你想使用某个地区的挂钟时间:

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( instant ) ;

instantzdt 都表示同一时刻,时间轴上的同一点。只有挂钟时间不同。

或者您可以跳过 Instant

ZonedDateTime zdt = ZonedDateTime.now( z ) ;

格式 LONGFULL 需要时区

这两种格式样式:

…两者 都需要时区 作为其显示的一部分。

如上所述,LocalDateTime 对象没有区域或偏移量。所以对这样的对象使用 LONGFUL 格式是没有意义的。

提示: LocalDateTime 在最常见的面向业务的应用程序中通常不是您想要的 class。仅当您清楚地考虑到特定问题时才使用 class,例如在未来预订足够远的约会,以至于您 运行 政客重新定义时区偏移的风险(他们经常做,在大多数政体中)。跟踪特定时刻时,首先考虑使用 Instant