从 Elasticsearch 读取日期时,可能看到这样的文本:
2019-11-20T01:32:22.234Z
末尾的 Z 表示 UTC。它是时间表示的一部分,不意味着这种文本不能传给前端;接口采用什么格式,应由前后端共同约定。
Java 8 及以后优先按时刻解析
对于这里的 ISO 时间文本,可以使用 java.time:
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
String text = "2019-11-20T01:32:22.234Z";
Instant instant = Instant.parse(text);
Date date = Date.from(instant);
String display = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
.withZone(ZoneId.of("Asia/Shanghai"))
.format(instant);
// 2019-11-20 09:32:22.234
Instant 和 Date 表示时刻;选择 Asia/Shanghai 只是决定怎样显示,不是把同一个时刻再向后移动八小时。Java 8 Instant API
带偏移量的输入可根据契约使用 OffsetDateTime 等类型;不要假设所有输入都包含同样位数的小数秒或都以 Z 结尾。
旧 SimpleDateFormat 写法哪里容易出错
旧项目曾使用:
SimpleDateFormat format = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT
);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = format.parse("2019-11-20T01:32:22.234Z");
这里被单引号包住的 Z 只是字面字符,真正的时区来自 setTimeZone。若输入明明表示 UTC,却把解析器设为其他时区,就会得到错误时刻。
该片段只说明固定格式旧写法的机制,并不是完整输入校验器。SimpleDateFormat 也不应被多个线程不加保护地共享;新代码通常更适合使用 java.time。
存储、传输和展示要分开
检查实际 mapping、客户端序列化和接口协议,而不要仅凭一段字符串推断存储方式。核对正确性时比较代表的时刻或时间戳,再比较展示结果;否则“看起来差八小时”可能只是时区显示差异,也可能真是解析错了。