You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在毫秒末位为0时截断末尾零?Java日期格式化优化

Jackson序列化OffsetDateTime自动截断末尾零毫秒问题

我编写了一个将Java对象序列化为JSON字符串的示例程序,使用Jackson处理OffsetDateTime类型字段的序列化,但遇到了毫秒格式的问题:当毫秒末位为0时,输出会保留多余的0(比如2024-01-29T17:23:30.290Z),我期望自动截断末尾的零,输出2024-01-29T17:23:30.29Z;而毫秒末位非0时(比如2024-01-29T17:23:30.821Z)输出正常,不需要修改。

原程序代码

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import io.vertx.core.json.jackson.DatabindCodec;

public class Main {

    public static void main(String[] args) throws JsonProcessingException {

        ObjectMapper objectMapper = DatabindCodec.mapper();
        JavaTimeModule module = new JavaTimeModule();

        module.addSerializer(OffsetDateTime.class, new JsonSerializer<OffsetDateTime>() {
            @Override
            public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, SerializerProvider arg2)
                    throws IOException, JsonProcessingException {
                jsonGenerator.writeString(
                        DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(offsetDateTime));

            }
        });

        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        objectMapper.setDateFormat(dateFormatter);
        objectMapper.registerModule(module);

        TestOffsetDateTime s1 = new TestOffsetDateTime();

        System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(s1));

    }

    public static class TestOffsetDateTime {

        private OffsetDateTime offSetField;
        
        public TestOffsetDateTime() {
            this.offSetField = OffsetDateTime.now();
        }

        public OffsetDateTime getOffSetField() {
            return offSetField;
        }

        public void setOffSetField(OffsetDateTime offSetField) {
            this.offSetField = offSetField;
        }
    }

}

原输出结果

{
  "offSetField" : "2024-01-29T17:23:30.290Z"
}

需求说明

  • 可接受的输出格式:
    • 2024-01-29T17:23:30.821Z(毫秒三位非零)
    • 2024-01-29T17:23:30.29Z(毫秒末位0截断为两位)
  • 需要修正的情况:
    • 2024-01-29T17:23:31.820Z → 期望输出 2024-01-29T17:23:31.82Z
    • 2024-01-29T17:23:31.290Z → 期望输出 2024-01-29T17:23:31.29Z

解决方案

问题出在固定模式SSS会强制输出三位毫秒,不管末尾是否为0。我们需要用DateTimeFormatterBuilder构建动态格式化规则,指定毫秒部分最多保留三位,自动截断末尾的零,最少保留一位(若有毫秒值)。

修改后的序列化器代码

替换原程序中OffsetDateTime序列化器的格式化逻辑:

module.addSerializer(OffsetDateTime.class, new JsonSerializer<OffsetDateTime>() {
    @Override
    public void serialize(OffsetDateTime offsetDateTime, JsonGenerator jsonGenerator, SerializerProvider arg2)
            throws IOException, JsonProcessingException {
        // 构建动态格式化器,自动截断毫秒末尾的零
        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .appendPattern("yyyy-MM-dd'T'HH:mm:ss")
                // 指定毫秒字段:最小1位,最大3位,自动截断末尾零
                .appendFraction(ChronoField.MILLI_OF_SECOND, 1, 3, true)
                .appendLiteral("Z")
                .toFormatter();
        jsonGenerator.writeString(formatter.format(offsetDateTime));
    }
});

额外优化

如果你的程序中没有java.util.Date类型的字段,可以移除以下冗余代码,避免不必要的配置:

DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
objectMapper.setDateFormat(dateFormatter);

修改后运行程序,就能得到符合需求的JSON输出,毫秒末尾的零会被自动截断,非零的毫秒部分完整保留。

内容的提问来源于stack exchange,提问作者Starbucks Admin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 08:48:17