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

Java 日期時間處理大全:SimpleDateFormat、Calendar 實戰工具類

     編輯:Java 相關知識

日期時間處理是業務開發高頻剛需。Java 早期 java.util.Date、Calendar、SimpleDateFormat 雖被 Java 8 新 API 取代,但在大量存量專案中仍廣泛使用。本文整理完整工具類 VeDate,涵蓋格式化、解析、計算、邊界獲取等核心場景,並附帶 Java 8 遷移指引。

一、核心工具類:VeDate 完整實現

package com.util;

import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class VeDate {

    // ===== 格式化常量 =====
    public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_PATTERN = "yyyy-MM-dd";
    public static final String TIME_PATTERN = "HH:mm:ss";
    public static final String COMPACT_DATETIME = "yyyyMMdd HHmmss";

    // ===== 獲取當前時間 =====
    public static Date getNowDate() {
        return parseDate(formatDate(new Date(), DATETIME_PATTERN), DATETIME_PATTERN);
    }

    public static Date getNowDateShort() {
        return parseDate(formatDate(new Date(), DATE_PATTERN), DATE_PATTERN);
    }

    public static String getStringDate() {
        return formatDate(new Date(), DATETIME_PATTERN);
    }

    public static String getStringDateShort() {
        return formatDate(new Date(), DATE_PATTERN);
    }

    public static String getTimeShort() {
        return formatDate(new Date(), TIME_PATTERN);
    }

    // ===== 核心格式化/解析 =====
    public static String formatDate(Date date, String pattern) {
        if (date == null) return null;
        return new SimpleDateFormat(pattern).format(date);
    }

    public static Date parseDate(String strDate, String pattern) {
        if (strDate == null) return null;
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        ParsePosition pos = new ParsePosition(0);
        return sdf.parse(strDate, pos);
    }

    // ===== 長/短格式互轉 =====
    public static Date strToDateLong(String strDate) {
        return parseDate(strDate, DATETIME_PATTERN);
    }

    public static String dateToStrLong(Date date) {
        return formatDate(date, DATETIME_PATTERN);
    }

    public static String dateToStr(Date date) {
        return formatDate(date, DATE_PATTERN);
    }

    public static Date strToDate(String strDate) {
        return parseDate(strDate, DATE_PATTERN);
    }

    public static Date getNow() {
        return new Date();
    }

    // ===== 相對時間計算 =====
    public static Date getLastDate(long daysAgo) {
        Date date = new Date();
        long ms = date.getTime() - daysAgo * 24 * 60 * 60 * 1000L;
        return new Date(ms);
    }

    public static String getStringToday() {
        return formatDate(new Date(), COMPACT_DATETIME);
    }

    // ===== 本月最後一天 =====
    public static String getDefaultDay() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.DAY_OF_MONTH, 1);      // 本月 1 號
        cal.add(Calendar.MONTH, 1);             // 下月 1 號
        cal.add(Calendar.DAY_OF_MONTH, -1);     // 本月最後一天
        return formatDate(cal.getTime(), DATE_PATTERN);
    }

    // ===== 提取時/分 =====
    public static String getHour() {
        String dt = formatDate(new Date(), DATETIME_PATTERN);
        return dt.substring(11, 13); // HH
    }

    public static String getMinute() {
        String dt = formatDate(new Date(), DATETIME_PATTERN);
        return dt.substring(14, 16); // mm
    }
}

二、關鍵 API 用法詳解

SimpleDateFormat 線程不安全問題

⚠️ 警告:SimpleDateFormat 非線程安全!不可作為 static 共享實例。工具類中每次 new SimpleDateFormat(pattern) 雖有性能開銷,但保證安全。高併發場景建議:

  • 使用 ThreadLocal<SimpleDateFormat> 緩存
  • 或升級 Java 8 DateTimeFormatter(線程安全、不可變)

Calendar 日期運算最佳實踐

Calendar cal = Calendar.getInstance();
cal.setTime(new Date());           // 設置基準時間
cal.add(Calendar.MONTH, 1);        // 月份 +1(自動處理跨年、天數差異)
cal.add(Calendar.DAY_OF_MONTH, -1); // 日期 -1
Date result = cal.getTime();
  • 優先用 add() 而非 set() 做相對運算,自動進位/借位
  • Calendar.DAY_OF_MONTH 等同 Calendar.DATE
  • 月份從 0 開始:Calendar.JANUARY = 0

ParsePosition 進階用法

String input = "2024-01-15 14:30:45 Extra";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ParsePosition pos = new ParsePosition(0);
Date date = sdf.parse(input, pos);
// pos.getErrorIndex() == -1 表示解析成功
// pos.getIndex() 返回解析到的字符位置(可用於繼續解析後續內容)

三、常見場景代碼片段

場景代碼示例
字串轉 Date(多格式兼容)Date d = parseDateMulti(str, "yyyy-MM-dd", "yyyy/MM/dd", "yyyy.MM.dd");
計算兩日期天數差long days = (d1.getTime() - d2.getTime()) / (24*60*60*1000);
判斷是否同一天formatDate(d1, "yyyyMMdd").equals(formatDate(d2, "yyyyMMdd"))
獲取本週一/週日cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
時間戳轉日期new Date(timestamp) / Instant.ofEpochMilli(ts) (Java 8)
格式化輸出帶時區sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));

四、Java 8 新時代:java.time 遷移指引

舊 APIJava 8 替代優勢
java.util.Datejava.time.Instant / LocalDateTime不可變、線程安全、納秒精度
CalendarZonedDateTime / LocalDate流式 API、時區支持完善
SimpleDateFormatDateTimeFormatter線程安全、預定義格式豐富

Java 8 等價寫法對比

// 當前時間
LocalDateTime.now()                    // VeDate.getNowDate()
LocalDate.now()                        // VeDate.getNowDateShort()

// 格式化
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime.now().format(dtf)        // VeDate.getStringDate()

// 解析
LocalDateTime.parse("2024-01-15 14:30", dtf) // VeDate.strToDateLong()

// 日期運算
LocalDate.now().plusMonths(1).minusDays(1)   // VeDate.getDefaultDay()

// 區間計算
ChronoUnit.DAYS.between(start, end)          // 天數差

// 時區處理
ZonedDateTime.now(ZoneId.of("Asia/Shanghai"))

五、避坑指南

  1. SimpleDateFormat 共享實例導致數據錯亂 → 每次 new 或 ThreadLocal
  2. Date.getYear() 已棄用,返回年份-1900 → 用 Calendar.YEAR 或 Java 8 getYear()
  3. Calendar.MONTH 0-based 易導致月份差一 → 寫常量或註釋
  4. 跨時區部署時,new Date() 獲取的是 JVM 默認時區時間 → 顯式設置 TimeZone 或用 ZonedDateTime
  5. 時間戳單位:Java System.currentTimeMillis() 毫秒,JS Date.now() 毫秒,Linux date +%s 秒 → 注意轉換

總結:VeDate 等工具類解決了舊 API 時代的高頻需求,但新專案強烈建議直接使用 Java 8 java.time 包。存量專案維護時,注意線程安全、時區、棄用方法三大坑,漸進式遷移至新 API。

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