试图通过Java的new Date().toString()
支持Javascript的DateTimeFormatter
输出格式,但似乎无法使其正常工作。
Js的输出具有以下性质:
2018年4月4日星期三09:56:16 GMT-0500(美国太平洋标准时间)
2018年4月4日星期三16:12:41 GMT + 0200(CEST)
我当前的格式化程序:
int defaultOffset = ZonedDateTime.now().getOffset().getTotalSeconds();
DateTimeFormatter dtfJs = new DateTimeFormatterBuilder()
.appendPattern("EE MMM dd yyyy HH:mm:ss [OOOO (zzzz)]")
.parseDefaulting(ChronoField.OFFSET_SECONDS,defaultOffset
.toFormatter();
如果我
.parse()
来自js的那些日期字符串,则会出现以下错误:[日期]无法在索引25处解析
提到的两个日期的索引25是:
GMT-0500(南太平洋标准时间)
GMT + 0200(CEST)
我知道问题出在:(冒号),因为如果用
dtfJs
打印当前日期,则会得到:2018年4月4日星期三10:25:10 GMT-05:00(哥伦比亚时间)
因此,在接收到的字符串中,
GMT-05:00
的部分被期望为GMT-0500
,但是我找不到与此匹配的reserved pattern letter。文档说:
偏移量O:此格式可根据
图案字母。一个字母输出本地化的缩写
偏移量,是带有小时的本地化偏移量文本,例如“ GMT”
不带前导零,可选的两位数分钟和秒(如果非零),
和冒号,例如“ GMT + 8”。四个字母输出完整格式,
这是本地化的偏移文本,例如'GMT,带有2位数字的小时和
分钟字段,可选的第二字段(如果非零)和冒号,用于
例如“ GMT + 08:00”。其他任何字母计数
IllegalArgumentException。
偏移Z:这会根据图案数量设置偏移格式
字母。一,二或三个字母输出
小时和分钟,不带冒号,例如“ +0130”。输出将
当偏移量为零时为'+0000'。四个字母输出完整格式
的局部偏移量,相当于四个偏移量O。的
如果偏移量,输出将是对应的本地化偏移量文本
是零。五个字母输出时,分,可选秒
如果非零,则带有冒号。如果偏移为零,则输出“ Z”。六或
更多字母将引发IllegalArgumentException。
这意味着四个字母将始终以冒号“:”输出,从而抛出
DateTimeParseException
非常感谢帮助,谢谢
编辑
感谢@mszymborski,我设法继续验证括号部分“(CEST)”,这里有用的是什么?
我尝试使用
EE MMM dd yyyy HH:mm:ss 'GMT'Z (zz)
,但这仅适用于列表中的第二个日期,而不适用于第一个日期GMT-0500(南太平洋标准时间)错误
GMT + 0200(CEST)通过
最佳答案
JavaScript is a big mess中的日期。 toString()
不仅browser/implementation dependent,而且对语言环境敏感。我在巴西,因此我的浏览器设置为葡萄牙语,并且new Date().toString()
给出以下结果:
2018年4月4日星期三14:14:04 GMT-0300(Hora oficial do Brasil)
月和星期几的名称是英语,但时区名称是葡萄牙语。真是一团糟!
无论如何,要解析这些字符串,您必须做出一些决定。
您需要获取时区还是偏移量?
偏移量GMT + 0200为used by more than one country(因此,使用了多个时区)。尽管偏移量足以具有明确的时间点,但仅了解时区还是不够的。
即使短名称(例如CEST)也不够,因为这也是used by more than 1 country。
如果只想解析偏移量,最好的方法是简单地删除(
之后的所有内容并将其解析为OffsetDateTime
:
DateTimeFormatter parser = DateTimeFormatter.ofPattern("EEE MMM dd yyyy HH:mm:ss 'GMT'Z", Locale.US);
// 2018-04-04T16:12:41+02:00
OffsetDateTime.parse("Wed Apr 04 2018 16:12:41 GMT+0200", parser);
另请注意,我使用了
java.util.Locale
。这是因为星期和星期几是英文,并且如果您未设置语言环境,它将使用JVM默认值-并且您不能保证它始终是英文。如果您知道输入的语言,最好设置一个区域。但是,如果您需要获取时区,则更为复杂。
Names like "CEST" are ambiguous,您需要为它们做出任意选择。使用
java.time
可以构建一组首选时区,以便在出现歧义时使用:Set<ZoneId> zones = new HashSet<>();
zones.add(ZoneId.of("Europe/Berlin"));
zones.add(ZoneId.of("America/Bogota"));
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.appendPattern("EEE MMM dd yyyy HH:mm:ss 'GMT'Z (")
// optional long timezone name (such as "Colombia Time" or "Pacific Standard Time")
.optionalStart().appendZoneText(TextStyle.FULL, zones).optionalEnd()
// optional short timezone name (such as CET or CEST)
.optionalStart().appendZoneText(TextStyle.SHORT, zones).optionalEnd()
// close parenthesis
.appendLiteral(')')
// use English locale, for month, timezones and day-of-week names
.toFormatter(Locale.US);
这样,您可以将输入解析为
ZonedDateTime
:// 2018-04-04T16:12:41+02:00[Europe/Berlin]
ZonedDateTime.parse("Wed Apr 04 2018 16:12:41 GMT+0200 (CEST)", fmt);
// 2018-04-04T10:25:10-05:00[America/Bogota]
ZonedDateTime.parse("Wed Apr 04 2018 10:25:10 GMT-0500 (Colombia Time)", fmt);
不幸的是,这没有解析“ SA Pacific Standard Time”案例。这是因为时区名称是在JVM中内置的,并且“ SA Pacific Standard Time”不是预定义的字符串之一。
一个很好的选择是使用M.Prokhorov在注释中建议的映射:https://github.com/nfergu/Java-Time-Zone-List/blob/master/TimeZones/src/TimeZoneList.java
然后,您手动替换字符串中的名称,并使用
VV
模式(而不是z
)进行解析,因为映射使用IANA的名称(例如Europe/Berlin
,由VV
解析)。但是最好的选择是使用
toISOString()
,它会在ISO8601 format中生成字符串,例如2018-04-04T17:39:17.623Z
。最大的优点是java.time
类可以直接解析它(您无需创建自定义格式化程序):OffsetDateTime.parse("2018-04-04T17:39:17.623Z");