判斷當前時間是否落在每日 00:00:00 至 00:05:00 區間內,若在區間內則跳過特定業務邏輯 (如批次排程暫停、維護時段攔截)。
將時間格式化為字串,擷取時/分/秒進行多層 if-else 比較。程式碼冗長、易出錯、效能差,僅供理解邏輯參考。
// 原始碼過於冗長,此處不完整複製,核心缺點:
// 1. 大量 substring + parseInt 操作
// 2. 巢狀 if-else 超過 10 層
// 3. 無法處理跨日區間 (如 23:00-02:00)
// 4. 字串比較不如時間物件直觀
使用 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));
}
}
若需求為「在特定時段不執行排程」,建議直接用 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;
// 執行邏輯
}
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 排程 (方案四) | 聲明式、無業務代碼侵入 | 僅適用排程觸發場景 | 定時任務控制 |
LocalTime.now() 使用系統預設時區,分散式部署需統一時區或明確指定 ZoneId[start, end])now >= start || now <= end,而非 &&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 表達式聲明式配置。