1. java.util.Date

1.1 基本概念

Date 类表示特定的瞬间,精确到毫秒。它内部存储的是自 UTC 时间 1970 年 1 月 1 日 00:00:00 以来的毫秒数(称为 Unix 时间戳或纪元时间)。

1.2 常用构造方法

// 当前时间
Date now = new Date();

// 指定毫秒数
Date specificTime = new Date(1672531200000L); // 2023-01-01 00:00:00 UTC

// 已过时的构造方法(不推荐使用)
Date oldWay = new Date(123, 0, 1); // 2023年1月1日 (年份-1900, 月份0-11)

1.3 常用方法

Date date = new Date();

// 获取时间戳
long timestamp = date.getTime();

// 比较日期
boolean isBefore = date.before(anotherDate);
boolean isAfter = date.after(anotherDate);

// 修改时间(已过时,不推荐)
date.setTime(1672531200000L);

注意Date 类的许多方法(如 setYear(), getYear() 等)已过时,应避免使用。


2. java.text.SimpleDateFormat

SimpleDateFormatDateFormat 的子类,用于格式化(日期 → 字符串)和解析(字符串 → 日期)日期。

2.1 基本用法

格式化(Date → String)

import java.text.SimpleDateFormat;
import java.util.Date;

Date now = new Date();

// 创建格式化器
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

// 格式化日期
String formattedDate = sdf.format(now);
System.out.println(formattedDate); // 输出: 2025-08-15 10:47:00 (示例)

解析(String → Date)

String dateString = "2023-01-01 12:30:45";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

try {
    Date parsedDate = sdf.parse(dateString);
    System.out.println(parsedDate); // 输出: Sun Jan 01 12:30:45 CST 2023
} catch (ParseException e) {
    e.printStackTrace();
}

2.2 常用日期格式模式

模式 描述 示例
yyyy 四位年份 2025
MM 两位月份 08
dd 两位日期 15
HH 24小时制小时 10
mm 分钟 47
ss 00
SSS 毫秒 123
E 星期几(短) Fri
EEE 星期几(短) Fri
EEEE 星期几(长) Friday
a 上午/下午 AM
z 时区 CST
Z RFC 822 时区 +0800

常见格式示例

// 常用格式
new SimpleDateFormat("yyyy-MM-dd");           // 2025-08-15
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");   // 2025-08-15 10:47:00
new SimpleDateFormat("dd/MM/yyyy");           // 15/08/2025
new SimpleDateFormat("yyyy年MM月dd日");         // 2025年08月15日
new SimpleDateFormat("EEEE, MMMM dd, yyyy");  // Friday, August 15, 2025

3. 使用技巧

3.1 设置时区

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
// 或
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));

3.2 设置本地化

SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 EEEE", Locale.CHINESE);

3.3 自定义异常处理

public static Date parseDateSafely(String dateStr, String pattern) {
    try {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        return sdf.parse(dateStr);
    } catch (ParseException e) {
        System.err.println("日期解析失败: " + dateStr);
        return null;
    }
}

4. 常见错误

4.1 ParseException

// 错误:字符串格式与模式不匹配
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2023/01/01"); // 抛出 ParseException

// 正确:确保格式匹配
Date date = sdf.parse("2023-01-01");

4.2 大小写敏感

// 错误:使用小写 mm 表示月份
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd"); // mm 是分钟!

// 正确:使用 MM 表示月份
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

4.3 线程安全问题

// ❌ 错误:SimpleDateFormat 不是线程安全的
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

// ✅ 正确做法:
// 1. 每次使用都创建新的实例
// 2. 使用 ThreadLocal
// 3. 使用同步块
// 4. 使用 Java 8+ 的 DateTimeFormatter

4.4 闰秒和夏令时问题

SimpleDateFormat 在处理闰秒和夏令时转换时可能产生意外结果,需特别注意。


5. 注意事项

  1. 线程安全性SimpleDateFormat 不是线程安全的。在多线程环境中共享同一个实例会导致数据混乱。
  2. 性能:频繁创建 SimpleDateFormat 实例有一定开销,建议在单线程环境中重用,或使用 ThreadLocal
  3. 时区处理:默认使用系统时区,跨时区应用需显式设置。
  4. 闰年处理:正确处理闰年(2月29日),但需确保输入格式正确。
  5. 国际化:不同 Locale 下的月份和星期名称显示不同。

6. 最佳实践与性能优化

6.1 线程安全解决方案

使用 ThreadLocal

private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER = 
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

public static String formatDate(Date date) {
    return DATE_FORMATTER.get().format(date);
}

public static Date parseDate(String dateStr) throws ParseException {
    return DATE_FORMATTER.get().parse(dateStr);
}

使用同步

private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

public static synchronized Date parseDate(String dateStr) throws ParseException {
    return sdf.parse(dateStr);
}

6.2 预定义格式常量

public class DateFormats {
    public static final String DATE = "yyyy-MM-dd";
    public static final String DATETIME = "yyyy-MM-dd HH:mm:ss";
    public static final String TIMESTAMP = "yyyy-MM-dd HH:mm:ss.SSS";
    
    // 使用
    SimpleDateFormat sdf = new SimpleDateFormat(DATE);
}

6.3 使用 Java 8+ DateTimeFormatter(推荐)

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
String formatted = now.format(formatter);

// 解析
LocalDateTime parsed = LocalDateTime.parse("2025-08-15 10:47:00", formatter);

优势:不可变、线程安全、功能更强大、API 更清晰。

6.4 性能优化建议

  1. 避免在循环中创建 SimpleDateFormat:在循环外创建并重用(单线程环境)。
  2. 使用缓存:对于常用格式,可以缓存 SimpleDateFormat 实例(配合 ThreadLocal)。
  3. 考虑使用 FastDateFormat(Apache Commons Lang):
    import org.apache.commons.lang3.time.FastDateFormat;
    
    FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");
    String formatted = fdf.format(date);
    

7. 总结

特性 Date + SimpleDateFormat java.time (Java 8+)
线程安全 SimpleDateFormat 不安全 所有类都不可变且线程安全
API 清晰度 较混乱,方法过时 清晰、直观
功能完整性 基本功能 丰富(时区、周期、持续时间等)
性能 中等 通常更好
推荐程度 维护旧代码 新项目首选

核心要点

  1. Date:表示时间点,内部是毫秒时间戳。
  2. SimpleDateFormat:用于格式化和解析,但不是线程安全的
  3. 格式模式:注意大小写(MM 月份 vs mm 分钟)。
  4. 线程安全:多线程环境下必须使用 ThreadLocal 或同步。
  5. 现代替代强烈推荐在新项目中使用 java.time 包(LocalDateTime, ZonedDateTime, DateTimeFormatter)。