From e046c67f64def8bb29e3a4a9d428b79b55cf6021 Mon Sep 17 00:00:00 2001 From: zhoupeng <812968653@qq.com> Date: Sat, 5 Sep 2026 17:39:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimestampLocalDateTimeDeserializer.java | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java index 5bf5d6c..9e02aa0 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/TimestampLocalDateTimeDeserializer.java @@ -1,27 +1,80 @@ package cn.iocoder.yudao.framework.common.util.json.databind; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import java.io.IOException; import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; /** * 基于时间戳的 LocalDateTime 反序列化器 * + * 支持两种 JSON 写法: + * 1. 时间戳:13 位毫秒(yudao 前端默认),兼容 10 位秒级时间戳 + * 2. 日期字符串:"yyyy-MM-dd HH:mm:ss"、"yyyy-MM-dd"、ISO-8601("yyyy-MM-dd'T'HH:mm:ss") + * + * 注意:原实现直接 {@code p.getValueAsLong()},传入 "yyyy-MM-dd HH:mm:ss" 字符串时 + * 会静默解析失败并返回 0,导致入库时间变成 1970-01-01 08:00:00(东八区 epoch 0)。 + * * @author 老五 */ public class TimestampLocalDateTimeDeserializer extends JsonDeserializer { public static final TimestampLocalDateTimeDeserializer INSTANCE = new TimestampLocalDateTimeDeserializer(); + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - // 将 Long 时间戳,转换为 LocalDateTime 对象 - return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault()); + // 1. 数字:直接按毫秒时间戳处理(与原行为一致) + if (p.getCurrentToken() == JsonToken.VALUE_NUMBER_INT) { + return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getLongValue()), ZoneId.systemDefault()); + } + + // 2. 字符串:先尝试时间戳,再尝试常见日期格式 + String text = p.getValueAsString(); + if (text == null) { + return null; + } + text = text.trim(); + if (text.isEmpty()) { + return null; + } + // 2.1 纯数字:13 位毫秒、10 位秒 + if (text.matches("\\d+")) { + long timestamp = Long.parseLong(text); + if (text.length() <= 10) { // 秒级时间戳,转毫秒 + timestamp *= 1000; + } + return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault()); + } + // 2.2 "yyyy-MM-dd HH:mm:ss" + try { + return LocalDateTime.parse(text, DATE_TIME_FORMATTER); + } catch (Exception ignore) { + // 继续尝试其它格式 + } + // 2.3 "yyyy-MM-dd"(按当天 00:00:00) + try { + return LocalDate.parse(text, DATE_FORMATTER).atStartOfDay(); + } catch (Exception ignore) { + // 继续尝试 ISO 格式 + } + // 2.4 ISO-8601:"yyyy-MM-dd'T'HH:mm:ss" + try { + return LocalDateTime.parse(text); + } catch (Exception ignore) { + // 落到最后的报错 + } + throw ctxt.weirdStringException(text, LocalDateTime.class, + "Expected timestamp (millis or seconds) or date string (yyyy-MM-dd HH:mm:ss)"); } }