Java 8的java.time包以Instant为核心,基于UTC纳秒计数,强调不可变性、时区分离与语义明确;所有类型均为final且无setter,类名直指业务场景,强制区分机器时间与人类时间,并通过桥接支持旧API但不推荐使用。

Java的时间模型以“瞬时时间(Instant)”为核心,将时间抽象为自1970年1月1日00:00:00 UTC起经过的纳秒数,强调不可变性、时区分离与语义明确——这是JSR-310(java.time包)设计的根本出发点。
以UTC为统一基准,解耦时间表示与时区处理
Java 8引入的java.time.Instant代表绝对时间轴上的一个点,不包含时区或日历信息,仅依赖系统时钟(通常基于UTC)。所有本地时间(如LocalDateTime)、带时区时间(ZonedDateTime)都需显式通过ZoneId转换而来。这种设计避免了隐式时区假设导致的歧义,例如:
- 数据库存储推荐用Instant或TIMESTAMP WITH TIME ZONE,确保跨系统一致
- 用户界面展示必须结合ZoneId.systemDefault()或用户偏好时区,而非直接格式化Instant
- 跨时区计算(如会议时间换算)应先转为Instant,再转目标时区的ZonedDateTime
不可变对象 + 领域驱动命名,降低误用风险
所有java.time类型(LocalDate、Duration、Period等)均为final且无setter方法,每次操作返回新实例。同时类名直指业务含义:
- LocalDateTime:仅日期+时间,无时区,适合记录“2025-04-05 14:30”这类本地事件
- ZonedDateTime:完整时区上下文,适用于需要夏令时感知的调度逻辑
- Duration:基于时间长度(秒+纳秒),用于间隔计算;Period:基于日历单位(年/月/日),用于人可读的跨度表达
明确区分“机器时间”与“人类时间”语义
API强制开发者思考时间的使用场景:
立即学习“Java免费学习笔记(深入)”;
- 系统计时、超时控制、日志时间戳 → 用Instant或System.nanoTime()
- 生日、合同生效日、报表周期 → 用LocalDate(忽略具体时刻)
- 航班起飞、银行营业时间 → 用LocalTime或ZonedDateTime(需明确地点)
- 避免混用:LocalDateTime.now()不等于Instant.now().atZone(ZoneId.systemDefault()).toLocalDateTime()——前者忽略夏令时切换,后者精确反映本地真实时刻
向后兼容但不妥协:保留旧API,隔离风险
java.time未废弃java.util.Date/Calendar,而是提供互操作桥接方法(如Date.toInstant()),但明确标注旧类线程不安全、设计混乱。新项目应:
- 禁止在DTO、持久层、API契约中暴露Date/Calendar
- 数据库映射优先使用JDBC 4.2+的PreparedStatement.setObject(…, LocalDateTime)等原生支持
- 序列化统一配置Jackson的JavaTimeModule,避免手动toString()或自定义格式器










