在 Java 中,处理时间的 API 主要有两套:
- 旧版日期时间 API(java.util.Date / java.util.Calendar)
- 新版日期时间 API(java.time 包,JDK8+ 推荐使用)
下面我将分别列出这两类 API 的常用类、是否支持时区、常见方法及推荐用途。
✅ 一、旧版时间 API(java.util)
1. java.util.Date
| 特性 | 描述 |
|---|---|
| 是否包含时区? | ❌ 否(本质是 UTC 时间戳) |
| 常用方法 | getTime()、setTime(long)、toString()(输出带本地时区) |
| 缺点 | 可变、线程不安全、设计混乱 |
示例:
java
Date now = new Date();
System.out.println(now); // 输出格式:Sat Apr 05 14:30:00 CST 2025
2. java.util.Calendar
| 特性 | 描述 |
|---|---|
| 是否包含时区? | ✅ 是(默认系统时区,可设置) |
| 常用方法 | getInstance()、add()、get()、getTime() |
| 缺点 | 线程不安全、API 复杂 |
示例:
java
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, 1);
System.out.println(cal.getTime());
3. java.text.SimpleDateFormat
| 特性 | 描述 |
|---|---|
| 是否支持时区? | ✅ 是 |
| 常用方法 | format(Date)、parse(String) |
| 缺点 | 非线程安全,需加锁或使用 ThreadLocal |
示例:
java
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.format(new Date()));
✅ 二、新版时间 API(java.time,推荐使用)
JDK8 引入,位于
java.time包下,设计更合理、线程安全、支持时区、易读性强。
1. Instant ------ 时间戳(UTC)
| 特性 | 描述 |
|---|---|
| 是否包含时区? | ❌ 否 |
| 表示什么? | UTC 时间戳(从 1970-01-01T00:00:00Z 开始的秒/纳秒) |
| 常用方法 | now()、ofEpochMilli(long)、plusSeconds() |
| 推荐用途 | 日志记录、唯一 ID、分布式系统时间同步 |
示例:
java
Instant now = Instant.now(); // 当前 UTC 时间
Instant later = now.plusSeconds(3600); // 1小时后
2. LocalDate / LocalTime / LocalDateTime ------ 不带时区的时间
| 类名 | 是否有时区? | 表示内容 | 常用方法 |
|---|---|---|---|
LocalDate |
❌ 否 | 年月日 | now()、of()、plusDays() |
LocalTime |
❌ 否 | 时分秒 | now()、of()、plusMinutes() |
LocalDateTime |
❌ 否 | 年月日 + 时分秒 | now()、of()、plusHours() |
示例:
java
LocalDate today = LocalDate.now();
LocalDateTime now = LocalDateTime.now();
3. ZonedDateTime / OffsetDateTime ------ 带时区的时间
| 类名 | 是否有时区? | 表示内容 | 常用方法 |
|---|---|---|---|
ZonedDateTime |
✅ 是 | 完整带时区的时间(如 Asia/Shanghai) |
now(ZoneId)、withZoneSameInstant() |
OffsetDateTime |
✅ 是 | 偏移时间(如 +08:00) |
now()、toZonedDateTime() |
示例:
java
ZoneId zone = ZoneId.of("Europe/London");
ZonedDateTime londonTime = ZonedDateTime.now(zone);
// 转换为另一个时区
ZonedDateTime shanghaiTime = londonTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
4. ZoneId / ZoneOffset ------ 时区相关
| 类名 | 描述 | 常用方法 |
|---|---|---|
ZoneId |
代表一个完整的时区(如 Asia/Shanghai) |
of()、systemDefault() |
ZoneOffset |
代表一个偏移量(如 +08:00) |
ofHours()、UTC |
示例:
java
ZoneId zone = ZoneId.of("America/New_York");
ZoneOffset offset = ZoneOffset.of("+08:00");
5. Duration / Period ------ 时间差
| 类名 | 描述 | 单位 | 是否支持时区 |
|---|---|---|---|
Duration |
时间差(精确到纳秒) | 秒、纳秒 | ❌ 否(基于 Instant) |
Period |
日期差(精确到天) | 天、月、年 | ❌ 否 |
示例:
java
Duration duration = Duration.between(Instant.now(), Instant.now().plusSeconds(3600));
Period period = Period.between(LocalDate.now(), LocalDate.now().plusDays(5));
6. DateTimeFormatter ------ 格式化和解析
| 特性 | 描述 |
|---|---|
| 是否支持时区? | ✅ 是(可通过 withZone() 设置) |
| 常用方法 | ofPattern(String)、format(TemporalAccessor)、parse(CharSequence) |
| 推荐用途 | 格式化输出、解析字符串时间 |
示例:
java
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneId.of("UTC"));
String formatted = formatter.format(Instant.now());
🧾 总结对比表
| API 类名 | 是否带时区 | 精度 | 推荐用途 | 是否线程安全 |
|---|---|---|---|---|
Date |
❌ 否(实际是 UTC) | 毫秒 | 兼容旧代码 | ❌ |
Calendar |
✅ 是 | 毫秒 | 日期计算 | ❌ |
SimpleDateFormat |
✅ 是 | - | 格式化 | ❌ |
Instant |
❌ 否 | 纳秒 | 时间戳 | ✅ |
LocalDate / LocalTime / LocalDateTime |
❌ 否 | 天/小时/分钟 | 本地日期操作 | ✅ |
ZonedDateTime |
✅ 是 | 纳秒 | 带时区时间处理 | ✅ |
OffsetDateTime |
✅ 是 | 纳秒 | 固定偏移时间 | ✅ |
Duration |
❌ 否 | 纳秒 | 时间长度计算 | ✅ |
Period |
❌ 否 | 天/月/年 | 日期长度计算 | ✅ |
DateTimeFormatter |
✅ 是 | - | 格式化/解析 | ✅ |
✅ 推荐组合用法
| 场景 | 推荐 API 组合 |
|---|---|
| 记录时间戳(日志、ID生成) | Instant |
| 显示给用户看的时间 | ZonedDateTime + DateTimeFormatter |
| 日期运算(如"明天"、"下周几") | LocalDate |
| 跨时区时间转换 | ZonedDateTime + withZoneSameInstant() |
| 时间差计算(精确到毫秒) | Duration |
| 日期差计算(按天、月) | Period |
