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

Java 獲取日期時間的正確姿勢:拒絕 VBScript 混雜代碼

     編輯:Java 相關知識

注意:原文內容異常,混雜了 VBScript 文件搜索代碼,與標題「Java 獲取日期的方法」完全無關。本文重新撰寫,提供 Java 日期時間處理的現代、標準、完整指南,涵蓋 Java 8+ `java.time` 包(首選)、舊 `Date/Calendar` 互轉、格式化、計算、數據庫映射等核心場景。

一、現代首選:java.time 包(Java 8+)

Java 8 引入 JSR-310,提供不可變、線程安全、API 清晰的日期時間類,完全替代 `Date`、`Calendar`、`SimpleDateFormat`。

核心類速查

類用途示例
LocalDate僅日期(年月日)2024-03-15
LocalTime僅時間(時分秒納秒)14:30:00.123
LocalDateTime日期時間(無時區)2024-03-15T14:30:00
ZonedDateTime帶時區日期時間2024-03-15T14:30:00+08:00[Asia/Shanghai]
Instant時間戳(UTC,納秒精度)2024-03-15T06:30:00Z
Duration時間間隔(秒/納秒)PT2H30M
Period日期間隔(年月日)P1Y2M3D
DateTimeFormatter格式化/解析(線程安全)yyyy-MM-dd HH:mm:ss

常用操作速查

// 當前時間
LocalDate today = LocalDate.now();
LocalDateTime now = LocalDateTime.now();
Instant nowInstant = Instant.now();

// 指定構造
LocalDate d = LocalDate.of(2024, 3, 15);
LocalDateTime dt = LocalDateTime.of(2024, 3, 15, 14, 30, 0);
ZonedDateTime zdt = ZonedDateTime.of(dt, ZoneId.of("Asia/Shanghai"));

// 格式化輸出
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = now.format(fmt);  // 2024-03-15 14:30:00

// 解析字符串
LocalDate parsedDate = LocalDate.parse("2024-03-15", DateTimeFormatter.ISO_LOCAL_DATE);
LocalDateTime parsedDt = LocalDateTime.parse("2024-03-15 14:30:00", fmt);

// 日期計算
LocalDate tomorrow = today.plusDays(1);
LocalDate nextMonth = today.plusMonths(1);
Period period = Period.between(LocalDate.of(2024,1,1), today); // 間隔年月日
long daysBetween = ChronoUnit.DAYS.between(LocalDate.of(2024,1,1), today); // 總天數

// 時間計算
Duration duration = Duration.between(LocalDateTime.now(), LocalDateTime.now().plusHours(2));
long seconds = duration.getSeconds();

// 時區轉換
ZonedDateTime shanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime newYork = shanghai.withZoneSameInstant(ZoneId.of("America/New_York"));

// 舊類互轉
Date oldDate = Date.from(instant);
Instant instant = oldDate.toInstant();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());

二、舊 API 兼容(Date/Calendar/SimpleDateFormat)

維護舊系統時仍需掌握,新代碼禁用。

Date 基礎

Date date = new Date();                    // 當前毫秒時間戳
long timestamp = date.getTime();           // 毫秒數
Date fromTs = new Date(timestamp);         // 毫秒構造

// 格式化(線程不安全,需局部變量或 ThreadLocal)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String str = sdf.format(date);
Date parsed = sdf.parse("2024-03-15 14:30:00");

Calendar 運算

Calendar cal = Calendar.getInstance();     // 默認時區/語言環境
cal.setTime(date);                         // Date -> Calendar
cal.add(Calendar.DAY_OF_MONTH, 7);         // 加 7 天
cal.set(Calendar.HOUR_OF_DAY, 0);          // 置零時分秒毫秒
cal.clear(Calendar.MILLISECOND);
Date newDate = cal.getTime();              // Calendar -> Date

兩日期天數差(舊 API 通用算法)

