首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 日期時間獲取最佳實踐:LocalDateTime、Instant、DateTimeFormatter 現代用法

     編輯:Java 程式設計入門

現代 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 整合

避坑指南

  1. 別用 SimpleDateFormat:非線程安全,用 DateTimeFormatter
  2. 別用 Calendar:可變、API 反直覺,用 LocalDateTime/ZonedDateTime
  3. 時區明確化:存儲用 UTC (Instant),顯示用用戶時區 (ZonedDateTime)
  4. 序列化格式統一:API 統一輸出 ISO-8601 (yyyy-MM-dd'T'HH:mm:ss.SSSZ)
  5. 周邊庫升級: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,代碼審查時強制檢查。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景方案
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()