java.util.Date:月份 0-based、非線程安全、可變、設計混亂 (既有日期又有時間)。java.time (JSR-310):不可變、線程安全、領域模型清晰 (LocalDate/Time/DateTime/ZonedDateTime/Instant/Duration/Period)。| 類 | 含義 | 典型用途 |
|---|---|---|
LocalDate | 只有日期 (年月日) | 生日、合約生效日 |
LocalTime | 只有時間 (時分秒納秒) | 鬧鐘、營業時間 |
LocalDateTime | 日期+時間 (無時區) | 本地會議時間、日誌時間戳 |
ZonedDateTime | 日期+時間+時區 | 跨國航班、國際會議 |
Instant | 時間線上的瞬間 (UTC) | 系統事件、資料庫 TIMESTAMP |
Duration | 時間量 (秒/納秒) | 代碼執行耗時、倒計時 |
Period | 日期量 (年/月/日) | 年齡、訂閱週期 |
// 創建
LocalDate today = LocalDate.now();
LocalDate christmas = LocalDate.of(2024, 12, 25);
LocalDate parsed = LocalDate.parse("2024-12-25"); // ISO 格式
LocalTime now = LocalTime.now();
LocalTime noon = LocalTime.of(12, 0);
LocalTime parsedT = LocalTime.parse("12:00:00.123");
LocalDateTime dt = LocalDateTime.now();
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Taipei"));
// 格式化 (線程安全)
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = dt.format(fmt);
LocalDateTime back = LocalDateTime.parse(str, fmt);
// 本月第一天/最後一天
LocalDate first = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDate last = today.with(TemporalAdjusters.lastDayOfMonth());
// 下一個周五
LocalDate nextFri = today.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
// 今年第一個周一
LocalDate firstMon = LocalDate.of(2024, 1, 1).with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
// 加減
LocalDate tomorrow = today.plusDays(1);
LocalDateTime in2Hours = LocalDateTime.now().plusHours(2);
Period age = Period.between(LocalDate.of(1990, 5, 1), today);
Duration elapsed = Duration.between(Instant.now(), Instant.now().plusSeconds(3600));
// 寫入
PreparedStatement ps = conn.prepareStatement("INSERT INTO events (event_time) VALUES (?)");
ps.setObject(1, LocalDateTime.now()); // 自動映射到 TIMESTAMP
// 讀取
ResultSet rs = stmt.executeQuery("SELECT event_time FROM events");
if (rs.next()) {
LocalDateTime ldt = rs.getObject(1, LocalDateTime.class); // 直接獲取對象
}
zdt.withZoneSameInstant(userZoneId)。TIMESTAMP WITH TIME ZONE (PostgreSQL) 或統一存 UTC DATETIME (MySQL) + 應用層處理時區。LocalDateTime 不帶時區,不可直接跨時區比較,需先 .atZone(zoneId) 轉為 ZonedDateTime。Period 處理年月日,Duration 處理時分秒,**不可混用**。DateTimeFormatter 為不可變且線程安全,可定義為 static final 常量。Instant.toString()) 或長整數毫秒數,避免時區歧義。Java 8 時間 API 以「不可變、領域驅動、線程安全」三大特性徹底解決舊 API 痛點。掌握 LocalDate/Time/DateTime 三兄弟與 TemporalAdjusters、DateTimeFormatter、ZoneId 即可應對 95% 以上業務場景。新專案**強制禁用** java.util.Date 與 Calendar。