public static long daysBetween(Date start, Date end) {
    if (start == null || end == null) return 0;
    // 置零時分秒毫秒,確保按自然日計算
    Calendar cal = Calendar.getInstance();
    cal.setTime(start);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.clear(Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND);
    long startDay = cal.getTimeInMillis();

    cal.setTime(end);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.clear(Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND);
    long endDay = cal.getTimeInMillis();

    return (endDay - startDay) / (1000 * 60 * 60 * 24);
}

三、數據庫日期類型映射

數據庫類型Java 類型(推薦)舊類型說明
DATELocalDatejava.sql.Date僅年月日
TIMELocalTimejava.sql.Time僅時分秒
DATETIME / TIMESTAMPLocalDateTime / ZonedDateTimejava.sql.Timestamp含時區建議 ZonedDateTime

MyBatis/JPA 自動映射

  • MyBatis 3.5+ / MyBatis-Plus:自動支援 `java.time.*` 類型,無需 TypeHandler。
  • JPA (Hibernate 5.2+):`@Column(columnDefinition = "TIMESTAMP")` + `LocalDateTime` 自動映射。
  • 舊項目手動轉換工具類:
public class DateUtils {
    // java.sql.Date <- LocalDate
    public static java.sql.Date toSqlDate(LocalDate ld) {
        return ld == null ? null : java.sql.Date.valueOf(ld);
    }
    public static LocalDate toLocalDate(java.sql.Date sd) {
        return sd == null ? null : sd.toLocalDate();
    }
    // java.sql.Timestamp <- LocalDateTime
    public static java.sql.Timestamp toSqlTimestamp(LocalDateTime ldt) {
        return ldt == null ? null : java.sql.Timestamp.valueOf(ldt);
    }
    public static LocalDateTime toLocalDateTime(java.sql.Timestamp ts) {
        return ts == null ? null : ts.toLocalDateTime();
    }
    // 字符串解析(多格式兼容)
    public static LocalDate parseDate(String str, String... patterns) {
        for (String p : patterns) {
            try { return LocalDate.parse(str, DateTimeFormatter.ofPattern(p)); }
            catch (DateTimeParseException ignored) {}
        }
        throw new IllegalArgumentException("Unparsable date: " + str);
    }
}

四、最佳實踐總結

  1. 新代碼 100% 使用 java.time:不可變、線程安全、API 正交、時區處理正確。
  2. 格式化器靜態常量復用:`DateTimeFormatter` 線程安全,定義為 `private static final`。
  3. 業務邏輯用 LocalDate/LocalDateTime:存儲/傳輸用 Instant/UTC 字符串,展示轉本地時區。
  4. 計算間隔用 ChronoUnit / Period / Duration:語義清晰,避免手算毫秒除法 Bug。
  5. 序列化配置:Jackson `JavaTimeModule`、Gson `LocalDateTimeAdapter` 確保 JSON 格式統一(ISO-8601)。
  6. 舊系統隔離轉換:在 Repository/Mapper 層統一轉換,Service 層純用 java.time。

五、常見面試題

  • SimpleDateFormat 為什麼線程不安全?:內部 `Calendar` 實例共享,`parse/format` 修改其狀態。解決:局部變量、`ThreadLocal`、或改用 `DateTimeFormatter`。
  • Date 為什麼被詬病?:可變、月份 0-based、年份 1900-based、無時區概念、方法命名混亂、線程不安全。
  • 如何正確計算「兩日期相差天數」?:置零時分秒毫秒後算毫秒差,或用 `ChronoUnit.DAYS.between(localDate1, localDate2)`。
  • Instant vs LocalDateTime?:Instant = UTC 時間戳(絕對時刻);LocalDateTime = 無時區本地日期時間(如「2024-03-15 14:30」在上海/紐約代表不同時刻)。

掌握 `java.time` 體系,是現代 Java 開發者的必修課。拒絕 `Date`、`Calendar`、`SimpleDateFormat`,從今天開始寫正確、優雅、無 Bug 的日期時間代碼。

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