TL;DR)
| 方法 | 类/包 | 推荐度 | 说明 |
|---|---|---|---|
java.util.Date |
java.util |
⭐ | 已过时,设计有缺陷,主要用于遗留代码。 |
java.util.Calendar |
java.util |
⭐ | 已过时,比 Date 好用,但 API 复杂,易出错。 |
java.time.LocalDateTime |
java.time |
⭐⭐⭐⭐⭐ | 强烈推荐,Java 8+ 引入,现代、清晰、易用。 |
java.time.ZonedDateTime |
java.time |
⭐⭐⭐⭐⭐ | 强烈推荐,带时区的日期时间,用于精确时间。 |
java.time.Instant |
java.time |
⭐⭐⭐⭐ | 用于表示时间轴上的一个精确点(时间戳)。 |
传统方式 (已过时,不推荐用于新代码)
a) java.util.Date
这是最原始的方式,但它的设计存在一些问题,比如它同时包含了日期和时间,而且月份从 0 开始计数(0 代表一月)。

示例代码:
import java.util.Date;
public class GetDateExample {
public static void main(String[] args) {
// 直接创建 Date 对象,它代表当前时间
Date now = new Date();
// 打印当前时间
// 注意:直接打印 Date 对象会输出其 toString() 方法的返回值
System.out.println("当前时间 (Date): " + now);
// 获取时间戳(自 1970年1月1日 00:00:00 GMT 以来的毫秒数)
long timestamp = now.getTime();
System.out.println("当前时间戳 (毫秒): " + timestamp);
}
}
输出示例:
当前时间 (Date): Wed Oct 26 10:30:55 CST 2025
当前时间戳 (毫秒): 1698283855432
问题:
Date的很多方法都已过时(@Deprecated)。- 处理时区非常麻烦。
- API 不直观。
b) java.util.Calendar
为了弥补 Date 的不足,Java 引入了 Calendar,它提供了更丰富的功能,比如可以方便地获取/设置年、月、日等。
示例代码:
import java.util.Calendar;
public class GetCalendarExample {
public static void main(String[] args) {
// 获取一个 Calendar 实例,默认使用当前时间和默认时区
Calendar calendar = Calendar.getInstance();
// 获取年、月、日、时、分、秒
// 注意:月份是从 0 开始的!0 代表一月,11 代表十二月。
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // 需要手动 +1
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY); // 24小时制
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
System.out.printf("当前时间 (Calendar): %04d-%02d-%02d %02d:%02d:%02d%n",
year, month, day, hour, minute, second);
}
}
输出示例:
当前时间 (Calendar): 2025-10-26 10:30:55
问题:
- API 仍然非常繁琐和冗长。
- 线程不安全。
- 月份从 0 开始计数等设计缺陷依然存在。
- 自 Java 8 起,已标记为过时。
现代方式 (Java 8+ 强烈推荐)
自 Java 8 开始,Java 引入了全新的 java.time 包,它由 JSR-310 标准定义,旨在彻底解决旧日期时间 API 的所有问题。对于任何新的 Java 项目,都应该优先使用 java.time 包中的类。
a) java.time.LocalDateTime - 最常用
LocalDateTime 是一个不可变的类,表示一个不带时区的日期和时间,非常适合用于应用程序内部的逻辑处理。
示例代码:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class GetLocalDateTimeExample {
public static void main(String[] args) {
// 1. 获取当前日期和时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前日期时间 (LocalDateTime): " + now);
// 2. 获取年、月、日、时、分、秒
int year = now.getYear();
int month = now.getMonthValue(); // getMonth() 返回枚举,getMonthValue() 返回数字 (1-12)
int day = now.getDayOfMonth();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
System.out.printf("年: %d, 月: %d, 日: %d, 时: %d, 分: %d, 秒: %d%n",
year, month, day, hour, minute, second);
// 3. 格式化输出
// 定义一个格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = now.format(formatter);
System.out.println("格式化后的时间: " + formattedDateTime);
// 4. 解析字符串
String dateTimeString = "2025-12-31 23:59:59";
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeString, formatter);
System.out.println("解析后的时间: " + parsedDateTime);
}
}
输出示例:
当前日期时间 (LocalDateTime): 2025-10-26T10:30:55.123
年: 2025, 月: 10, 日: 26, 时: 10, 分: 30, 秒: 55
格式化后的时间: 2025-10-26 10:30:55
解析后的时间: 2025-12-31T23:59:59
优点:
- API 清晰、直观、不可变(线程安全)。
- 月份从 1 开始,符合常识。
- 内置了强大的格式化和解析功能。
b) java.time.ZonedDateTime - 带时区的精确时间
如果你的应用需要处理不同时区的时间,或者需要记录一个全球统一的时间点,ZonedDateTime 是最佳选择。
示例代码:
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
public class GetZonedDateTimeExample {
public static void main(String[] args) {
// 1. 获取当前时间,并使用系统默认时区
ZonedDateTime nowDefault = ZonedDateTime.now();
System.out.println("当前时间 (默认时区): " + nowDefault);
// 2. 获取指定时区的当前时间
ZoneId newYorkZone = ZoneId.of("America/New_York");
ZonedDateTime nowInNewYork = ZonedDateTime.now(newYorkZone);
System.out.println("纽约当前时间: " + nowInNewYork);
ZoneId shanghaiZone = ZoneId.of("Asia/Shanghai");
ZonedDateTime nowInShanghai = ZonedDateTime.now(shanghaiZone);
System.out.println("上海当前时间: " + nowInShanghai);
// 3. 获取UTC (协调世界时) 时间
ZonedDateTime nowInUtc = ZonedDateTime.now(ZoneId.of("UTC"));
System.out.println("UTC当前时间: " + nowInUtc);
// 4. 格式化输出
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
System.out.println("格式化 (上海时间): " + nowInShanghai.format(formatter));
}
}
输出示例:
当前时间 (默认时区): 2025-10-26T10:30:55.456+08:00[Asia/Shanghai]
纽约当前时间: 2025-10-25T22:30:55.456-04:00[America/New_York]
上海当前时间: 2025-10-26T10:30:55.456+08:00[Asia/Shanghai]
UTC当前时间: 2025-10-26T02:30:55.456Z
格式化 (上海时间): 2025-10-26 10:30:55 CST
优点:
- 正确处理时区转换,避免夏令时等问题。
- 是记录和传输时间的标准方式。
c) java.time.Instant - 时间戳
Instant 表示时间线上的一个精确点,类似于 System.currentTimeMillis(),但精度更高(纳秒级),它非常适合用于日志、数据库存储或与其他系统进行时间戳交换。
示例代码:
import java.time.Instant;
public class GetInstantExample {
public static void main(String[] args) {
// 1. 获取当前时间的 Instant 对象 (UTC时间)
Instant now = Instant.now();
System.out.println("当前 Instant: " + now);
// 2. 获取时间戳 (秒和纳秒)
long epochSecond = now.getEpochSecond(); // 从 1970-01-01T00:00:00Z 开始的秒数
int nano = now.getNano(); // 纳秒部分
System.out.println("时间戳 (秒): " + epochSecond);
System.out.println("纳秒: " + nano);
// 3. 与 System.currentTimeMillis() 对比
long currentTimeMillis = System.currentTimeMillis();
System.out.println("System.currentTimeMillis(): " + currentTimeMillis);
// Instant 的 toEpochMilli() 方法可以获取毫秒级时间戳
long instantMillis = now.toEpochMilli();
System.out.println("Instant.toEpochMilli(): " + instantMillis);
// 两者在毫秒级别上是相等的
System.out.println("两者是否相等: " + (currentTimeMillis == instantMillis));
}
}
输出示例:
当前 Instant: 2025-10-26T02:30:55.789Z
时间戳 (秒): 1698283855
纳秒: 789000000
System.currentTimeMillis(): 1698283855789
Instant.toEpochMilli(): 1698283855789
两者是否相等: true
如何选择?
- 新项目、新功能:直接使用
java.time包。- 只需要年月日时分秒,不带时区 ->
LocalDateTime - 需要处理时区,或者需要精确的时间点 ->
ZonedDateTime - 需要存储时间戳、记录日志 ->
Instant
- 只需要年月日时分秒,不带时区 ->
- 维护旧项目:
- 如果代码已经大量使用
Date或Calendar,可以继续维护。 - 如果需要将旧代码迁移到新 API,可以使用
java.time提供的转换方法,Date.from(Instant.now())将Instant转为Date。new Date().toInstant()将Date转为Instant。
- 如果代码已经大量使用
