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

Java 獲取當前日期時間的正確姿勢:告別 Date,擁抱 java.time

     編輯:Java 程式設計入門

歷史包袱:為什麼棄用 java.util.Date/Calendar?

  • 線程不安全:`SimpleDateFormat`、`Calendar` 非線程安全,併發環境下易出錯。
  • 設計缺陷:月份 0-11、年份從 1900 起、可變性導致 Bug。
  • 時區處理痛苦:`Date` 無時區概念,`Calendar` 時區 API 反直覺。
  • 不可變性缺失:修改操作會改變原對象,不可用於函數式編程。

現代標準:java.time (JDK 8+ JSR-310)

核心類均為 不可變、線程安全,領域模型清晰:

類含義典型場景
Instant時間線上的瞬間 (UTC、納秒精度)時間戳、日誌記錄、數據庫存儲
LocalDate無時區日期 (年月日)生日、合同簽署日、截止日期
LocalTime無時區時間 (時分秒納秒)鬧鐘、營業時間、每日定時任務
LocalDateTime無時區日期時間本地排程、表單提交時間
ZonedDateTime帶時區日期時間用戶顯示、跨時區業務、定時任務調度
OffsetDateTime帶偏移量日期時間API 交互、數據庫 TIMESTAMP WITH TIME ZONE
Duration時間間隔 (秒/納秒)執行耗時、倒計時、TTL
Period日期間隔 (年月日)年齡計算、訂閱周期

核心操作速查

1. 獲取當前時間


// 當前瞬間 (UTC)
Instant nowInstant = Instant.now(); 

// 系統默認時區的日期時間
LocalDate today = LocalDate.now();
LocalTime nowTime = LocalTime.now();
LocalDateTime nowDateTime = LocalDateTime.now();
ZonedDateTime nowZoned = ZonedDateTime.now(); // 推薦:含時區信息

// 指定時區 (如中國標準時間 UTC+8)
ZoneId shanghai = ZoneId.of("Asia/Shanghai");
ZonedDateTime shanghaiNow = ZonedDateTime.now(shanghai);

2. 格式化與解析


DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = nowDateTime.format(formatter); // "2024-03-15 14:30:05"

LocalDateTime parsed = LocalDateTime.parse("2024-03-15 14:30:05", formatter);

// ISO 標準格式 (無需定義 formatter)
String iso = nowInstant.toString(); // "2024-03-15T06:30:05.123Z"
Instant parsedInstant = Instant.parse(iso);

3. 日期時間運算 (不可變,返回新對象)


LocalDate nextWeek = today.plusWeeks(1);
LocalDate lastMonth = today.minusMonths(1);
LocalDateTime in3Hours = nowDateTime.plusHours(3);
ZonedDateTime in45Days = nowZoned.plusDays(45);

// 精確調整
LocalDate firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());
LocalDate nextFriday = today.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));

4. 時區轉換


// 同一瞬間,不同時區的表達
ZonedDateTime nyTime = shanghaiNow.withZoneSameInstant(ZoneId.of("America/New_York"));

// 僅改變時區標譤,保持本地時間不變 (極少用)
ZonedDateTime sameLocal = shanghaiNow.withZoneSameLocal(ZoneId.of("UTC"));

5. 間隔計算


// 兩個時間點之間的間隔
Duration duration = Duration.between(startInstant, endInstant);
long millis = duration.toMillis();
long seconds = duration.getSeconds();

Period period = Period.between(startDate, endDate);
int years = period.getYears();
int months = period.getMonths();
int days = period.getDays();

6. 舊 API 互操作


// Date -> Instant
Instant instant = oldDate.toInstant();
Date date = Date.from(instant);

// Calendar -> ZonedDateTime
ZonedDateTime zdt = calendar.toInstant().atZone(calendar.getTimeZone().toZoneId());

// sql.Date / sql.Timestamp
java.sql.Date sqlDate = java.sql.Date.valueOf(localDate);
LocalDate localDate = sqlDate.toLocalDate();
Timestamp ts = Timestamp.valueOf(localDateTime);
LocalDateTime ldt = ts.toLocalDateTime();

實戰最佳實踐

數據庫存儲策略

  • 統一存儲 UTC Instant (對應 `TIMESTAMP` 或 `BIGINT` 毫秒數)。
  • 應用層根據用戶時區轉換為 `ZonedDateTime` 顯示。
  • MyBatis/JPA 自動映射 `LocalDateTime`/`Instant`,配置 `jdbcTimezone=UTC`。

定時任務調度


// Quartz / Spring Task 使用 ZonedDateTime 避免夏令時陷阱
@Scheduled(cron = "0 0 3 * * ?", zone = "Asia/Shanghai")
public void dailyJob() { ... }

序列化傳輸

  • JSON (Jackson) 默認序列化 `LocalDateTime` 為字符串,配置 `SerializationFeature.WRITE_DATES_AS_TIMESTAMPS = false`。
  • 跨語言交互統一用 ISO-8601 字符串 (`yyyy-MM-dd'T'HH:mm:ss.SSSZ`)。

線程安全工具類


public final class DateUtils {
    private static final DateTimeFormatter DEFAULT_FMT = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final ZoneId DEFAULT_ZONE = ZoneId.of("Asia/Shanghai");

    public static String now() { 
        return LocalDateTime.now(DEFAULT_ZONE).format(DEFAULT_FMT); 
    }
    public static LocalDateTime parse(String str) { 
        return LocalDateTime.parse(str, DEFAULT_FMT); 
    }
    // 禁止實例化
    private DateUtils() {}
}

常見坑點提醒

錯誤代碼問題正確寫法
new Date()已棄用構造器,無時區Instant.now() / ZonedDateTime.now()
Calendar.getInstance()可變、線程不安全、月份0基LocalDate.now() / ZonedDateTime.now()
SimpleDateFormat線程不安全DateTimeFormatter (不可變、線程安全)
LocalDateTime 存數據庫無時區信息,跨時區錯誤存 Instant/UTC,或存 OffsetDateTime
Duration.between(LocalDate...)類型不匹配Period.between 計算年月日,Duration 計算時分秒

總結

Java 8 `java.time` 包徹底解決了舊 API 的所有痛點:不可變、線程安全、領域模型清晰、時區支持完善、函數式風格。新項目全面禁用 `Date`、`Calendar`、`SimpleDateFormat`,舊項目逐步遷移。掌握 Instant (機器時間) ↔ ZonedDateTime (人類時間) ↔ LocalDateTime (本地時間) 三者轉換,是處理好時間業務的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved