LocalDate仅存年月日,LocalDateTime存年月日及时分秒,二者均无时区;需表示绝对时刻须用ZonedDateTime或Instant。

LocalDate 和 LocalDateTime 的核心区别在哪
别一上来就套用,LocalDate 只存年月日(不带时分秒、不带时区),LocalDateTime 存年月日 + 时分秒(依然不带时区)。两者都不可变、线程安全,但如果你需要表示“某个时刻”,比如数据库写入时间戳或计算两个时间点的毫秒差,LocalDateTime 不够——它没时区信息,无法映射到真实世界的时间轴。这时候得升维用 ZonedDateTime 或 Instant。
怎么从字符串解析成 LocalDate 或 LocalDateTime
用 parse() 最常见,但格式必须严格匹配默认格式,否则抛 DateTimeParseException。比如 LocalDate.parse("2024-05-20") 没问题,但 "20/05/2024" 就会炸。这时候要显式传 DateTimeFormatter:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date = LocalDate.parse("20/05/2024", formatter);
-
LocalDateTime.parse("2024-05-20T14:30:45")能直接解析 ISO 格式(含T) - 如果字符串只有日期没有时间,别硬塞给
LocalDateTime.parse(),会报错;先用LocalDate.parse()再调atStartOfDay() - 中文格式如“2024年05月20日”必须自定义
DateTimeFormatter,且注意年月日是文字,要用yyyy年MM月dd日,不能写成yyyy-MM-dd
如何把 LocalDate / LocalDateTime 转成时间戳或旧 API 的 Date
它们本身不提供 getTime(),因为没时区。想转 java.util.Date 或毫秒值,必须先指定时区:
// LocalDate → Date(按系统默认时区的当天零点) Date date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant()); // LocalDateTime → Date(按系统默认时区解释该时刻) Date dt = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
- 千万别用
localDateTime.atZone(ZoneOffset.UTC).toInstant().toEpochMilli()来“假装”得到 UTC 时间戳——这其实把你的本地时间强行当 UTC 解释了,结果错 8 小时(东八区) - 如果业务明确要求“所有时间按 UTC 存储”,那应该统一用
Instant接收输入,再转成LocalDateTime.ofInstant(instant, ZoneOffset.UTC)展示 - JDBC 4.2+ 支持直接 set
LocalDateTime到PreparedStatement,但底层驱动是否真正按 UTC 处理,取决于数据库连接参数(如 MySQL 的serverTimezone=UTC)
加减日期和跨月计算要注意什么
plusDays()、minusMonths() 看似简单,但跨月逻辑容易掉坑。比如 LocalDate.of(2024, 1, 31).plusMonths(1) 得到的是 2024-02-29(不是 31 日),因为 2 月没有 31 号,Java 自动“退到当月最后一天”。类似地,plusYears(1) 遇到闰年 2 月 29 日也会回退。
立即学习“Java免费学习笔记(深入)”;
- 用
withDayOfMonth(31)强行设日期可能抛DateTimeException(比如 2 月设 31) - 需要“每月固定日期”的场景(如每月 25 日扣款),建议用
TemporalAdjusters.dayOfMonthOfMonth(25),它会在目标月自动取有效最大日 -
Period.between(d1, d2)返回的是“年月日”逻辑差,不是总天数;要总天数用ChronoUnit.DAYS.between(d1, d2)
LocalDateTime 当作带时区的时间来用,或者在跨系统传时间时混淆了“本地时间表达”和“绝对时间点”。这两者一旦混用,测试环境看着对,上线后时区一变就全乱。










