尝试将PDT中的日期时间解析为ZonedDateTime表示

时间:2017-06-25 04:14:52

标签: java datetime timezone datetime-parsing zoneddatetime

我应该如何解析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

3 个答案:

答案 0 :(得分:5)

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

ZonedDateTime.parse(
    "06/24/2017 07:00 AM (PDT)", 
    DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm a (zzz)")
);

答案 1 :(得分:4)

parse method expects a String in a specific format,与2007-12-03T10:15:30+01:00[Europe/Paris]一样。由于您的输入格式不同,因此需要DateTimeFormatter

要注意的一个细节是API使用IANA timezones names(始终采用Continent/City格式,如America/Sao_PauloEurope/Berlin。 避免使用3个字母的缩写(例如CSTPST),因为它们是ambiguous and not standard

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

另一个细节是,在下面的示例中,我在模式中使用了小写hh:格式具有AM / PM指示,因此我认为hh是正确的模式,因为{{3} (当有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 [美国/温哥华]

不清楚输入String来自何处。如果你无法控制它们的格式,那么你别无选择:它们需要以这种方式解析。然后,您可以使用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]

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

// 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"));

答案 2 :(得分:4)

您已收到的错误已在其他答案中详细说明。

  

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

是和否。您的字符串绝对应该被解析为ZonedDateTime。我建议您将其转换为Instant并存储。然后,当您需要根据他/她的时区偏好向用户展示时,您可以再次将Instant转换为ZonedDateTime,或者只使用DateTimeFormatter将其格式化为所需的默认时区。

为什么这样?首先,通常的做法是存储Instant。有些人喜欢自纪元以来只存储几毫秒,我认为这是一些(经常被误解的)性能测量。当然,Instant s LocalDateTime s可以在视线上被破译,至少大致是这样的毫秒。我唯一尊重的另一个选择是,当您确定您的应用程序永远不需要关注某个时区时(这种情况会发生吗?),有时会Instant用于存储。

如果我理解您的情况,您需要存储显示在多个时区的时间点。您不需要存储最初输入时间的时区(如PDT,但PDT实际上不是全时区)。 ZonedDateTime是时区中立的,这是我更喜欢将其存储在某个时区的一个原因,如Instant那样。另外,# screen 在概念上更简单,我的猜测是它也更简单。

这里有几个更好的答案:Best practices with saving datetime & timezone info in database when data is dependant on datetime