Javascript Date.toString输出的DateTimeFormatter

est*_*con 1 javascript java java-8 java-time

试图new Date().toString()用Java 支持Javascript的输出格式,DateTimeFormatter但似乎无法使其工作.

Js输出具有以下特性:

  • Wed Apr 04 2018 09:56:16 GMT-0500(SA太平洋标准时间)
  • Wed Apr 04 2018 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();
Run Code Online (Sandbox Code Playgroud)

如果我.parse()从js那些日期字符串,我得到以下错误:

[date]无法在索引25处解析

上述两个日期的指数25为:

  • GMT-0500(SA太平洋标准时间)
  • GMT + 0200(CEST)

我知道问题是:(冒号),因为如果我打印当前日期dtfJs,我得到:

Wed Apr 04 2018 10:25:10 GMT-05:00(哥伦比亚时间)

因此GMT-05:00,GMT-0500在收到的字符串中执行部分,但我找不到与此匹配的保留模式字母.

文档说:

偏移O:根据模式字母的数量格式化本地化偏移.一个字母输出本地化偏移的缩写形式,即局部偏移文本,例如'GMT',小时不带前导零,可选2位数分钟,第二个非零,和冒号,例如'GMT + 8 ".四个字母输出完整形式,这是本地化的偏移文本,例如'GMT,具有2位小时和分钟字段,可选的第二字段(如果非零)和冒号,例如'GMT + 08:00'.任何其他字母数量都会抛出IllegalArgumentException.

偏移Z:根据图案字母的数量格式化偏移.一个,两个或三个字母输出

小时和分钟,没有冒号,例如'+0130'.当偏移量为零时,输出将为"+0000".四个字母输出完整形式的局部偏移,相当于Offset-O的四个字母.如果偏移为零,则输出将是相应的本地化偏移文本.五个字母输出小时,分钟,可选第二个,如果非零,则使用冒号.如果偏移量为零,则输出"Z".六个或更多字母抛出IllegalArgumentException.

这意味着四个字母将始终以冒号":"输出,从而抛出 DateTimeParseException

非常感谢,谢谢

编辑

感谢@mszymborski我设法传递验证括号部分"(CEST)",这里有什么用处?

我试过 EE MMM dd yyyy HH:mm:ss 'GMT'Z (zz)但这只适用于列表中的第二个日期,而不是第一个日期

  • GMT-0500(SA太平洋标准时间)错误
  • GMT + 0200(CEST)通过

小智 5

JavaScript 中的日期非常混乱.toString()不仅是浏览器/实现依赖项,还是区域设置敏感.我在巴西,所以我的浏览器设置为葡萄牙语,并new Date().toString()给出了这个结果:

Wed Apr 04 2018 14:14:04 GMT-0300(Hora oficial do Brasil)

月份和星期几的名称是英文,但时区名称是葡萄牙语.真是一团糟!

无论如何,要解析这些字符串,你必须做出一些决定.

你需要获得时区或只是偏移吗?

偏移GMT + 0200 由多个国家使用(因此,不止一个时区使用它).虽然偏移量足以具有非模糊的时间点,但仅知道时区是不够的.

即使像CEST这样的短名称也是不够的,因为这也被超过1个国家使用.

如果你只想解析偏移量,最好的方法是简单地删除所有内容(并将其解析为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);
Run Code Online (Sandbox Code Playgroud)

还要注意我用的是java.util.Locale.这是因为一周中的月份和星期几是英语,如果你没有设置语言环境,它将使用JVM默认值 - 你不能保证它总是英语.如果你知道输入的语言是什么,最好设置一个语言环境.

但是,如果你需要获得时区,那就更复杂了.

像"CEST"这样的名称含糊不清,你需要为它们做出任意选择.用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);
Run Code Online (Sandbox Code Playgroud)

有了这个,您可以将输入解析为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);
Run Code Online (Sandbox Code Playgroud)

但不幸的是,这并没有解析"SA太平洋标准时间"的案例.这是因为时区名称是内置在JVM中的,"SA太平洋标准时间"不是预定义的字符串之一.

一个很好的选择是使用M.Prokhorov在评论中建议的映射:https://github.com/nfergu/Java-Time-Zone-List/blob/master/TimeZones/src/TimeZoneList.java

然后手动替换字符串中的名称并使用VVpattern(而不是z)解析它,因为映射使用IANA的名称(例如Europe/Berlin,解析者VV).


但最好的选择是使用toISOString(),它产生ISO8601格式的字符串,例如2018-04-04T17:39:17.623Z.最大的优点是java.time类可以直接解析它(您不需要创建自定义格式化程序):

OffsetDateTime.parse("2018-04-04T17:39:17.623Z");
Run Code Online (Sandbox Code Playgroud)