核心要点
-
旧版 API (
java.util.Date和SimpleDateFormat):java.util.Date:一个“遗留”类,设计上有很多问题,例如它同时包含日期和时间,且月份从 0 开始。SimpleDateFormat:用于解析和格式化日期,它是非线程安全的,在多线程环境下使用会引发严重问题。- 不推荐在新项目中使用,但维护旧代码时必须了解。
-
新版 API (
java.time包):- Java 8 引入了一套全新的、不可变且线程安全的日期时间 API,位于
java.time包下。 - 这是目前官方推荐的最佳实践。
- 主要类包括:
LocalDate:只表示日期(年、月、日)。LocalTime:只表示时间(时、分、秒、纳秒)。LocalDateTime:表示日期和时间,但不包含时区信息。ZonedDateTime:表示带有时区的日期和时间。DateTimeFormatter:用于解析和格式化日期,它是线程安全的。
- Java 8 引入了一套全新的、不可变且线程安全的日期时间 API,位于
使用新版 API (Java 8+) - 推荐
这是最现代、最安全、最推荐的方式。
步骤:
- 定义你的日期字符串格式。
- 创建
DateTimeFormatter实例,并应用该格式。 - 使用
LocalDate.parse()或LocalDateTime.parse()等静态方法,传入字符串和DateTimeFormatter进行解析。
示例代码:
解析为 LocalDate (仅日期)
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class StringToDateExample {
public static void main(String[] args) {
String dateString = "2025-10-27";
// 定义格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
// 将字符串解析为 LocalDate
LocalDate localDate = LocalDate.parse(dateString, formatter);
System.out.println("原始字符串: " + dateString);
System.out.println("解析后的 LocalDate: " + localDate);
System.out.println("年份: " + localDate.getYear());
System.out.println("月份: " + localDate.getMonthValue());
System.out.println("日期: " + localDate.getDayOfMonth());
} catch (DateTimeParseException e) {
System.err.println("日期格式解析失败,请检查字符串格式是否正确: " + e.getMessage());
}
}
}
输出:
原始字符串: 2025-10-27
解析后的 LocalDate: 2025-10-27
年份: 2025
月份: 10
日期: 27
解析为 LocalDateTime (日期和时间)
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class StringToDateTimeExample {
public static void main(String[] args) {
String dateTimeString = "2025-10-27 15:30:45";
// 定义格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
try {
// 将字符串解析为 LocalDateTime
LocalDateTime localDateTime = LocalDateTime.parse(dateTimeString, formatter);
System.out.println("原始字符串: " + dateTimeString);
System.out.println("解析后的 LocalDateTime: " + localDateTime);
System.out.println("小时: " + localDateTime.getHour());
System.out.println("分钟: " + localDateTime.getMinute());
} catch (Exception e) {
System.err.println("日期时间格式解析失败: " + e.getMessage());
}
}
}
输出:
原始字符串: 2025-10-27 15:30:45
解析后的 LocalDateTime: 2025-10-27T15:30:45
小时: 15
分钟: 30
处理不同格式
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class DifferentFormatExample {
public static void main(String[] args) {
// 格式1: "2025/10/27"
String dateStr1 = "2025/10/27";
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date1 = LocalDate.parse(dateStr1, formatter1);
System.out.println("格式1解析结果: " + date1);
// 格式2: "27-Oct-2025" (使用英文月份缩写)
String dateStr2 = "27-Oct-2025";
// 可以指定 Locale 来确保月份名称解析正确
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("dd-MMM-yyyy", Locale.ENGLISH);
LocalDate date2 = LocalDate.parse(dateStr2, formatter2);
System.out.println("格式2解析结果: " + date2);
// 格式3: "20251027" (紧凑格式)
String dateStr3 = "20251027";
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate date3 = LocalDate.parse(dateStr3, formatter3);
System.out.println("格式3解析结果: " + date3);
}
}
使用旧版 API (java.util.Date 和 SimpleDateFormat) - 不推荐
如果你正在维护旧的 Java 代码(Java 7 或更早),你可能会遇到这种方式。
步骤:
- 定义你的日期字符串格式。
- 创建
SimpleDateFormat实例,并应用该格式。 - 调用
parse()方法进行解析。
示例代码:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class LegacyStringToDateExample {
public static void main(String[] args) {
String dateString = "2025-10-27";
// 定义格式
// 注意:SimpleDateFormat 不是线程安全的!
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
try {
// 将字符串解析为 Date
Date date = formatter.parse(dateString);
System.out.println("原始字符串: " + dateString);
// Date.toString() 输出的格式不是我们想要的,它包含时区信息
System.out.println("解析后的 java.util.Date: " + date);
System.out.println("Date.getTime() (时间戳): " + date.getTime());
} catch (ParseException e) {
System.err.println("日期格式解析失败: " + e.getMessage());
}
}
}
重要警告:
SimpleDateFormat 是非线程安全的,如果在多线程环境中共享同一个 SimpleDateFormat 实例,可能会导致数据损坏或解析异常,如果必须在旧版代码中使用,请为每个线程创建新的实例,或者使用 ThreadLocal 来封装它。
常见格式模式字母
在 DateTimeFormatter 和 SimpleDateFormat 中,使用特定的字母来表示日期和时间的部分。
| 字母 | 含义 | 示例 |
|---|---|---|
y |
年 | yyyy (2025), yy (23) |
M |
月 | MM (10), M (10), MMM (Oct), MMMM (October) |
d |
日 | dd (27), d (27) |
H |
小时 (24小时制) | HH (15) |
h |
小时 (12小时制) | hh (03) |
m |
分钟 | mm (30) |
s |
秒 | ss (45) |
a |
上午/下午标记 | a (PM) |
z |
时区名称 | z (GMT+08:00, CST) |
总结与建议
| 特性 | java.time (推荐) |
java.util.Date (不推荐) |
|---|---|---|
| 线程安全 | 是 (所有类都是不可变的) | 否 (SimpleDateFormat 是问题根源) |
| API 设计 | 清晰、丰富、类型安全 (如 LocalDate, LocalTime) |
混乱,Date 类同时包含日期和时间 |
| 时区处理 | 强大的 ZonedDateTime 和 ZoneId |
处理起来很麻烦 |
| 可读性 | 方法名如 getYear(), plusDays() 更直观 |
方法名如 getYear() 已废弃,使用 getTime() 获取时间戳 |
| 版本要求 | Java 8+ | Java 1.1+ |
最终建议:
- 新项目:请务必使用
java.time包,这是未来的方向,也是业界公认的最佳实践。 - 维护旧项目:如果代码库已经大量使用了
java.util.Date和SimpleDateFormat,在修改时需要特别注意线程安全问题,如果有机会,可以考虑逐步将代码迁移到java.time。
