该parse
方法需要String
特定格式的 a,例如2007-12-03T10:15:30+01:00[Europe/Paris]
. 由于您的输入格式不同,因此您需要一个DateTimeFormatter
.
需要注意的一个细节是 API 使用IANA 时区名称(始终采用格式Continent/City
,例如America/Sao_Paulo
或Europe/Berlin
)。避免使用 3 个字母的缩写(如CST
or 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"));