- 旧版 API (
java.util.Date和java.text.SimpleDateFormat):不推荐在新代码中使用,但维护旧项目时必须了解。 - 新版 API (
java.time包):自 Java 8 引入,是目前官方推荐、功能强大且线程安全的现代日期时间 API。 - 第三方库 (
Joda-Time):在 Java 8 之前是事实上的标准,现在仍被广泛使用,但新项目应优先考虑java.time。
下面我们分点详细介绍。
旧版 API (已过时,但需了解)
核心类
java.util.Date: 表示一个特定的瞬间,精确到毫秒,它同时包含了日期和时间信息,但很多方法(如getYear(),getMonth())已被废弃,且设计上存在缺陷。java.text.SimpleDateFormat: 用于Date对象的格式化和解析。
格式化示例
SimpleDateFormat 通过模式字母来定义格式。
| 字母 | 含义 | 示例 |
|---|---|---|
y |
年 | yyyy (2025), yy (23) |
M |
月 | MM (08), MMM (Aug), MMMM (August) |
d |
日 | dd (15) |
H |
小时 (24小时制) | HH (14) |
h |
小时 (12小时制) | hh (02) |
m |
分钟 | mm (30) |
s |
秒 | ss (05) |
S |
毫秒 | SSS (123) |
E |
星期几 | EEE (Tue), EEEE (Tuesday) |
a |
上午/下午标记 | AM 或 PM |
代码示例:
import java.text.SimpleDateFormat;
import java.util.Date;
public class LegacyDateTimeFormat {
public static void main(String[] args) {
// 1. 创建一个 Date 对象,代表当前时间
Date now = new Date();
// 2. 创建 SimpleDateFormat 实例,定义格式
// yyyy-MM-dd HH:mm:ss 是最常见的格式
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// yyyy年MM月dd日 HH时mm分ss秒
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
// EEE, MMM d, yyyy
SimpleDateFormat sdf3 = new SimpleDateFormat("EEE, MMM d, yyyy");
// 3. 格式化 Date 对象为字符串
String formattedDate1 = sdf1.format(now);
String formattedDate2 = sdf2.format(now);
String formattedDate3 = sdf3.format(now);
System.out.println("标准格式: " + formattedDate1);
System.out.println("中文格式: " + formattedDate2);
System.out.println("英文格式: " + formattedDate3);
// 4. 解析字符串为 Date 对象
String dateStr = "2025-08-15 14:30:00";
try {
Date parsedDate = sdf1.parse(dateStr);
System.out.println("解析后的 Date 对象: " + parsedDate);
System.out.println("解析后再次格式化: " + sdf1.format(parsedDate));
} catch (Exception e) {
e.printStackTrace();
}
}
}
⚠️ 重要缺点:
SimpleDateFormat是非线程安全的,如果在多线程环境中共享同一个实例,会导致数据错乱,每次使用都new一个新实例又会影响性能。Date类设计混乱,同时包含日期和时间,且时区处理不直观。
新版 API (Java 8+, 强烈推荐)
自 Java 8 起,引入了全新的 java.time 包,彻底解决了旧版 API 的所有问题,它是不可变的,并且是线程安全的。
核心类
LocalDate: 表示一个日期(年、月、日),如2025-08-15。LocalTime: 表示一个时间(时、分、秒、纳秒),如14:30:00。LocalDateTime: 表示一个日期和时间,但不包含时区信息,如2025-08-15T14:30:00。ZonedDateTime: 表示一个带时区的日期和时间,如2025-08-15T14:30:00+08:00[Asia/Shanghai]。DateTimeFormatter: 新的格式化工具类,用于替代SimpleDateFormat。
格式化示例
DateTimeFormatter 同样使用模式字母,但更清晰、更强大。
代码示例:
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class ModernDateTimeFormat {
public static void main(String[] args) {
// 1. 获取当前日期时间
LocalDateTime now = LocalDateTime.now();
// 2. 创建 DateTimeFormatter 实例
// 预定义的格式,如 ISO 标准格式
DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
// 自定义格式
DateTimeFormatter customFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter customFormatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
DateTimeFormatter customFormatter3 = DateTimeFormatter.ofPattern("EEE, MMM d, yyyy HH:mm");
// 3. 格式化 LocalDateTime 为字符串
String formattedIso = now.format(isoFormatter);
String formattedCustom1 = now.format(customFormatter1);
String formattedCustom2 = now.format(customFormatter2);
String formattedCustom3 = now.format(customFormatter3);
System.out.println("ISO 格式: " + formattedIso);
System.out.println("自定义格式1: " + formattedCustom1);
System.out.println("自定义格式2: " + formattedCustom2);
System.out.println("自定义格式3: " + formattedCustom3);
// 4. 解析字符串为 LocalDateTime
String dateTimeStr = "2025-08-15 14:30:00";
try {
LocalDateTime parsedDateTime = LocalDateTime.parse(dateTimeStr, customFormatter1);
System.out.println("解析后的 LocalDateTime: " + parsedDateTime);
} catch (Exception e) {
e.printStackTrace();
}
// 5. 处理带时区的日期时间
ZonedDateTime shanghaiTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
DateTimeFormatter zoneFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
System.out.println("上海时间: " + shanghaiTime.format(zoneFormatter));
}
}
DateTimeFormatter 的优势:
- 线程安全:
DateTimeFormatter是不可变的,可以安全地在多线程环境中共享。 - API 设计优秀:
LocalDate,LocalTime等类职责单一,API 更清晰易用。 - 功能强大: 内置了许多有用的格式(如
ISO_*),并支持轻松地进行日期时间的加减、比较等操作。
第三方库 (Joda-Time)
虽然 Java 8 的 java.time 已经非常优秀,但在 Java 8 之前,Joda-Time 是业界标准,许多现有项目仍在使用它。
核心类
org.joda.time.DateTime: 表示一个带时区的日期时间。org.joda.time.format.DateTimeFormat: 用于格式化和解析。org.joda.time.format.DateTimeFormatter: 格式化器。
代码示例:
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class JodaTimeFormat {
public static void main(String[] args) {
// 1. 获取当前日期时间
DateTime now = new DateTime();
// 2. 创建 DateTimeFormatter 实例
// forPattern() 是最常用的方式
DateTimeFormatter formatter1 = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter formatter2 = DateTimeFormat.forPattern("yyyy年MM月dd日 HH时mm分ss秒");
// 3. 格式化
String formatted1 = now.toString(formatter1);
String formatted2 = now.toString(formatter2);
System.out.println("Joda-Time 格式1: " + formatted1);
System.out.println("Joda-Time 格式2: " + formatted2);
// 4. 解析
String dateStr = "2025-08-15 14:30:00";
DateTime parsedDateTime = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").parseDateTime(dateStr);
System.out.println("Joda-Time 解析后: " + parsedDateTime);
}
}
与 java.time 的关系:
Joda-Time 的设计理念直接影响了 Java 8 的 java.time API,如果你熟悉 Joda-Time,学习 java.time 会非常快,对于新项目,请直接使用 java.time。
总结与对比
| 特性 | 旧版 API (SimpleDateFormat) |
新版 API (DateTimeFormatter) |
Joda-Time (DateTimeFormat) |
|---|---|---|---|
| 推荐度 | 不推荐 (仅维护旧代码) | 强烈推荐 | 可接受 (旧项目) |
| 线程安全 | ❌ 非线程安全 | ✅ 线程安全 | ✅ 线程安全 |
| 核心类 | java.util.Date |
java.time.LocalDate, LocalDateTime 等 |
org.joda.time.DateTime |
| API 设计 | 混乱,职责不清 | 清晰,职责单一 | 优秀,与 java.time 类似 |
| 时区处理 | 复杂 | 简单 (ZonedDateTime) |
简单 |
| Java 版本 | 所有版本 | Java 8+ | 所有版本 (需引入依赖) |
最佳实践
- 新项目必须使用
java.time: 除非你被迫使用旧版 Java,否则请优先选择java.time包。 - 维护旧项目时: 如果遇到
java.util.Date和SimpleDateFormat,理解其工作原理和线程安全问题即可,可以考虑使用java.time中的转换工具(如Date.from(localDateTime.toInstant(ZoneOffset.UTC)))逐步进行迁移。 - 明确需求: 在格式化时,首先确定你需要的是纯日期、纯时间还是日期时间,以及是否需要包含时区信息,然后选择合适的类(
LocalDate,LocalTime,LocalDateTime,ZonedDateTime)。 - 复用
DateTimeFormatter: 由于它是线程安全的,可以在应用启动时创建好,然后作为单例或静态常量复用,避免重复创建。
