Instant 是基于 UTC 的不可变时间点,需用 toEpochMilli() 转毫秒数;Duration 仅支持 Instant 运算;Instant.parse() 仅接受 ISO-8601 带时区格式;跨系统传时间优先用 Instant,存库前须匹配字段类型。

Java里用 Instant 表示时间点,不是“当前时间字符串”
很多人一看到 Instant.now() 就以为能直接当“时间戳字符串”用,结果打印出来是 2024-05-12T08:23:45.123Z 这种格式,和数据库或前端要的 1715502225123(毫秒级 long)不一致。
关键在于:Instant 是一个不可变的、基于 UTC 的时间点,它本身不带时区偏移,也不提供格式化能力——它只负责精确到纳秒的时间定位。
需要转成毫秒数就用 instant.toEpochMilli();要转成秒级就用 instant.getEpochSecond();反过来从毫秒构造就用 Instant.ofEpochMilli(1715502225123L)。
Duration 只算两个 Instant 之间的差,不能加到 LocalDateTime 上
Duration 是纯时间段(比如“3小时20分钟”),它只支持和 Instant 或另一个 Duration 运算。常见错误是想对 LocalDateTime 加 Duration:
LocalDateTime now = LocalDateTime.now(); LocalDateTime later = now.plus(Duration.ofHours(3)); // 编译失败!
因为
LocalDateTime 没有时区信息,无法确定“3小时后”对应哪个绝对时刻;而 Instant 可以:Instant now = Instant.now(); Instant later = now.plus(Duration.ofHours(3)); // ✅ 正确
如果真要操作本地时间,得先转成带时区的类型,比如
ZonedDateTime:ZonedDateTime zdt = LocalDateTime.now().atZone(ZoneId.systemDefault()); ZonedDateTime later = zdt.plus(Duration.ofHours(3));
别再用 Date 和 SimpleDateFormat,但要注意 Instant.parse() 的格式限制
Instant.parse() 只接受 ISO-8601 格式,且必须带时区标识(Z 或 +08:00)。传入 "2024-05-12 08:23:45" 或 "2024-05-12T08:23:45" 都会抛 DateTimeParseException。
常见适配方式:
- 如果是无时区的本地时间字符串,先用
LocalDateTime.parse(),再结合时区转成Instant:LocalDateTime ldt = LocalDateTime.parse("2024-05-12T08:23:45"); Instant instant = ldt.atZone(ZoneId.of("Asia/Shanghai")).toInstant(); - 如果原始字符串带空格分隔,用
DateTimeFormatter显式指定:DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime ldt = LocalDateTime.parse("2024-05-12 08:23:45", f);
Instant 不处理“年月日”逻辑,闰秒、夏令时、月份天数这些都交给 ZonedDateTime 或 LocalDateTime 去管。
跨系统传时间优先用 Instant,存数据库前确认字段类型
微服务之间传递时间点,统一用 Instant + JSON 序列化(如 Jackson 默认支持),避免各服务用不同时区解析出错。
但写入数据库时容易踩坑:
- MySQL 的
DATETIME类型不带时区,JDBC 驱动默认按 JVM 时区解释,可能把Instant错转成本地时间;建议显式用PreparedStatement.setObject(idx, instant, JDBCType.TIMESTAMP_WITH_TIMEZONE)(需 MySQL 8.0+ 和 Connector/J 8.0+) - PostgreSQL 的
TIMESTAMP WITH TIME ZONE字段能正确接收Instant,但 Hibernate 6 默认映射为java.time.Instant,5.x 则需手动配置@Column(columnDefinition = "TIMESTAMP WITH TIME ZONE") - 如果数据库字段是
BIGINT存毫秒值,那就老老实实用instant.toEpochMilli()转,别碰字符串中间层










