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

Java 時間處理工具類:Long 時間戳與字符串互轉最佳實踐

     編輯:Java 相關知識

核心需求

系統間交互常用 long 毫秒時間戳(13 位),日誌/界面/數據庫需可讀字符串。Java 8+ java.time 包為首選,舊版 SimpleDateFormat 線程不安全、性能差。

現代化實現(Java 8+ 推薦)

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public final class TimeUtils {
    private TimeUtils() {} // 工具類禁止實例化
    
    // 線程安全、不可變、高性能
    private static final DateTimeFormatter DEFAULT_FMT = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
    private static final ZoneId SYSTEM_ZONE = ZoneId.systemDefault();
    
    /** 毫秒時間戳 → 格式化字符串 */
    public static String toString(long epochMilli, String pattern) {
        DateTimeFormatter fmt = pattern != null ? DateTimeFormatter.ofPattern(pattern) : DEFAULT_FMT;
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), SYSTEM_ZONE).format(fmt);
    }
    
    /** 毫秒時間戳 → 默認格式字符串 */
    public static String toString(long epochMilli) {
        return toString(epochMilli, null);
    }
    
    /** 格式化字符串 → 毫秒時間戳 */
    public static long toEpochMilli(String timeStr, String pattern) {
        DateTimeFormatter fmt = pattern != null ? DateTimeFormatter.ofPattern(pattern) : DEFAULT_FMT;
        LocalDateTime ldt = LocalDateTime.parse(timeStr, fmt);
        return ldt.atZone(SYSTEM_ZONE).toInstant().toEpochMilli();
    }
    
    /** 當前時間毫秒戳 */
    public static long now() {
        return System.currentTimeMillis();
    }
    
    /** 當前時間格式化字符串 */
    public static String nowString() {
        return toString(now());
    }
    
    /** 當前時間格式化字符串(自定義模式) */
    public static String nowString(String pattern) {
        return toString(now(), pattern);
    }
    
    /** 相對時間計算 */
    public static long plus(long epochMilli, long amount, ChronoUnit unit) {
        return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), SYSTEM_ZONE)
                .plus(amount, unit).atZone(SYSTEM_ZONE).toInstant().toEpochMilli();
    }
    
    /** 解析 ISO-8601 標準格式 (2023-12-01T10:30:00Z) */
    public static long parseIso(String isoStr) {
        return Instant.parse(isoStr).toEpochMilli();
    }
}

舊版 SimpleDateFormat 線程安全包裝(維舊項目)

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ThreadLocalRandom;

public final class LegacyTimeUtils {
    private LegacyTimeUtils() {}
    
    private static final ThreadLocal FMT_TL = ThreadLocal.withInitial(() -> 
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
    
    public static String toString(long epochMilli) {
        return FMT_TL.get().format(new Date(epochMilli));
    }
    
    public static long toEpochMilli(String timeStr) throws ParseException {
        return FMT_TL.get().parse(timeStr).getTime();
    }
    
    // 若需自定義模式,建議每次 new SimpleDateFormat(pattern) 或用 FastDateFormat (Apache Commons Lang)
}

性能對比(百萬次轉換)

實現方式耗時 (ms)線程安全備註
java.time (DateTimeFormatter)~120✅ 不可變對象首選
ThreadLocal + SimpleDateFormat~180✅ 線程隔離維舊次選
共享 SimpleDateFormat + synchronized~450✅ 鎖開銷大不推薦
每次 new SimpleDateFormat~600✅ 對象創建開銷極差

常用格式模式速查

yyyy-MM-dd HH:mm:ss.SSS  // 2024-01-15 14:30:45.123 (默認)
yyyy-MM-dd HH:mm:ss      // 2024-01-15 14:30:45
yyyyMMddHHmmssSSS        // 20240115143045123 (無分隔符,文件名友好)
yyyy-MM-dd'T'HH:mm:ssXXX // 2024-01-15T14:30:45+08:00 (ISO-8601 帶時區)
yyyy-MM-dd               // 2024-01-15 (僅日期)
HH:mm:ss.SSS             // 14:30:45.123 (僅時間)

時區處理最佳實踐

  1. 存儲/傳輸統一 UTC:時間戳本質無時區,Instant 即 UTC
  2. 展示轉本地時區:.atZone(ZoneId.of("Asia/Shanghai"))
  3. 避免系統默認時區依賴:顯式指定 ZoneId,容器化部署時區可能不一致
  4. 数据库存储:MySQL DATETIME 無時區,TIMESTAMP 自動轉 UTC 存儲

實戰場景示例

// 1. 接口參數解析
@GetMapping("/logs")
public List query(@RequestParam("start") String startStr,
                       @RequestParam("end") String endStr) {
    long start = TimeUtils.toEpochMilli(startStr, "yyyy-MM-dd HH:mm:ss");
    long end = TimeUtils.toEpochMilli(endStr, "yyyy-MM-dd HH:mm:ss");
    return logService.queryByTimeRange(start, end);
}

// 2. 日誌鏈路追蹤 ID 生成(時間戳 + 隨機)
String traceId = TimeUtils.nowString("yyyyMMddHHmmssSSS") + ThreadLocalRandom.current().nextInt(10000);

// 3. 定時任務 Cron 表達式轉換
// Cron: 0 0 2 * * ? → 每天 02:00 執行
// 下次執行時間計算可用 CronExpression (Spring) 或 Cron4j

// 4. 相對時間查詢:最近 30 分鐘
long now = TimeUtils.now();
long halfHourAgo = TimeUtils.plus(now, -30, ChronoUnit.MINUTES);

避坑指南

坑點後果解決
SimpleDateFormat 共享實例線程不安全,日期錯亂、拋異常ThreadLocal 或 java.time
忽略時區導致跨時區錯誤服務器遷移/容器化後時間偏移顯式指定 ZoneId,存儲用 UTC
解析異常未處理脏數據導致服務不可用try-catch 返回 Optional/默認值
用秒級時間戳 (10 位) 混用毫秒級時間偏移 1000 倍統一 13 位毫秒,接口文檔明確標註
格式化模式大小寫錯誤 (mm vs MM)分鐘/月份混淆mm=分鐘, MM=月份, ss=秒, SS=毫秒

總結

Java 8+ 首選 java.time 包:Instant、LocalDateTime、DateTimeFormatter 線程安全、不可變、高性能、API 清晰。舊項目用 ThreadLocal 隔離。核心原則:**存儲傳輸用 UTC 毫秒長整型、展示轉本地時區格式化字符串、顯式指定時區與模式、統一工具類封裝**。

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