关于Spring启动:DateTimeFormatter.ISO_OFFSET_DATE_TIME无法按预期工作

DateTimeFormatter.ISO_OFFSET_DATE_TIME not working as expected

基于此Java日期时间-OffsetDateTime.format()示例文章和DateTimeFormatter的此官方文档,我希望我的OffsetDateTime被序列化为2011-12-03T10:15:30+00:00,其中,自从UTC以来,偏移量为+00:00

我无法使OffsetDateTime具有偏移量进行渲染,它始终仅以'Z'Zulu进行渲染。我在做什么错?

enter image description here

这是Spring Boot 2.0.0.RELEASE,正如您在屏幕快照中所见,我在类路径上具有以下模块并向objectMapper注册,尽管我认为这不是相对的,因为似乎存在此问题直接与DateTimeFormatter一起使用,我的对象映射器只是使用我提供的格式化程序。

它确实有影响,因为正如您在第二张屏幕截图中看到的那样,当我指定BASIC_ISO_FORMAT时,它确实会产生不同的结果。

我确实在我的application.properties中将此属性设置为spring.jackson.date-format= com.fasterxml.jackson.databind.util.ISO8601DateFormat,但是据我所知,这对OffsetDateTime没有影响,它仅支持Java早期版本中的旧Date对象。顺便更改此似乎没有预期的影响。

任何帮助将不胜感激。

使用ISO_ZONED_DATE_TIME格式...
Using ISO_ZONED_DATE_TIME format

使用BASIC_ISO_FORMAT ...确实会产生影响,所以我知道格式化程序正在执行某些操作,但我不清楚为什么ISO_ZONED_DATE_TIME未按预期呈现。
Using BASIC_ISO_FORMAT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 this.objectMapper = objectMapperBuilder.build()      
      .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
      .disable(SerializationFeature.INDENT_OUTPUT)
      .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
      .disable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);

 SimpleModule simpleModule = new SimpleModule();
 simpleModule.addSerializer(OffsetDateTime.class, new JsonSerializer<OffsetDateTime>() {
        @Override
        public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            String formattedDate = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(offsetDateTime);
            jsonGenerator.writeString(formattedDate);
        }
    });
this.objectMapper.registerModule(simpleModule);

OffsetDateTime now = OffsetDateTime.now();
TimeZone defaultTimeZone = TimeZone.getDefault();
ZoneId defautlZoneOffset = ZoneOffset.systemDefault();
String serializedOffsetDateTime = this.objectMapper.writeValueAsString(now);
//returns ->"2018-06-27T11:45:56.035Z"


如您所知,将偏移量零(或Zulu)格式化为Z应该有效。它是您使用的标准格式ISO 8601的一部分。但是,如果没有,则:

1
2
3
4
5
6
7
8
    static DateTimeFormatter formatter
            = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssxxx");

    @Override
    public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        String formattedDate = formatter.format(offsetDateTime);
        jsonGenerator.writeString(formattedDate);
    }

这将序列化OffsetDateTime,例如2011-12-03T10:14:30+00:00(在标准中也允许)。格式模式字符串中的小写字母x设置偏移量的格式,而对于零位偏移不求助于Z

上面是格式化程序的简短直接声明。由于我希望尽可能地依赖更高级别的标准元素,因此我考虑使用更长的变体:

1
2
3
4
    static DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
            .appendPattern("xxx")
            .toFormatter();

这将重用标准库中的ISO_LOCAL_DATE_TIME。如果您的时间没有几分之一秒,那么输出将是相同的。如果已包含,则将在后一种情况下将其包括在序列化中,就像使用ISO_OFFSET_DATE_TIME(例如,2011-12-03T10:14:30.1234+00:00)一样。

语言:维基百科文章:ISO 8601


功能,而非错误

DateTimeFormatter. ISO_OFFSET_DATE_TIME的文档告诉您在使用零偏移量(UTC本身)时期望使用Z而不是+00:00

那个医生说:

The format consists of:

? The ISO_LOCAL_DATE_TIME

? The offset ID.

跟随第二项的链接,偏移ID。该页面显示:

There are three formats:

? Z - for UTC (ISO-8601)

? +hh:mm or -hh:mm - if the seconds are zero (ISO-8601)

? +hh:mm:ss or -hh:mm:ss - if the seconds are non-zero (not ISO-8601)

Z是ISO 8601标准的一部分。任何体面的日期时间库都应该能够使用Z

解析字符串。

如果您坚持使用+00:00,而不是更常见的Z,则需要指定自定义格式设置模式,如Ole V.V.

链接的问题中所讨论的那样。
没有-00:00之类的东西

offset is -00:00 since its UTC.

您在问题中对-00:00的使用不正确。 ISO 8601标准明确禁止使用该值。 UTC本身的偏移量为正零,不是负零:+00:00

有RFC 3339宣称自己是ISO 8601的"配置文件"。RFC在允许使用-00:00并为其分配未知偏移量的含义方面违反了ISO 8601。恕我直言,这是一个非常贫穷和不明智的选择,令人困惑和不必要。 ISO 8601已经说明了未知的偏移量:只需完全省略偏移量符号即可。

我建议避免同时使用负零和RFC3339。