日期時間處理是業務開發高頻剛需。Java 早期 java.util.Date、Calendar、SimpleDateFormat 雖被 Java 8 新 API 取代,但在大量存量專案中仍廣泛使用。本文整理完整工具類 VeDate,涵蓋格式化、解析、計算、邊界獲取等核心場景,並附帶 Java 8 遷移指引。
package com.util;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class VeDate {
// ===== 格式化常量 =====
public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static final String DATE_PATTERN = "yyyy-MM-dd";
public static final String TIME_PATTERN = "HH:mm:ss";
public static final String COMPACT_DATETIME = "yyyyMMdd HHmmss";
// ===== 獲取當前時間 =====
public static Date getNowDate() {
return parseDate(formatDate(new Date(), DATETIME_PATTERN), DATETIME_PATTERN);
}
public static Date getNowDateShort() {
return parseDate(formatDate(new Date(), DATE_PATTERN), DATE_PATTERN);
}
public static String getStringDate() {
return formatDate(new Date(), DATETIME_PATTERN);
}
public static String getStringDateShort() {
return formatDate(new Date(), DATE_PATTERN);
}
public static String getTimeShort() {
return formatDate(new Date(), TIME_PATTERN);
}
// ===== 核心格式化/解析 =====
public static String formatDate(Date date, String pattern) {
if (date == null) return null;
return new SimpleDateFormat(pattern).format(date);
}
public static Date parseDate(String strDate, String pattern) {
if (strDate == null) return null;
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
ParsePosition pos = new ParsePosition(0);
return sdf.parse(strDate, pos);
}
// ===== 長/短格式互轉 =====
public static Date strToDateLong(String strDate) {
return parseDate(strDate, DATETIME_PATTERN);
}
public static String dateToStrLong(Date date) {
return formatDate(date, DATETIME_PATTERN);
}
public static String dateToStr(Date date) {
return formatDate(date, DATE_PATTERN);
}
public static Date strToDate(String strDate) {
return parseDate(strDate, DATE_PATTERN);
}
public static Date getNow() {
return new Date();
}
// ===== 相對時間計算 =====
public static Date getLastDate(long daysAgo) {
Date date = new Date();
long ms = date.getTime() - daysAgo * 24 * 60 * 60 * 1000L;
return new Date(ms);
}
public static String getStringToday() {
return formatDate(new Date(), COMPACT_DATETIME);
}
// ===== 本月最後一天 =====
public static String getDefaultDay() {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_MONTH, 1); // 本月 1 號
cal.add(Calendar.MONTH, 1); // 下月 1 號
cal.add(Calendar.DAY_OF_MONTH, -1); // 本月最後一天
return formatDate(cal.getTime(), DATE_PATTERN);
}
// ===== 提取時/分 =====
public static String getHour() {
String dt = formatDate(new Date(), DATETIME_PATTERN);
return dt.substring(11, 13); // HH
}
public static String getMinute() {
String dt = formatDate(new Date(), DATETIME_PATTERN);
return dt.substring(14, 16); // mm
}
}
⚠️ 警告:
SimpleDateFormat非線程安全!不可作為 static 共享實例。工具類中每次new SimpleDateFormat(pattern)雖有性能開銷,但保證安全。高併發場景建議:
- 使用
ThreadLocal<SimpleDateFormat>緩存- 或升級 Java 8
DateTimeFormatter(線程安全、不可變)
Calendar cal = Calendar.getInstance();
cal.setTime(new Date()); // 設置基準時間
cal.add(Calendar.MONTH, 1); // 月份 +1(自動處理跨年、天數差異)
cal.add(Calendar.DAY_OF_MONTH, -1); // 日期 -1
Date result = cal.getTime();
add() 而非 set() 做相對運算,自動進位/借位Calendar.DAY_OF_MONTH 等同 Calendar.DATECalendar.JANUARY = 0String input = "2024-01-15 14:30:45 Extra";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ParsePosition pos = new ParsePosition(0);
Date date = sdf.parse(input, pos);
// pos.getErrorIndex() == -1 表示解析成功
// pos.getIndex() 返回解析到的字符位置(可用於繼續解析後續內容)
| 場景 | 代碼示例 |
|---|---|
| 字串轉 Date(多格式兼容) | Date d = parseDateMulti(str, "yyyy-MM-dd", "yyyy/MM/dd", "yyyy.MM.dd"); |
| 計算兩日期天數差 | long days = (d1.getTime() - d2.getTime()) / (24*60*60*1000); |
| 判斷是否同一天 | formatDate(d1, "yyyyMMdd").equals(formatDate(d2, "yyyyMMdd")) |
| 獲取本週一/週日 | cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); |
| 時間戳轉日期 | new Date(timestamp) / Instant.ofEpochMilli(ts) (Java 8) |
| 格式化輸出帶時區 | sdf.setTimeZone(TimeZone.getTimeZone("GMT+8")); |
| 舊 API | Java 8 替代 | 優勢 |
|---|---|---|
java.util.Date | java.time.Instant / LocalDateTime | 不可變、線程安全、納秒精度 |
Calendar | ZonedDateTime / LocalDate | 流式 API、時區支持完善 |
SimpleDateFormat | DateTimeFormatter | 線程安全、預定義格式豐富 |
// 當前時間
LocalDateTime.now() // VeDate.getNowDate()
LocalDate.now() // VeDate.getNowDateShort()
// 格式化
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime.now().format(dtf) // VeDate.getStringDate()
// 解析
LocalDateTime.parse("2024-01-15 14:30", dtf) // VeDate.strToDateLong()
// 日期運算
LocalDate.now().plusMonths(1).minusDays(1) // VeDate.getDefaultDay()
// 區間計算
ChronoUnit.DAYS.between(start, end) // 天數差
// 時區處理
ZonedDateTime.now(ZoneId.of("Asia/Shanghai"))
Date.getYear() 已棄用,返回年份-1900 → 用 Calendar.YEAR 或 Java 8 getYear()Calendar.MONTH 0-based 易導致月份差一 → 寫常量或註釋new Date() 獲取的是 JVM 默認時區時間 → 顯式設置 TimeZone 或用 ZonedDateTimeSystem.currentTimeMillis() 毫秒,JS Date.now() 毫秒,Linux date +%s 秒 → 注意轉換總結:VeDate 等工具類解決了舊 API 時代的高頻需求,但新專案強烈建議直接使用 Java 8 java.time 包。存量專案維護時,注意線程安全、時區、棄用方法三大坑,漸進式遷移至新 API。