使用分区时间将字符串转换为 LocalDateTime

Convert String to LocalDateTime with zoned time

我在主题中收到了一个带有日期和偏移量的字符串字段,我需要通过添加偏移量将此字符串转换为 LocalDateTime。例如,如果我收到:

2021-07-20T19:00:00.000+02:00

我想在 LocalDateTime 中转换:

2021-07-20T21:00:00.000

为此,我有一个带有自定义对象映射器的 Bean:

@Configuration
public class MyConfiguration {

     @Bean
    public MyCustomObjectMapper configure() {

        final ObjectMapper mapper = new ObjectMapper();
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX");
        final LocalDateTimeDeserializer dateTimeDeserializer = new LocalDateTimeDeserializer(formatter);
        final LocalDateTimeSerializer dateTimeSerializer = new LocalDateTimeSerializer(formatter);
        
        final JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addDeserializer(LocalDateTime.class, dateTimeDeserializer);
        javaTimeModule.addSerializer(LocalDateTime.class, dateTimeSerializer);
        mapper.registerModule(javaTimeModule);

        return new MyCustomObjectMapper (mapper);
    }

}

但它并没有像我预期的那样工作,因为生成的 LocalDateTime 偏移量消失了并且没有被添加:

2021-07-20T19:00:00.000

我怎样才能实现这个目标?

LocalDateTime class 是一种 date-time 表示,它不知道时区,LocalDateTimeDeserializer 忽略源数据中的任何时区信息是合乎逻辑的.

要考虑时区,您可以使用 InstantDeserializer.OFFSET_DATE_TIME 反序列化器(DateTimeFormatter.ISO_OFFSET_DATE_TIME 实际上是您拥有的源日期时间的格式)并将其结果转换为 LocalDateTime在所需区域内。这可以包装在自定义反序列化器中以便于使用,例如

class SmartLocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
    private final InstantDeserializer<OffsetDateTime> delegate = InstantDeserializer.OFFSET_DATE_TIME;

    public SmartLocalDateTimeDeserializer() {
        super(LocalDateTime.class);
    }

    @Override
    public LocalDateTime deserialize(JsonParser p,
                                     DeserializationContext ctxt) throws IOException, JsonProcessingException {
        final OffsetDateTime result = delegate.deserialize(p, ctxt);
        return result.atZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime();
    }
}

...

javaTimeModule.addDeserializer(LocalDateTime.class, new SmartLocalDateTimeDeserializer());