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

Java 獲取時間方法總結:DateFormat、Timestamp、LocalDateTime 三種方案對比

     編輯:Java 程式設計入門

三種主流方案對比

方案一:DateFormat(舊 API,僅維護用)

// 獲取日期時間實例(風格常數)
DateFormat df1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.CHINESE);
String s1 = df1.format(new Date());  // 2024/1/15 上午10:30

DateFormat df2 = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.CHINESE);
String s2 = df2.format(new Date());  // 2024年1月15日 上午10時30分45秒

// 自定義格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s3 = sdf.format(new Date());  // 2024-01-15 10:30:45

// ⚠️ 線程不安全!多線程環境必須 ThreadLocal 或每次 new
ThreadLocal tl = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

方案二:Timestamp(JDBC/數據庫層)

// 當前時間戳(納秒精度)
Timestamp ts = new Timestamp(System.currentTimeMillis());
// 或
Timestamp ts2 = Timestamp.from(Instant.now());

// 固定格式輸出:yyyy-MM-dd HH:mm:ss.fffffffff
String tsStr = ts.toString();  // 2024-01-15 10:30:45.123456789

// 與數據庫交互
PreparedStatement ps = conn.prepareStatement("INSERT INTO t(time_col) VALUES (?)");
ps.setTimestamp(1, ts);  // 自動處理時區轉換

// 從結果集獲取
ResultSet rs = ps.executeQuery();
Timestamp dbTs = rs.getTimestamp("time_col");  // 使用連接時區
Timestamp dbTsUtc = rs.getTimestamp("time_col", Calendar.getInstance(TimeZone.getTimeZone("UTC")));

方案三:LocalDateTime(現代首選)

// 當前本地時間
LocalDateTime now = LocalDateTime.now();  // 系統默認時區

// 指定時區
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = now.format(fmt);  // 2024-01-15 10:30:45

// 解析
LocalDateTime parsed = LocalDateTime.parse("2024-01-15 10:30:45", fmt);

// 計算
LocalDateTime yesterday = now.minusDays(1);
LocalDateTime nextMonth = now.plusMonths(1);
Duration dur = Duration.between(yesterday, now);  // PT24H

// 轉 Timestamp(JDBC 4.2+)
Timestamp ts = Timestamp.valueOf(now);  // 無時區轉換,視為本地時間
Timestamp ts2 = Timestamp.from(zdt.toInstant());  // 正確時區轉換

完整工具類封裝

public final class TimeUtils {
    private TimeUtils() {}
    
    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 final DateTimeFormatter ISO_MILLIS = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
    
    // === 獲取當前 ===
    public static String now() { return LocalDateTime.now().format(STANDARD); }
    public static String now(DateTimeFormatter fmt) { return LocalDateTime.now().format(fmt); }
    public static LocalDateTime nowLocal() { return LocalDateTime.now(); }
    public static Instant nowInstant() { return Instant.now(); }
    public static ZonedDateTime nowZone(String zoneId) { return ZonedDateTime.now(ZoneId.of(zoneId)); }
    
    // === 格式化 ===
    public static String format(LocalDateTime ldt) { return ldt.format(STANDARD); }
    public static String format(LocalDateTime ldt, DateTimeFormatter fmt) { return ldt.format(fmt); }
    public static String format(Instant instant, String zoneId) { 
        return instant.atZone(ZoneId.of(zoneId)).format(STANDARD); 
    }
    public static String format(Timestamp ts) { return ts.toLocalDateTime().format(STANDARD); }
    
    // === 解析 ===
    public static LocalDateTime parse(String str) { return LocalDateTime.parse(str, STANDARD); }
    public static LocalDateTime parse(String str, DateTimeFormatter fmt) { return LocalDateTime.parse(str, fmt); }
    
    // === 相對時間 ===
    public static LocalDateTime yesterday() { return LocalDateTime.now().minusDays(1); }
    public static LocalDateTime tomorrow() { return LocalDateTime.now().plusDays(1); }
    public static LocalDateTime daysAgo(int days) { return LocalDateTime.now().minusDays(days); }
    public static LocalDateTime daysLater(int days) { return LocalDateTime.now().plusDays(days); }
    
    // === 互轉 ===
    public static Timestamp toTimestamp(LocalDateTime ldt) { return Timestamp.valueOf(ldt); }
    public static LocalDateTime toLocalDateTime(Timestamp ts) { return ts.toLocalDateTime(); }
    public static Date toDate(LocalDateTime ldt) { return Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); }
    public static LocalDateTime toLocalDateTime(Date date) { return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); }
    
    // === 友好顯示 ===
    public static String friendly(LocalDateTime ldt) {
        Duration d = Duration.between(ldt, LocalDateTime.now());
        long sec = d.getSeconds();
        if (sec < 60) return "剛剛";
        if (sec < 3600) return sec/60 + "分鐘前";
        if (sec < 86400) return sec/3600 + "小時前";
        if (sec < 172800) return "昨天 " + ldt.format(TIME_ONLY);
        return ldt.format(DATE_ONLY);
    }
}

避坑指南

  1. SimpleDateFormat 非線程安全:併發環境用 ThreadLocal 或改用 DateTimeFormatter
  2. Timestamp.toString() 格式固定:不可自定義,需配合 LocalDateTime 格式化
  3. 時區陷阱:Date/Calendar/Timestamp 依賴 JVM 時區,LocalDateTime 無時區,ZonedDateTime 明確時區
  4. 數據庫存儲建議:存 UTC (TIMESTAMP WITH TIME ZONE 或 BIGINT 毫秒數),應用層轉換
  5. 序列化格式統一:API 輸出 ISO-8601 (yyyy-MM-dd'T'HH:mm:ss.SSSZ)

總結

新代碼無腦用 LocalDateTime/Instant/ZonedDateTime + DateTimeFormatter。DateFormat 僅用於維護舊系統,Timestamp 僅用於 JDBC 交互邊界。封裝 TimeUtils 統一格式常數,代碼審查禁止出現 new SimpleDateFormat、new Date()、Calendar.getInstance()。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方案API特點推薦指數
DateFormatjava.text.DateFormat舊 API、Locale 敏感、格式化/解析、非線程安全⭐⭐ (維護舊代碼)
Timestampjava.sql.TimestampJDBC 交互、納秒精度、繼承 Date、toString 固定格式⭐⭐⭐ (數據庫層)
LocalDateTimejava.time.LocalDateTime現代 API、不可變、線程安全、豐富計算、ISO 格式⭐⭐⭐⭐⭐ (新代碼首選)