傳統 Date/Calendar 方案(維護項目常見)
常用格式化模式
| 需求 | 代碼示例 | 輸出示例 |
| 標準日期時間 | DateFormat.getDateTimeInstance(2, 2, Locale.CHINESE).format(new Date()) | 2024-01-15 14:30:45 |
| 中文長格式 | DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.CHINESE).format(new Date()) | 2024年1月15日 下午2時30分45秒 |
| 毫秒精度 | new Timestamp(System.currentTimeMillis()).toString() | 2024-01-15 14:30:45.123 |
SimpleDateFormat 模式字符對照表
| 字母 | 含義 | 示例 |
| y | 年 | 2024 |
| M | 月 | 01 |
| d | 日 | 15 |
| H | 時(24制) | 14 |
| m | 分 | 30 |
| s | 秒 | 45 |
| S | 毫秒 | 123 |
| E | 星期 | 週一 |
| a | 上午/下午 | 下午 |
字符串↔日期互轉
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// String → Date
Date date = sdf.parse("2024-01-15 14:30:45");
// Date → String
String str = sdf.format(date);
Calendar 日期運算實戰
常用操作封裝工具類
public class DateUtils {
// 獲取指定格式當前時間
public static String getNow(String pattern) {
return new SimpleDateFormat(pattern).format(new Date());
}
// 獲取 N 月後日期
public static String getAfterMonth(int months) {
Calendar c = Calendar.getInstance();
c.add(Calendar.MONTH, months);
return new SimpleDateFormat("yyyy-MM-dd").format(c.getTime());
}
// 本周一日期
public static String getMondayOfWeek() {
Calendar c = Calendar.getInstance();
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK) - 1; // 周日=1
if (dayOfWeek == 0) dayOfWeek = 7; // 將周日調為7
c.add(Calendar.DATE, 1 - dayOfWeek);
return new SimpleDateFormat("yyyy-MM-dd").format(c.getTime());
}
// 本月第一天/最後一天
public static String getFirstDayOfMonth() {
Calendar c = Calendar.getInstance();
c.set(Calendar.DAY_OF_MONTH, 1);
return new SimpleDateFormat("yyyy-MM-dd").format(c.getTime());
}
public static String getLastDayOfMonth() {
Calendar c = Calendar.getInstance();
c.set(Calendar.DAY_OF_MONTH, 1);
c.add(Calendar.MONTH, 1);
c.add(Calendar.DATE, -1);
return new SimpleDateFormat("yyyy-MM-dd").format(c.getTime());
}
// 兩日期天數差
public static long daysBetween(String d1, String d2) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
long diff = sdf.parse(d1).getTime() - sdf.parse(d2).getTime();
return diff / (24 * 60 * 60 * 1000);
} catch (ParseException e) {
return 0;
}
}
}
現代 Java 8+ Time API(新項目首選)
核心類對照
| 舊 API | 新 API | 說明 |
| Date | Instant | 時間戳 |
| Calendar | ZonedDateTime | 帶時區日期時間 |
| 無 | LocalDateTime | 本地日期時間(無時區) |
| 無 | LocalDate | 本地日期 |
| 無 | LocalTime | 本地時間 |
| SimpleDateFormat | DateTimeFormatter | 線程安全格式化 |
現代 API 實戰示例
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
// 當前時間
LocalDateTime now = LocalDateTime.now();
LocalDate today = LocalDate.now();
// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = now.format(fmt);
// 解析
LocalDateTime parsed = LocalDateTime.parse("2024-01-15T14:30:45", fmt);
// 日期運算
LocalDate nextMonth = today.plusMonths(1);
LocalDate firstDay = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDay = today.with(TemporalAdjusters.lastDayOfMonth());
LocalDate monday = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
// 期間計算
Period period = Period.between(LocalDate.of(2024,1,1), today);
long days = ChronoUnit.DAYS.between(LocalDate.of(2024,1,1), today);
// 時區轉換
ZonedDateTime beijing = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime utc = beijing.withZoneSameInstant(ZoneOffset.UTC);
線程安全陷阱與最佳實踐
SimpleDateFormat 非線程安全
// ❌ 危險:靜態共享會導致解析錯誤
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd");
// ✅ 方案1:ThreadLocal
private static final ThreadLocal TL_SDF =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
// ✅ 方案2:局部變量(推薦簡單場景)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// ✅ 方案3:Java 8 DateTimeFormatter(天然線程安全)
private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd");
時區處理避坑指南
- 存儲統一用 UTC(數據庫 TIMESTAMP/TIMESTAMPTZ)
- 展示時按用戶時區轉換
- 避免在業務邏輯中依賴默認時區
- 跨時區計算使用
ZonedDateTime 或 Instant
總結選型建議
| 場景 | 推薦方案 |
| 維護舊系統 | SimpleDateFormat + Calendar + ThreadLocal |
| 新項目開發 | java.time 全家桶 |
| 高併發解析/格式化 | DateTimeFormatter (線程安全) |
| 複雜日期運算 | TemporalAdjusters / Period / Duration |
| 序列化/數據庫交互 | Instant (UTC 毫秒/納秒) |
日期時間處理看似簡單,實則坑深。拥抱 Java 8 Time API,告別線程安全噩夢,享受不可變、流式、領域驅動的優雅體驗。