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

Java 判斷時間是否在指定時間區間內的多種實作方式

     編輯:Java 相關知識

需求場景

判斷當前時間是否落在每日 00:00:00 至 00:05:00 區間內,若在區間內則跳過特定業務邏輯 (如批次排程暫停、維護時段攔截)。

方案一:字串解析比較 (原始做法,不推薦)

將時間格式化為字串,擷取時/分/秒進行多層 if-else 比較。程式碼冗長、易出錯、效能差,僅供理解邏輯參考。

// 原始碼過於冗長,此處不完整複製,核心缺點:
// 1. 大量 substring + parseInt 操作
// 2. 巢狀 if-else 超過 10 層
// 3. 無法處理跨日區間 (如 23:00-02:00)
// 4. 字串比較不如時間物件直觀

方案二:java.time.LocalTime (Java 8+ 推薦標準做法)

使用 java.time 套件,語意清晰、執行效率高、支援跨日區間判斷。

import java.time.LocalTime;

public class TimeRangeChecker {
    
    /** 判斷當前時間是否在區間內 (支援跨午夜) */
    public static boolean isInTimeRange(LocalTime start, LocalTime end) {
        LocalTime now = LocalTime.now();
        
        if (start.isBefore(end)) {
            // 正常區間:開始 < 結束 (如 09:00-18:00)
            return !now.isBefore(start) && !now.isAfter(end);
        } else {
            // 跨午夜區間:開始 > 結束 (如 22:00-06:00)
            return !now.isBefore(start) || !now.isAfter(end);
        }
    }
    
    /** 專門判斷 00:00-00:05 區間 */
    public static boolean isMidnightMaintenanceWindow() {
        return isInTimeRange(LocalTime.MIDNIGHT, LocalTime.of(0, 5));
    }
    
    public static void main(String[] args) {
        System.out.println("目前是否在維護時段: " + isMidnightMaintenanceWindow());
        // 測試指定時間
        System.out.println("00:03 是否在區間: " + isInTimeRange(LocalTime.of(0,0), LocalTime.of(0,5), LocalTime.of(0,3)));
        System.out.println("23:59 是否在區間: " + isInTimeRange(LocalTime.of(22,0), LocalTime.of(6,0), LocalTime.of(23,59)));
    }
    
    /** 完整版:傳入指定時間判斷 */
    public static boolean isInTimeRange(LocalTime start, LocalTime end, LocalTime target) {
        if (start.isBefore(end)) {
            return !target.isBefore(start) && !target.isAfter(end);
        } else {
            return !target.isBefore(start) || !target.isAfter(end);
        }
    }
}

方案三:毫秒/納秒數值比較 (高效能、低層級)

將時間轉換為當日零點起算的毫秒數比較,適合極高頻呼叫場景。

public class TimeRangeMillisChecker {
    
    public static boolean isInRange(long startMillis, long endMillis) {
        long nowMillis = System.currentTimeMillis() % 86_400_000; // 當日毫秒數
        long start = startMillis % 86_400_000;
        long end = endMillis % 86_400_000;
        
        if (start < end) {
            return nowMillis >= start && nowMillis <= end;
        } else {
            return nowMillis >= start || nowMillis <= end;
        }
    }
    
    public static void main(String[] args) {
        // 00:00:00 = 0, 00:05:00 = 5 * 60 * 1000 = 300,000
        System.out.println(isInRange(0, 300_000));
    }
}

方案四:Spring Schedule / Cron 表達式 (排程場景專用)

若需求為「在特定時段不執行排程」,建議直接用 Cron 表達式控制執行時間,而非在程式碼中判斷。

@Scheduled(cron = "0 5 0 * * ?") // 每日 00:05 執行 (避開 00:00-00:05)
public void dailyBatchJob() {
    // 業務邏輯
}

// 或使用 SchedulerLock 防止重複執行
@Scheduled(cron = "0 * * * * ?") // 每分鐘檢查
public void conditionalJob() {
    if (isMidnightMaintenanceWindow()) return;
    // 執行邏輯
}

方案五:舊版 Calendar/Date 兼容寫法 (Java 7 或舊系統維護)

import java.util.Calendar;
import java.util.Date;

public class LegacyTimeChecker {
    
