- 推荐方式:使用 Java 8+ 的
java.timeAPI - 旧方式:使用
java.util.Date和SimpleDateFormat(不推荐,但为了兼容性需要了解)
Part 1: 推荐方式 - Java 8+ java.time API
java.time 包中包含几个核心类,用于处理不同的日期和时间需求。

| 类名 | 描述 |
|---|---|
LocalDate |
仅包含日期(年、月、日),如 2025-10-27 |
LocalTime |
仅包含时间(时、分、秒、纳秒),如 10:15:30 |
LocalDateTime |
包含日期和时间,但不带时区信息,如 2025-10-27T10:15:30 |
ZonedDateTime |
包含日期、时间和时区信息,如 2025-10-27T10:15:30+08:00[Asia/Shanghai] |
DateTimeFormatter |
用于格式化和解析日期时间对象 |
场景 1: 字符串 与 LocalDateTime 互转
这是最常见的转换场景。
字符串 -> LocalDateTime
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class StringToLocalDateTime {
public static void main(String[] args) {
// 1. 定义字符串格式的日期时间
String dateTimeStr = "2025-10-27 15:45:30";
// 2. 定义格式化模式,必须与字符串格式完全匹配
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 3. 使用 parse 方法进行转换
LocalDateTime dateTime = LocalDateTime.parse(dateTimeStr, formatter);
// 4. 输出结果
System.out.println("转换后的 LocalDateTime: " + dateTime);
// 输出: 转换后的 LocalDateTime: 2025-10-27T15:45:30
}
}
LocalDateTime -> 字符串
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeToString {
public static void main(String[] args) {
// 1. 获取当前的 LocalDateTime
LocalDateTime now = LocalDateTime.now();
// 2. 定义你想要的格式化模式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
// 3. 使用 format 方法进行转换
String formattedDateTime = now.format(formatter);
// 4. 输出结果
System.out.println("格式化后的字符串: " + formattedDateTime);
// 输出可能为: 格式化后的字符串: 2025年10月27日 16时30分45秒
}
}
场景 2: java.util.Date 与 java.time 互转
在与旧代码库交互时,这种转换是必要的。

java.util.Date -> java.time
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public class DateToLocalDateTime {
public static void main(String[] args) {
// 1. 创建一个旧的 Date 对象
Date date = new Date();
// 2. 将 Date 转换为 Instant
Instant instant = date.toInstant();
// 3. 将 Instant 转换为 LocalDateTime (需要指定时区)
// ZoneId.systemDefault() 获取系统默认时区
LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
System.out.println("Date 对象: " + date);
System.out.println("转换后的 LocalDateTime: " + dateTime);
}
}
java.time -> java.util.Date
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
public class LocalDateTimeToDate {
public static void main(String[] args) {
// 1. 创建一个 LocalDateTime 对象
LocalDateTime dateTime = LocalDateTime.now();
// 2. 将 LocalDateTime 转换为 Instant (需要指定时区)
Instant instant = dateTime.atZone(ZoneId.systemDefault()).toInstant();
// 3. 将 Instant 转换为 Date
Date date = Date.from(instant);
System.out.println("LocalDateTime 对象: " + dateTime);
System.out.println("转换后的 Date 对象: " + date);
}
}
场景 3: String 与 java.util.Date 互转 (旧方式)
虽然不推荐,但在一些遗留项目中仍然会用到。
字符串 -> java.util.Date
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class StringToDate {
public static void main(String[] args) {
String dateStr = "2025/10/27";
// 注意:SimpleDateFormat 不是线程安全的,不要定义为 static 成员变量
SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
try {
Date date = formatter.parse(dateStr);
System.out.println("转换后的 Date 对象: " + date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
java.util.Date -> 字符串
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateToString {
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate = formatter.format(date);
System.out.println("格式化后的字符串: " + formattedDate);
}
}
Part 2: 旧方式 - java.util.Date 和 SimpleDateFormat
重要警告:
java.util.Date 是一个“遗留类”,它设计上有很多问题:
- 它包含日期和时间,但时区处理不明确。
- 它的年份从 1900 开始,月份从 0 开始,非常容易出错。
SimpleDateFormat是非线程安全的,如果在多线程环境下共享同一个SimpleDateFormat实例,可能会导致数据错乱和程序异常,每次使用都创建新实例又会影响性能。
强烈建议在所有新代码中只使用 java.time API。
总结与最佳实践
| 转换类型 | 推荐方法 (Java 8+) | 旧方法 (不推荐) |
|---|---|---|
字符串 -> LocalDateTime |
LocalDateTime.parse(str, formatter) |
new SimpleDateFormat(...).parse(str) |
LocalDateTime -> 字符串 |
localDateTime.format(formatter) |
new SimpleDateFormat(...).format(date) |
Date -> LocalDateTime |
Date.toInstant().atZone(zoneId).toLocalDateTime() |
- |
LocalDateTime -> Date |
Date.from(localDateTime.atZone(zoneId).toInstant()) |
- |
字符串 -> LocalDate |
LocalDate.parse(str, formatter) |
new SimpleDateFormat(...).parse(str) |
LocalDate -> 字符串 |
localDate.format(formatter) |
new SimpleDateFormat(...).format(date) |
常用 DateTimeFormatter 模式
| 模式字母 | 含义 | 示例 |
|---|---|---|
yyyy |
4位年份 | 2025 |
MM |
2位月份 | 10 (十月) |
dd |
2位月份中的天数 | 27 |
HH |
24小时制的小时 | 15 |
hh |
12小时制的小时 | 03 |
mm |
分钟 | 45 |
ss |
秒 | 30 |
SSS |
毫秒 | 500 |
XXX |
时区偏移 (如 +08:00) | +08:00 |
Z |
时区名称 (如 Asia/Shanghai) | Asia/Shanghai |
核心要点
- 首选
java.time:所有新的 Java 项目都应该使用java.time包,它是现代、正确且功能强大的标准。 - 线程安全:
java.time的所有类都是不可变且线程安全的,可以在任何地方安全地共享。 - 明确时区:处理跨时区的日期时间时,始终使用
ZonedDateTime或明确指定ZoneId。 SimpleDateFormat的陷阱:如果你必须使用旧的Date和SimpleDateFormat,请记住它不是线程安全的,最佳实践是在每次使用时都创建一个新的实例,或者使用ThreadLocal来包装它(但这会增加复杂性)。- 格式匹配:在解析字符串时,
DateTimeFormatter的模式必须与字符串的格式完全一致,否则会抛出异常。
