Java日期和时间API的语法基础

LocalDateTime 无时区,仅表示本地日历时间;ZonedDateTime 绑定时区(如 Asia/Shanghai),可正确处理夏令时和历史变更,适用于跨系统比对与持久化。

LocalDateTime 和 ZonedDateTime 的核心区别在哪

LocalDateTime 表示“2025-05-20T14:30:00”只是本地日历时间,不带时区信息;ZonedDateTime 则明确绑定到某个时区(如 Asia/Shanghai),能正确处理夏令时、历史时区变更等场景。

常见错误:把服务器本地时间直接转成 ZonedDateTime 却没指定时区,结果默认用了系统时区,部署到海外服务器就出错。

  • LocalDateTime.now() 永远不包含时区,不能用于跨系统时间比对或持久化存储
  • ZonedDateTime.now(ZoneId.of("Asia/Shanghai")) 才是真正代表东八区当前时刻的可靠值
  • 数据库字段类型要匹配:PostgreSQL 用 timestamptzZonedDateTime,别用 timestamp without time zone

Instant 是什么,为什么它适合做时间戳存储

Instant 表示从 1970-01-01T00:00:00Z 开始的纳秒偏移量,本质就是 UTC 时间轴上的一个点。它没有时区概念,也不受本地格式影响,是跨系统、跨语言最安全的时间表示方式。

常见错误:用 System.currentTimeMillis() 构造 LocalDateTime,结果丢失了时区上下文,再转回 Instant

可能偏差一整天。

  • 入库前一律转成 InstantzonedDateTime.toInstant()localDateTime.atZone(ZoneId.of("UTC")).toInstant()
  • 前端传来的 ISO 格式字符串(如 "2025-05-20T14:30:00Z")可直接用 Instant.parse()
  • 不要用 new Date().toInstant() —— Date 是遗留类,语义模糊,容易引发隐式时区转换

DateTimeFormatter 的 pattern 字符必须严格区分大小写

yyyyYYYY 不是一回事:yyyy 是“年份”,YYYY 是“基于周的年份”,在每年年初或年末可能差一年;MM 是月份,mm 是分钟,写错就解析失败或结果错乱。

常见错误:用 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") 解析带毫秒的字符串(如 "2025-05-20T14:30:00.123"),抛出 DateTimeParseException

  • 解析含毫秒的 ISO 时间,优先用内置常量:DateTimeFormatter.ISO_LOCAL_DATE_TIMEDateTimeFormatter.ISO_INSTANT
  • 自定义 pattern 必须匹配输入:毫秒用 SSS,纳秒用 nnn,时区用 XXX(如 +08:00)或 ZZZ(如 GMT+08:00
  • 避免手动拼接字符串:用 LocalDateTime.format() 而不是 String.format(),后者无法处理时区偏移

ZoneId 和 ZoneOffset 的使用边界

ZoneOffset 是固定偏移(如 +08:00),不随夏令时变化;ZoneId 是真实地理时区(如 Europe/London),会自动应用 DST 规则。两者不能混用。

常见错误:用 ZoneOffset.of("+08:00") 替代 ZoneId.of("Asia/Shanghai"),看似一样,但遇到中国未来调整时区规则时,前者永远不变,后者可通过 JDK 更新支持新规则。

  • 只在明确需要固定偏移的场景用 ZoneOffset(如日志时间统一打成 UTC+0)
  • 涉及用户本地时间、业务营业时间、节假日计算等,必须用 ZoneId
  • ZoneId.systemDefault() 不可靠——服务器时区可能被运维修改,应显式配置并注入,比如 Spring Boot 中通过 @Value("${app.timezone:Asia/Shanghai}")
ZoneId shanghai = ZoneId.of("Asia/Shanghai");
LocalDateTime local = LocalDateTime.of(2025, 3, 31, 2, 30, 0); // 凌晨2:30
ZonedDateTime zdt = local.atZone(shanghai); // 自动按规则处理是否为夏令时(中国不实行,但逻辑存在)
System.out.println(zdt); // 2025-03-31T02:30+08:00[Asia/Shanghai]
时区规则更新、夏令时边界、毫秒精度截断——这些细节不会报错,但会在特定日期、特定地区悄悄出错。写时间逻辑时,别依赖“看起来对”。