1. 核心概念
java.util.Date
:表示特定时间点(毫秒级精度),内部存储自 1970-01-01 00:00:00 UTC 的毫秒数。String
:日期的可读文本表示形式,如"2025-08-15 10:47:00"
。- 转换目标:将机器可读的时间戳转换为人类可读的字符串格式。
2. 操作步骤(非常详细)
方法一:使用 SimpleDateFormat
(传统方式)
步骤 1:创建 Date
对象
import java.util.Date;
Date date = new Date(); // 当前时间
// 或 Date date = new Date(1672531200000L); // 指定时间戳
步骤 2:定义日期格式模式
import java.text.SimpleDateFormat;
// 常见格式示例
String pattern = "yyyy-MM-dd HH:mm:ss"; // 2025-08-15 10:47:00
// String pattern = "dd/MM/yyyy"; // 15/08/2025
// String pattern = "yyyy年MM月dd日 HH时mm分"; // 2025年08月15日 10时47分
// String pattern = "EEEE, MMMM dd, yyyy"; // Friday, August 15, 2025
步骤 3:创建 SimpleDateFormat
实例
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
步骤 4:设置时区(可选但推荐)
import java.util.TimeZone;
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
// 或 sdf.setTimeZone(TimeZone.getDefault());
步骤 5:执行转换
String dateString = sdf.format(date);
System.out.println(dateString); // 输出: 2025-08-15 10:47:00
完整示例
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class DateToStringExample {
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
String result = sdf.format(date);
System.out.println("格式化结果: " + result);
}
}
方法二:使用 Java 8+ DateTimeFormatter
(推荐)
步骤 1:将 Date
转为 LocalDateTime
import java.time.LocalDateTime;
import java.time.ZoneId;
LocalDateTime localDateTime = date.toInstant()
.atZone(ZoneId.of("Asia/Shanghai"))
.toLocalDateTime();
步骤 2:创建 DateTimeFormatter
import java.time.format.DateTimeFormatter;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
步骤 3:格式化为字符串
String dateString = localDateTime.format(formatter);
完整示例(推荐)
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
public class ModernDateToString {
public static void main(String[] args) {
Date date = new Date();
// 1. Date → LocalDateTime (指定时区)
LocalDateTime ldt = date.toInstant()
.atZone(ZoneId.of("Asia/Shanghai"))
.toLocalDateTime();
// 2. 创建格式化器
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 3. 转换
String result = ldt.format(formatter);
System.out.println("现代方式结果: " + result);
}
}
3. 常用日期格式模式
模式 | 含义 | 示例 |
---|---|---|
yyyy |
四位年份 | 2025 |
MM |
两位月份 | 08 |
dd |
两位日期 | 15 |
HH |
24小时制小时 | 10 |
hh |
12小时制小时 | 10 |
mm |
分钟 | 47 |
ss |
秒 | 00 |
SSS |
毫秒 | 123 |
E 或 EEE |
星期几(短) | Fri |
EEEE |
星期几(长) | Friday |
a |
上午/下午 | AM |
z |
时区名称 | CST |
Z |
RFC 822 时区 | +0800 |
常用组合:
"yyyy-MM-dd" // 2025-08-15
"yyyy-MM-dd HH:mm:ss" // 2025-08-15 10:47:00
"yyyy-MM-dd HH:mm:ss.SSS" // 2025-08-15 10:47:00.123
"dd/MM/yyyy" // 15/08/2025
"MMM dd, yyyy" // Aug 15, 2025
"EEEE, MMMM dd, yyyy" // Friday, August 15, 2025
4. 常见错误
4.1 大小写错误
// ❌ 错误:mm 是分钟,不是月份
new SimpleDateFormat("yyyy-mm-dd"); // 输出: 2025-47-15 (错误!)
// ✅ 正确:MM 表示月份
new SimpleDateFormat("yyyy-MM-dd");
4.2 时区忽略
// ❌ 可能错误:未设置时区,使用系统默认
SimpleDateFormat sdf = new SimpleDateFormat("...");
// 结果依赖服务器时区设置
// ✅ 显式设置时区
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
4.3 线程安全问题
// ❌ 错误:SimpleDateFormat 不是线程安全的
private static SimpleDateFormat sdf = new SimpleDateFormat("...");
// 多线程环境下可能导致数据混乱
4.4 null
值处理
Date date = null;
// sdf.format(date); // 抛出 NullPointerException
5. 注意事项
SimpleDateFormat
线程不安全:不要在多线程环境中共享同一个实例。- 时区重要性:
Date
是 UTC 时间点,显示时需要转换为本地时间。 - 模式字符大小写敏感:
MM
(月) vsmm
(分),HH
(24小时) vshh
(12小时)。 - 性能考虑:频繁创建
SimpleDateFormat
有开销。 - 过时方法:避免使用
Date
的toString()
方法进行格式化。
6. 使用技巧
6.1 封装工具类
public class DateUtils {
private static final DateTimeFormatter DEFAULT_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final ZoneId SHANGHAI = ZoneId.of("Asia/Shanghai");
public static String format(Date date) {
if (date == null) return null;
LocalDateTime ldt = date.toInstant().atZone(SHANGHAI).toLocalDateTime();
return ldt.format(DEFAULT_FORMATTER);
}
public static String format(Date date, String pattern) {
if (date == null) return null;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
LocalDateTime ldt = date.toInstant().atZone(SHANGHAI).toLocalDateTime();
return ldt.format(formatter);
}
}
// 使用
String result = DateUtils.format(new Date());
String custom = DateUtils.format(new Date(), "yyyy年MM月dd日");
6.2 使用 ThreadLocal
(传统方式)
private static final ThreadLocal<SimpleDateFormat> DATE_FORMATTER =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static String formatSafely(Date date) {
return DATE_FORMATTER.get().format(date);
}
6.3 处理异常
public static String formatDateSafely(Date date, String pattern) {
try {
if (date == null) return "N/A";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
LocalDateTime ldt = date.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
return ldt.format(formatter);
} catch (Exception e) {
return "格式化失败: " + e.getMessage();
}
}
7. 最佳实践与性能优化
7.1 最佳实践
- 优先使用
java.time
API:DateTimeFormatter
是线程安全的,API 更清晰。 - 显式指定时区:避免依赖系统默认时区。
- 预定义格式常量:
public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
- 处理
null
值:在工具方法中进行空值检查。
7.2 性能优化
- 缓存
DateTimeFormatter
:它是不可变且线程安全的,可全局缓存。 - 避免循环中创建格式化器:
// ✅ 好:循环外创建 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("..."); for (Date date : dates) { String str = date.toInstant().atZone(zone).toLocalDateTime().format(formatter); }
- 使用 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); // 线程安全且性能好
8. 总结
方法 | 优点 | 缺点 | 推荐程度 |
---|---|---|---|
SimpleDateFormat |
传统,广泛支持 | 线程不安全,API 混乱 | ⚠️ 仅用于维护旧代码 |
DateTimeFormatter |
线程安全,API 清晰,性能好 | 需要 Java 8+ | ✅ 强烈推荐 |
FastDateFormat |
线程安全,性能优异 | 需要第三方库 | ✅ 推荐(尤其在 Java 7 环境) |
核心要点:
- 推荐使用
java.time
方案:Date → Instant → LocalDateTime → String
- 必须处理时区:明确指定目标时区(如
"Asia/Shanghai"
) - 避免
SimpleDateFormat
共享:多线程环境下会出错 - 封装常用逻辑:创建工具类提高代码复用性
一行代码转换(推荐):
String result = date.toInstant()
.atZone(ZoneId.of("Asia/Shanghai"))
.toLocalDateTime()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));