我应该如何解析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
?
答案 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_Paulo
或Europe/Berlin
。
避免使用3个字母的缩写(例如CST
或PST
),因为它们是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。