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的数值,和JavaLocalDate的月份规则一致,无需额外转换。 - 纳秒转毫秒:目标格式保留三位毫秒,
LocalTime会自动将纳秒值截断为三位(如335000000纳秒会转为335毫秒)。 - 时区兼容:如果上游返回非UTC时区,代码依然有效,格式化时会自动转换为对应的UTC时间并带上Z标识(因为目标格式指定了'Z',确保输出为UTC标准格式)。
内容的提问来源于stack exchange,提问作者adamkwn
相关产品推荐
相关产品推荐

