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

Java Spring Boot:将嵌套结构日期时间转为指定格式字符串

解决Spring Boot中嵌套结构日期转ISO 8601格式字符串的问题

上游API返回的valueSetAt是嵌套层级的日期时间结构,要转换成"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"格式的字符串,下面给出两种可行的实现方式:


方式一:先映射实体类再组装转换

1. 创建对应嵌套结构的实体类

根据JSON结构定义实体,用于接收上游返回的数据:

public class ValueSetAt {
    private DateTime dateTime;
    private Offset offset;
    private Zone zone;

    // 自动生成getter、setter方法
}

public class DateTime {
    private DatePart date;
    private TimePart time;

    // 自动生成getter、setter方法
}

public class DatePart {
    private int year;
    private int month;
    private int day;

    // 自动生成getter、setter方法
}

public class TimePart {
    private int hour;
    private int minute;
    private int second;
    private int nano;

    // 自动生成getter、setter方法
}

public class Offset {
    private int totalSeconds;

    // 自动生成getter、setter方法
}

public class Zone {
    private String id;

    // 自动生成getter、setter方法
}

2. 编写转换工具类

将实体类数据组装成ZonedDateTime,再格式化为目标字符串:

import java.time.ZonedDateTime;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class DateConversionUtil {
    // 定义目标格式:保留三位毫秒,UTC时区标识为Z
    private static final DateTimeFormatter TARGET_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

    public static String convertToStartTime(ValueSetAt valueSetAt) {
        // 组装日期部分
        LocalDate date = LocalDate.of(
                valueSetAt.getDateTime().getDate().getYear(),
                valueSetAt.getDateTime().getDate().getMonth(),
                valueSetAt.getDateTime().getDate().getDay()
        );

        // 组装时间部分,直接传入纳秒值,格式化时自动处理为三位毫秒
        LocalTime time = LocalTime.of(
                valueSetAt.getDateTime().getTime().getHour(),
                valueSetAt.getDateTime().getTime().getMinute(),
                valueSetAt.getDateTime().getTime().getSecond(),
                valueSetAt.getDateTime().getTime().getNano()
        );

        // 获取时区信息
        ZoneId zoneId = ZoneId.of(valueSetAt.getZone().getId());

        // 构建ZonedDateTime并格式化
        ZonedDateTime zonedDateTime = ZonedDateTime.of(date, time, zoneId);
        return TARGET_FORMATTER.format(zonedDateTime);
    }
}

方式二:自定义Jackson反序列化器(更高效)

如果用Spring默认的Jackson处理JSON,可以直接自定义反序列化器,跳过实体类映射步骤,直接将valueSetAt解析为ZonedDateTime:

1. 实现自定义反序列化器

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;

import java.io.IOException;
import java.time.ZonedDateTime;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;

public class ValueSetAtDeserializer extends JsonDeserializer<ZonedDateTime> {
    @Override
    public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonNode rootNode = p.getCodec().readTree(p);

        // 解析日期节点
        JsonNode dateNode = rootNode.get("dateTime").get("date");
        LocalDate date = LocalDate.of(
                dateNode.get("year").asInt(),
                dateNode.get("month").asInt(),
                dateNode.get("day").asInt()
        );

        // 解析时间节点
        JsonNode timeNode = rootNode.get("dateTime").get("time");
        LocalTime time = LocalTime.of(
                timeNode.get("hour").asInt(),
                timeNode.get("minute").asInt(),
                timeNode.get("second").asInt(),
                timeNode.get("nano").asInt()
        );

        // 解析时区
        String zoneId = rootNode.get("zone").get("id").asText();

        return ZonedDateTime.of(date, time, ZoneId.of(zoneId));
    }
}

2. 在API响应实体中使用注解

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.time.ZonedDateTime;

public class ApiResponse {
    // 指定反序列化器,直接将valueSetAt转为ZonedDateTime
    @JsonDeserialize(using = ValueSetAtDeserializer.class)
    private ZonedDateTime valueSetAt;

    // 其他字段及getter、setter
}

3. 格式化输出

拿到ZonedDateTime后,直接用之前定义的TARGET_FORMATTER格式化即可得到目标字符串。


注意事项

  • 月份处理:上游返回的month是1-12的数值,和Java LocalDate的月份规则一致,无需额外转换。
  • 纳秒转毫秒:目标格式保留三位毫秒,LocalTime会自动将纳秒值截断为三位(如335000000纳秒会转为335毫秒)。
  • 时区兼容:如果上游返回非UTC时区,代码依然有效,格式化时会自动转换为对应的UTC时间并带上Z标识(因为目标格式指定了'Z',确保输出为UTC标准格式)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 10:02:07