現代 API 三大核心類
| 類 | 用途 | 特點 |
| LocalDateTime | 本地日期時間(無時區) | 年月日時分秒納秒、不可變、線程安全 |
| Instant | 時間戳(UTC 時間線上的點) | 納秒精度、機器時間、適合存儲/傳輸/日誌 |
| ZonedDateTime | 帶時區日期時間 | 包含 ZoneId、處理夏令時、適合用戶顯示 |
獲取當前時間
// 本地時間(系統默認時區)
LocalDateTime now = LocalDateTime.now();
// 指定時區
ZonedDateTime nowTokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
// 時間戳(UTC)
Instant instant = Instant.now(); // 2024-01-15T08:30:45.123456789Z
// 互轉
LocalDateTime ldt = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
Instant instant2 = ldt.atZone(ZoneId.systemDefault()).toInstant();
格式化與解析
// 預定義格式化器(線程安全、可復用)
DateTimeFormatter ISO = DateTimeFormatter.ISO_LOCAL_DATE_TIME; // 2024-01-15T08:30:45
DateTimeFormatter CUSTOM = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter CHINESE = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH時mm分ss秒", Locale.CHINESE);
// 格式化
String formatted = LocalDateTime.now().format(CUSTOM);
// 解析
LocalDateTime parsed = LocalDateTime.parse("2024-01-15 08:30:45", CUSTOM);
// 解析帶時區
ZonedDateTime zdt = ZonedDateTime.parse("2024-01-15T08:30:45+08:00[Asia/Shanghai]");
常用計算操作
LocalDateTime now = LocalDateTime.now();
// 加減時間
LocalDateTime yesterday = now.minusDays(1);
LocalDateTime nextWeek = now.plusWeeks(1);
LocalDateTime nextMonth = now.plusMonths(1);
LocalDateTime specific = now.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0); // 本月第一天 00:00:00
// 期間計算
Period period = Period.between(now.toLocalDate(), nextMonth.toLocalDate()); // 年月日差
Duration duration = Duration.between(now, nextWeek); // 時分秒納秒差
// 比較
boolean isBefore = now.isBefore(nextWeek);
boolean isAfter = now.isAfter(yesterday);
實用工具類封裝
public final class DateTimeUtils {
private DateTimeUtils() {}
public static final DateTimeFormatter STANDARD = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static final DateTimeFormatter DATE_ONLY = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static final DateTimeFormatter TIME_ONLY = DateTimeFormatter.ofPattern("HH:mm:ss");
// 當前時間字串
public static String now() { return LocalDateTime.now().format(STANDARD); }
public static String today() { return LocalDate.now().format(DATE_ONLY); }
// 相對時間
public static String yesterday() { return LocalDate.now().minusDays(1).format(DATE_ONLY); }
public static String tomorrow() { return LocalDate.now().plusDays(1).format(DATE_ONLY); }
// 週/月邊界
public static LocalDate firstDayOfMonth() { return LocalDate.now().withDayOfMonth(1); }
public static LocalDate lastDayOfMonth() { return LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()); }
public static LocalDate firstDayOfWeek() { return LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); }
// 解析/格式化
public static LocalDateTime parse(String str) { return LocalDateTime.parse(str, STANDARD); }
public static String format(LocalDateTime ldt) { return ldt.format(STANDARD); }
public static String format(LocalDateTime ldt, DateTimeFormatter fmt) { return ldt.format(fmt); }
// 時區轉換
public static ZonedDateTime toZone(LocalDateTime ldt, String zoneId) {
return ldt.atZone(ZoneId.of(zoneId));
}
// 友好顯示(如「3分鐘前」、「昨天 14:30」)
public static String friendly(LocalDateTime ldt) {
Duration dur = Duration.between(ldt, LocalDateTime.now());
long seconds = dur.getSeconds();
if (seconds < 60) return "剛剛";
if (seconds < 3600) return seconds / 60 + "分鐘前";
if (seconds < 86400) return seconds / 3600 + "小時前";
if (seconds < 172800) return "昨天 " + ldt.format(TIME_ONLY);
return ldt.format(DATE_ONLY);
}
}
與舊 API 互操作
// Date → LocalDateTime
Date date = new Date();
LocalDateTime ldt = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
// LocalDateTime → Date
Date date2 = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
// Calendar → LocalDateTime
Calendar cal = Calendar.getInstance();
LocalDateTime ldt2 = cal.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
// sql.Timestamp → LocalDateTime
Timestamp ts = new Timestamp(System.currentTimeMillis());
LocalDateTime ldt3 = ts.toLocalDateTime(); // JDBC 4.2+ 直接支援
資料庫與 JSON 整合
| 場景 | 方案 |
| MyBatis | 自動支援 java.time 類型(MyBatis 3.5+),無需 TypeHandler |
| JPA/Hibernate | @Column(columnDefinition = "TIMESTAMP") + LocalDateTime |
| Jackson | 啟用 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS=false,輸出 ISO 字串 |
| Gson | 註冊 LocalDateTimeAdapter 或用 GsonBuilder().registerTypeAdapter() |
避坑指南
- 別用 SimpleDateFormat:非線程安全,用 DateTimeFormatter
- 別用 Calendar:可變、API 反直覺,用 LocalDateTime/ZonedDateTime
- 時區明確化:存儲用 UTC (Instant),顯示用用戶時區 (ZonedDateTime)
- 序列化格式統一:API 統一輸出 ISO-8601 (yyyy-MM-dd'T'HH:mm:ss.SSSZ)
- 周邊庫升級:JDBC 4.2、Jackson 2.8+、Hibernate 5.2+ 完整支援 java.time
總結
Java 8 java.time 包徹底解決了舊 API 的線程安全、可變性、API 設計混亂等歷史包袱。**核心原則:存儲用 Instant,計算用 LocalDateTime/Duration,顯示用 ZonedDateTime/DateTimeFormatter**。封裝工具類統一格式常數,專案中禁用 Date/Calendar/SimpleDateFormat,代碼審查時強制檢查。
- 上一篇文章: Java 順序表(動態陣列)手寫實現:泛型、擴容、增刪查完整源碼解析
- 下一篇文章: Java 迭代器模式深度解析:從介面設計到自定義集合實戰
Copyright ©
程式碼之家 All Rights Reserved