3

我应该如何解析 PDT 时区中的这个日期时间值?

06/24/2017 07:00 AM (PDT)

我想维护时区,以便我可以根据网站访问者的偏好表示其他时区的时间。

我尝试使用ZonedDateTime,但出现解析错误:

   java.time.ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)")

错误是:

java.time.format.DateTimeParseException: Text '06/24/2017 07:00 AM (PDT)' could not be parsed at index 0
   at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
   at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
   at java.time.ZonedDateTime.parse(ZonedDateTime.java:597)
   at java.time.ZonedDateTime.parse(ZonedDateTime.java:582)   ... 29 elided

另外,您是否同意我应该使用ZonedDateTime?

4

3 回答 3

5

由于您的格式是非标准的,您需要将其指定给解析器:

ZonedDateTime.parse(
    "06/24/2017 07:00 AM (PDT)", 
    DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm a (zzz)")
);
于 2017-06-25T04:39:31.883 回答
4

您得到的错误已经很好地涵盖在其他答案中。

另外,您是否同意我应该使用ZonedDateTime?

是和不是。您的字符串绝对应该被解析为ZonedDateTime. 我建议您将其转换为Instant并存储它。然后,当您需要根据用户的时区偏好将其呈现给用户时,您可以再次将其转换Instant为 a或使用所需的默认时区将其ZonedDateTime格式化。DateTimeFormatter

为什么要这样做?首先,通常的做法是存储Instants。有些人更喜欢只存储自纪元以来的毫秒数,我认为这是一些(经常被误解的)性能度量。当然,这样的毫秒我很难读懂,而Instants 可以用肉眼破译,至少粗略。我尊重的唯一另一种选择是,当您确定您的应用程序永远不需要关注时区时(这曾经发生过吗?),然后有时LocalDateTime用于存储。

如果我正确理解您的情况,您需要将显示的时间点存储到多个时区。您不需要存储最初输入时间的时区(如 PDT,除了 PDT 不是真正的完整时区)。Instant是时区中性的,这是我更喜欢它而不是将时间存储在某个时区的原因之一ZonedDateTime。anInstant在概念上也更简单,我的猜测是它在实现方面也更简单。

这里有几个更好的答案:当数据依赖于 datetime 时,在数据库中保存日期时间和时区信息的最佳实践

于 2017-06-25T13:18:41.077 回答
4

parse方法需要String特定格式的 a,例如2007-12-03T10:15:30+01:00[Europe/Paris]. 由于您的输入格式不同,因此您需要一个DateTimeFormatter.

需要注意的一个细节是 API 使用IANA 时区名称(始终采用格式Continent/City,例如America/Sao_PauloEurope/Berlin)。避免使用 3 个字母的缩写(如CSTor PST),因为它们模棱两可且不标准

API 使用特定 ID 进行一些例外处理,并为它们提供一些默认值。对于PDT,它默认为America/Los_Angeles

另一个细节是,在下面的示例中,我hh在模式中使用了小写:格式有 AM/PM 指示,所以我认为这hh是正确的模式,因为它的值是从 1 到 12(有 AM/PM 时的常用值指标)。

如果你使用大写HH,它允许从 0 到 23 的值(在 AM/PM 中使用它并不常见),如果输入包含一个小时,它会抛出一个异常07:00 PM

所以代码会是这样的:

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm a (zzz)");
ZonedDateTime z = ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)", fmt);
System.out.println(z);

输出是:

2017-06-24T07:00-07:00[美国/洛杉矶]

但并非所有 3 个字母的时区名称都能被 API 识别并引发异常。

无论如何,PDT 中还有其他时区(例如America/Vancouver) - 您可以通过调用来获取所有时区的列表ZoneId.getAvailableZoneIds()。如果您想使用不同的时区作为默认时区,您可以创建一组首选时区并使用该组构建格式化程序:

Set<ZoneId> preferredZones = new HashSet<>();
// set America/Vancouver as preferred zone
preferredZones.add(ZoneId.of("America/Vancouver"));
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // pattern
    .appendPattern("MM/dd/yyyy hh:mm a (")
    // append timezone with set of prefered zones
    .appendZoneText(TextStyle.SHORT, preferredZones)
    // finish the pattern
    .appendPattern(")")
    // create formatter
    .toFormatter();
System.out.println(ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)", fmt));

API 将使用首选区域集(在本例中为America/Vancouver),而不是默认的 ( America/Los_Angeles)。输出将是:

2017-06-24T07:00-07:00[美国/温哥华]


目前尚不清楚 inputString的来源。如果您无法控制它们的格式,那么您别无选择:它们需要以这种方式解析。然后您可以使用以下方法将其转换为另一个时区withZoneSameInstant

// parse the input string
ZonedDateTime z = ZonedDateTime.parse("06/24/2017 07:00 AM (PDT)", fmt);
// convert to another timezone
ZonedDateTime other = z.withZoneSameInstant(ZoneId.of("America/Sao_Paulo")); // 2017-06-24T11:00-03:00[America/Sao_Paulo]

other的值为2017-06-24T11:00-03:00[America/Sao_Paulo]

但是,如果您可以控制输出,最好 (IMO) 在内部使用 UTC ( java.time.Instant),并且仅在向用户显示时转换为某个时区:

// convert ZonedDateTime to instant
ZonedDateTime z = // parse input
// convert to UTC (Instant is always in UTC)
Instant instant = z.toInstant();
// internally work with instant (as it's always in UTC)

// convert instant to some timezone only when necessary (like displaying to users)
ZonedDateTime converted = instant.atZone(ZoneId.of("Europe/London"));
于 2017-06-25T05:49:31.120 回答