日期時間處理是業務開發高頻需求。本文提供生產級 DateUtils 工具類,整合 SimpleDateFormat、Calendar 常用操作,預定義多種中英文格式常數,支援格式化、解析、增減年月日、天數差計算、時間戳產生等功能,並處理執行緒安全與空值保護。
public final class DateUtils {
// 英文格式
public static final String FORMAT_SHORT = "yyyy-MM-dd";
public static final String FORMAT_LONG = "yyyy-MM-dd HH:mm:ss";
public static final String FORMAT_FULL = "yyyy-MM-dd HH:mm:ss.S";
// 中文格式
public static final String FORMAT_SHORT_CN = "yyyy年MM月dd日";
public static final String FORMAT_LONG_CN = "yyyy年MM月dd日 HH时mm分ss秒";
public static final String FORMAT_FULL_CN = "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒";
private DateUtils() {} // 工具類禁止實例化
// ...
public static String format(Date date) {
return format(date, FORMAT_LONG);
}
public static String format(Date date, String pattern) {
if (date == null) return "";
return new SimpleDateFormat(pattern).format(date);
}
public static Date parse(String strDate) {
return parse(strDate, FORMAT_LONG);
}
public static Date parse(String strDate, String pattern) {
if (strDate == null) return null;
try {
return new SimpleDateFormat(pattern).parse(strDate);
} catch (ParseException e) {
e.printStackTrace();
return null;
}
}
public static String getNow() { return format(new Date()); }
public static String getNow(String pattern) { return format(new Date(), pattern); }
public static Date addMonth(Date date, int months) {
if (date == null) return null;
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.MONTH, months);
return cal.getTime();
}
public static Date addDay(Date date, int days) {
if (date == null) return null;
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DATE, days);
return cal.getTime();
}
public static String getTimeString() {
return new SimpleDateFormat(FORMAT_FULL).format(Calendar.getInstance().getTime());
}
public static String getYear(Date date) {
return date == null ? "" : format(date).substring(0, 4);
}
public static int countDays(String dateStr) {
return countDays(dateStr, FORMAT_LONG);
}
public static int countDays(String dateStr, String pattern) {
if (dateStr == null) return 0;
Date target = parse(dateStr, pattern);
if (target == null) return 0;
long diffMs = System.currentTimeMillis() - target.getTime();
return (int)(diffMs / (1000 * 60 * 60 * 24));
}
| 議題 | 處理方式 |
|---|---|
| 執行緒安全 | SimpleDateFormat 非執行緒安全,每次呼叫新建實例,避免共享欄位污染 |
| 空值防護 | 所有公共方法檢查 null,回傳空字串/0/null 而非拋出 NPE |
| 異常處理 | parse 捕獲 ParseException 列印堆疊並回傳 null,呼叫端需自行判斷 |
| 中文格式 | 直接在 pattern 中嵌入中文字符,SimpleDateFormat 原生支援 |
| 效能 | 高頻呼叫建議改用 ThreadLocal 或 Java 8 DateTimeFormatter |
新專案強烈建議使用
java.time套件 (LocalDateTime,DateTimeFormatter,ChronoUnit),不可變、執行緒安全、API 更豐富。
// Java 8+ 等價操作範例
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String now = LocalDateTime.now().format(fmt);
LocalDateTime parsed = LocalDateTime.parse("2024-01-15 14:30:00", fmt);
LocalDateTime plusMonths = parsed.plusMonths(3);
long daysBetween = ChronoUnit.DAYS.between(parsed.toLocalDate(), LocalDate.now());
| 舊 API (DateUtils) | 新 API (java.time) | 優勢 |
|---|---|---|
Date | Instant / LocalDateTime / ZonedDateTime | 不可變、明確時區 |
Calendar | TemporalAdjusters / plus/minus | 鏈式呼叫、不可變 |
SimpleDateFormat | DateTimeFormatter | 執行緒安全、預編譯快取 |
countDays | ChronoUnit.DAYS.between() | 精確處理閏年/夏令時間 |
DateUtils,內部逐步遷移至 java.time 並轉換 Date.from(instant)LocalDateTime (需適配器)java.time (需 jackson-datatype-jsr310)此 DateUtils 為傳統專案提供完整、開箱即用的日期處理能力;新專案請直接擁抱 java.time 現代 API。