Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

    • Java概述
    • Java语法
    • 面向对象编程
    • Java数组
    • String字符串
    • 异常处理
    • 基本类型与包装类
    • Object类与常用方法
    • 泛型
    • 枚举与注解
    • 反射
    • 值传递与引用
    • Lambda表达式与函数式接口
    • Stream API
    • Optional与空值处理
    • 日期时间API
      • 1. 老 API 的问题
      • 2. 核心类型
      • 3. LocalDate
      • 4. LocalDateTime
      • 5. Instant
      • 6. 时区
      • 7. 格式化与解析
      • 8. Duration 与 Period
      • 9. 与 Date 转换
      • 10. 常见坑
      • 11. 时间建模与存储建议
      • Tips 快问快答
    • Java现代语法特性
  • Java集合

  • Java并发

  • Java IO

  • JVM

目录

日期时间API

Java 8 引入 java.time 包,用于替代老的 Date、Calendar 和 SimpleDateFormat。新时间 API 不可变、线程安全、语义清晰,是现代 Java 项目处理日期时间的首选。

# 1. 老 API 的问题

老 API 问题
Date 名称像日期,实际表示时间点;很多方法已废弃
Calendar 可变、API 繁琐
SimpleDateFormat 非线程安全

错误示例:

private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd");

多线程共享 SimpleDateFormat 可能解析错乱。

# 2. 核心类型

类型 含义
LocalDate 日期,不含时间和时区
LocalTime 时间,不含日期和时区
LocalDateTime 日期时间,不含时区
Instant 时间线上的瞬时点,适合机器时间
ZonedDateTime 带时区日期时间
Duration 时间量,偏秒和纳秒
Period 日期量,偏年月日
DateTimeFormatter 格式化和解析

选择:

只要日期 -> LocalDate
业务日期时间 -> LocalDateTime
跨时区/绝对时间点 -> Instant / ZonedDateTime
计算耗时 -> Duration
计算年月日差 -> Period

# 3. LocalDate

LocalDate today = LocalDate.now();
LocalDate date = LocalDate.of(2026, 6, 25);

常用方法:

date.plusDays(1);
date.minusMonths(1);
date.getDayOfWeek();
date.isAfter(LocalDate.now());

LocalDate 不可变,调用 plusDays 会返回新对象。

# 4. LocalDateTime

LocalDateTime now = LocalDateTime.now();
LocalDateTime time = LocalDateTime.of(2026, 6, 25, 10, 30);

适合表示业务上的本地日期时间,例如订单创建时间。但如果涉及跨时区传输,必须明确时区。

# 5. Instant

Instant 表示 UTC 时间线上的瞬时点。

Instant now = Instant.now();

适合:

  • 存储事件发生时间。
  • 日志时间。
  • 跨系统传输。
  • 与时间戳互转。
long millis = Instant.now().toEpochMilli();
Instant instant = Instant.ofEpochMilli(millis);

# 6. 时区

ZoneId shanghai = ZoneId.of("Asia/Shanghai");
ZonedDateTime zoned = ZonedDateTime.now(shanghai);

LocalDateTime 不包含时区,不能代表全球唯一时间点。

2026-06-25 10:00
  ├─ Asia/Shanghai
  └─ America/New_York
代表不同瞬时点

跨系统接口建议传递 ISO-8601 字符串或时间戳,并明确时区语义。

# 7. 格式化与解析

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

String text = LocalDateTime.now().format(formatter);
LocalDateTime parsed = LocalDateTime.parse(text, formatter);

DateTimeFormatter 是线程安全的,可以定义为常量。

private static final DateTimeFormatter FORMATTER =
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

# 8. Duration 与 Period

Duration 表示时间长度:

Duration duration = Duration.between(startInstant, endInstant);
long seconds = duration.toSeconds();

Period 表示日期间隔:

Period period = Period.between(startDate, endDate);
int months = period.getMonths();

区别:

类型 适合
Duration 秒、毫秒、纳秒级耗时
Period 年、月、日日期差

# 9. 与 Date 转换

Date date = Date.from(instant);
Instant instant = date.toInstant();

与 LocalDateTime 转换需要时区:

LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();

# 10. 常见坑

  • 把 LocalDateTime 当成绝对时间点跨时区传输。
  • 日期格式使用 YYYY 而不是 yyyy。
  • 多线程共享 SimpleDateFormat。
  • 数据库存储时区语义不明确。
  • 计算自然月差时误用固定天数。

YYYY 是 week-based-year,跨年周可能出错。普通年份使用 yyyy。

# 11. 时间建模与存储建议

时间问题的难点不在 API,而在语义。建模前先问清楚:这个值表示“日历日期”“本地业务时间”,还是“全球唯一瞬时点”。

业务语义 推荐类型 示例
生日、账单日 LocalDate 1990-01-01
门店营业时间 LocalTime 09:00
用户填写的预约时间 LocalDateTime + 业务时区 2026-06-25 10:00
事件发生时间 Instant 统一时间线
跨地区会议 ZonedDateTime 带时区规则
接口传输 ISO-8601 或 epoch millis 明确时区语义
用户输入本地时间
  │ 携带业务时区
  ▼
ZonedDateTime
  │ 转换为统一时间线
  ▼
Instant
  │ 存储/传输
  ▼
按展示时区格式化

数据库字段也要有约定:如果存时间戳,要明确是 UTC 还是数据库会话时区;如果存字符串,要明确格式和时区;如果存日期,不要额外拼接午夜时间点来冒充日期。

时间 API 使用原则:

  • 机器记录和跨系统传输优先 Instant。
  • 人类日历概念优先 LocalDate、LocalTime。
  • 涉及时区规则用 ZonedDateTime,不要只保存偏移量。
  • 格式化器可复用,SimpleDateFormat 不要多线程共享。
  • 测试时间逻辑时使用 Clock 注入当前时间,避免测试依赖真实系统时间。

# Tips 快问快答

Q:现代 Java 日期时间首选哪个包? A:java.time。

Q:LocalDateTime 有时区吗? A:没有。

Q:跨系统传输时间推荐什么? A:使用 Instant、时间戳或明确带时区的 ISO-8601 字符串。

Q:DateTimeFormatter 线程安全吗? A:是,可以作为静态常量。

Q:SimpleDateFormat 线程安全吗? A:不安全。

Q:Duration 和 Period 区别是什么? A:Duration 表示时间量,Period 表示年月日日期量。

Q:为什么 YYYY 有坑? A:它是基于周的年份,普通日期格式应使用 yyyy。

Q:LocalDate 适合表示什么? A:只包含日期的业务值,例如生日、账单日。

上次更新: 2026/06/25, 14:19:18
Optional与空值处理
Java现代语法特性

← Optional与空值处理 Java现代语法特性→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式