    public static boolean isInMidnightWindow() {
        Calendar now = Calendar.getInstance();
        int hour = now.get(Calendar.HOUR_OF_DAY);
        int minute = now.get(Calendar.MINUTE);
        int second = now.get(Calendar.SECOND);
        
        int totalSeconds = hour * 3600 + minute * 60 + second;
        // 00:00:00 = 0, 00:05:00 = 300
        return totalSeconds >= 0 && totalSeconds <= 300;
    }
    
    /** 通用區間判斷 (含跨日) */
    public static boolean isInRange(Calendar start, Calendar end, Calendar target) {
        long startSec = start.get(Calendar.HOUR_OF_DAY) * 3600 + start.get(Calendar.MINUTE) * 60;
        long endSec = end.get(Calendar.HOUR_OF_DAY) * 3600 + end.get(Calendar.MINUTE) * 60;
        long targetSec = target.get(Calendar.HOUR_OF_DAY) * 3600 + target.get(Calendar.MINUTE) * 60;
        
        if (startSec < endSec) {
            return targetSec >= startSec && targetSec <= endSec;
        } else {
            return targetSec >= startSec || targetSec <= endSec;
        }
    }
}

效能比較與選擇建議

方案優點缺點適用場景
java.time (方案二)語意清晰、不可變、執行緒安全、功能豐富Java 8+ 才支援現代專案首選、所有新代碼
毫秒比較 (方案三)極致效能、無物件建立開銷可讀性較低、需手動處理時區超高頻呼叫 (百萬次/秒)、嵌入式系統
Calendar/Date (方案五)相容舊系統可變性、執行緒不安全、API 設計差Java 7 維護專案、Android 舊版本
Cron 排程 (方案四)聲明式、無業務代碼侵入僅適用排程觸發場景定時任務控制

常見陷阱與注意事項

  1. 時區問題:LocalTime.now() 使用系統預設時區,分散式部署需統一時區或明確指定 ZoneId
  2. 邊界包含性:業務需確認「包含端點」還是「不包含端點」 (上述範例採用閉區間 [start, end])
  3. 跨日區間:如 23:00-02:00,邏輯為 now >= start || now <= end,而非 &&
  4. 夏令時間:使用 ZonedDateTime 處理含時區的精確判斷

完整工具類封裝 (生產就緒)

import java.time.*;

public final class TimeRangeUtils {
    private TimeRangeUtils() {}
    
    /** 判斷指定時間是否在每日區間內 */
    public static boolean isInDailyRange(LocalTime start, LocalTime end, ZonedDateTime target) {
        LocalTime targetTime = target.toLocalTime();
        return isInDailyRange(start, end, targetTime);
    }
    
    /** 判斷當前時間 (系統時區) 是否在區間內 */
    public static boolean isInDailyRange(LocalTime start, LocalTime end) {
        return isInDailyRange(start, end, LocalTime.now());
    }
    
    /** 核心判斷邏輯 */
    public static boolean isInDailyRange(LocalTime start, LocalTime end, LocalTime target) {
        if (start.equals(end)) return true; // 全天區間
        
        if (start.isBefore(end)) {
            return !target.isBefore(start) && !target.isAfter(end); // [start, end]
        } else {
            return !target.isBefore(start) || !target.isAfter(end); // 跨午夜
        }
    }
    
    /** 建構器模式:流暢 API */
    public static Builder range() { return new Builder(); }
    
    public static class Builder {
        private LocalTime start, end;
        private ZoneId zone = ZoneId.systemDefault();
        
        public Builder start(LocalTime t) { this.start = t; return this; }
        public Builder end(LocalTime t) { this.end = t; return this; }
        public Builder zone(ZoneId z) { this.zone = z; return this; }
        
        public boolean containsNow() { 
            return isInDailyRange(start, end, LocalTime.now(zone)); 
        }
        public boolean contains(ZonedDateTime t) { 
            return isInDailyRange(start, end, t); 
        }
    }
}

// 使用範例
// TimeRangeUtils.range().start(LocalTime.MIDNIGHT).end(LocalTime.of(0,5)).containsNow()
// TimeRangeUtils.range().start(LocalTime.of(22,0)).end(LocalTime.of(6,0)).zone(ZoneId.of("Asia/Taipei")).containsNow()

總結

現代 Java 專案應優先使用 java.time.LocalTime 進行時間區間判斷,程式碼簡潔、語意明確、正確處理跨午夜情況。避免使用字串解析或舊版 Calendar,除非受限於舊版本環境。排程控制場景優先考慮 Cron 表達式聲明式配置。

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