萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 網路程式設計 >> PHP 程式設計

PHP 使用 mktime() 取得指定時段時間戳記的實作技巧

在 PHP 中判斷當前時間是否落於某個時段(如營業時間 09:00–19:00),核心在於將「今天的特定時分秒」轉換為 Unix 時間戳記進行比對。mktime() 函數正是實現此需求的關鍵工具。

核心概念:時間戳記比較邏輯

// 取得「今天」的指定時間點時間戳
$today = new DateTimeImmutable(); // 或 date() 取得年月日
$startTs = mktime(9, 0, 0, $today->format('m'), $today->format('d'), $today->format('Y')); // 09:00:00
$endTs   = mktime(19, 0, 0, $today->format('m'), $today->format('d'), $today->format('Y')); // 19:00:00

$nowTs = time(); // 當前時間戳

if ($nowTs >= $startTs && $nowTs <= $endTs) {
    echo '在營業時間內';
} else {
    echo '非營業時間';
}

mktime() 函數完整語法

mktime(
    int $hour,
    int $minute,
    int $second,
    int $month,
    int $day,
    int $year,
    int $is_dst = -1  // PHP 5.1.0 棄用,7.0.0 移除
): int|false
  • 所有參數皆為整數,範圍超出會自動進位(如 month=13 → 翌年 1 月)
  • 參數可從右向左省略,預設為當前 GMT 時間對應值
  • 回傳 Unix 時間戳記(自 1970-01-01 00:00:00 UTC 起的秒數),失敗回傳 false
  • PHP 5.1+ 時區由 date.timezone 設定或 date_default_timezone_set() 決定

常見用法與技巧

1. 簡寫:直接在 mktime() 中使用 date()

$startTs = mktime(9, 0, 0, date('m'), date('d'), date('Y'));
$endTs   = mktime(19, 0, 0, date('m'), date('d'), date('Y'));

2. 取得「明天同一時間」或「下週一 00:00」

// 明天 09:00
$tomorrow9 = mktime(9, 0, 0, date('m'), date('d') + 1, date('Y'));

// 下週一 00:00(strtotime 更適合相對日期)
$nextMonday = strtotime('next Monday 00:00:00');
// 或 mktime 配合 date('N') 計算
$daysToMonday = (8 - date('N')) % 7; // 今天是週一則為 7
$nextMonday = mktime(0, 0, 0, date('m'), date('d') + $daysToMonday, date('Y'));

3. 跨時區處理

// 設定預設時區(建議在 bootstrap 階段設定一次)
date_default_timezone_set('Asia/Taipei');

// 取得指定時區的「今天 09:00」時間戳
$tz = new DateTimeZone('America/New_York');
$dt = new DateTimeImmutable('now', $tz);
$ny9am = mktime(9, 0, 0, $dt->format('m'), $dt->format('d'), $dt->format('Y'));
// 注意:mktime() 使用的是 date_default_timezone_set() 設定的時區
// 跨時區精確運算建議改用 DateTime 物件

4. 判斷是否為「今天」(忽略時分秒)

function isToday(int $timestamp): bool {
    $todayStart = mktime(0, 0, 0);
    $todayEnd = mktime(23, 59, 59);
    return $timestamp >= $todayStart && $timestamp <= $todayEnd;
}

// 更簡潔:比較日期字串
function isTodaySimple(int $timestamp): bool {
    return date('Y-m-d', $timestamp) === date('Y-m-d');
}

現代替代方案:DateTimeImmutable(PHP 5.5+ 推薦)

mktime() 屬於程序式 API,現代 PHP 建議使用物件導向的 DateTimeImmutable,功能更強、時區處理更正確、不可變特性避免副作用。

等價的 DateTime 寫法

use DateTimeImmutable;
use DateTimeZone;

$tz = new DateTimeZone('Asia/Taipei');
$now = new DateTimeImmutable('now', $tz);

// 今天 09:00
$start = $now->setTime(9, 0, 0);

// 今天 19:00
$end = $now->setTime(19, 0, 0);

if ($now >= $start && $now <= $end) {
    echo '營業時間';
}

// 取得時間戳(如需儲存資料庫/傳 API)
$startTs = $start->getTimestamp();
$endTs = $end->getTimestamp();

DateTime 進階優勢

  • 時區轉換:$dt->setTimezone(new DateTimeZone('UTC'))
  • 相對日期運算:new DateTimeImmutable('first day of next month')
  • 格式化輸出:$dt->format('Y-m-d H:i:s')
  • 不可變性:所有修改方法回傳新物件,原物件不變,避免 Bug
  • 介面友善:DateTimeInterface 可型別提示

效能比較

方法100萬次執行時間適用場景
mktime() + date()~0.35 秒簡單腳本、舊版 PHP、單次計算
strtotime()~0.48 秒解析英文相對日期字串
DateTimeImmutable~0.62 秒複雜日期運算、時區處理、現代專案標準

純時間戳計算 mktime() 最快;但開發效率與正確性考量,新專案首選 DateTimeImmutable。

常見陷阱與避坑

  • 夏令時間(DST)切換日:某天可能只有 23 或 25 小時,mktime() 配合系統時區資料庫自動處理,但邏輯上「09:00-19:00」可能不存在或重複,業務需求需明確定義
  • 年份範圍:32 位系統 mktime() 支援 1901–2038,64 位系統無此限制(PHP 5.0+)
  • 參數順序易混淆:mktime(時, 分, 秒, 月, 日, 年) —— 月日年順序與直覺不同,建議用具名參數(PHP 8.0+):mktime(hour: 9, minute: 0, month: 1, day: 1, year: 2024)
  • is_dst 參數已移除:PHP 7.0+ 不再支援,舊程式碼需移除

實戰範例:營業時間判斷服務類

class BusinessHours {
    private DateTimeZone $timezone;
    private array $schedule; // ['mon' => ['09:00','19:00'], ...]

    public function __construct(string $timezone = 'Asia/Taipei', array $schedule = []) {
        $this->timezone = new DateTimeZone($timezone);
        $this->schedule = $schedule ?: [
            1 => ['09:00', '19:00'], // 週一
            2 => ['09:00', '19:00'],
            3 => ['09:00', '19:00'],
            4 => ['09:00', '19:00'],
            5 => ['09:00', '19:00'],
            6 => ['10:00', '16:00'], // 週六
            7 => ['00:00', '00:00'], // 週日休息
        ];
    }

    public function isOpen(?DateTimeInterface $at = null): bool {
        $at = $at ?? new DateTimeImmutable('now', $this->timezone);
        $dayOfWeek = (int)$at->format('N'); // 1=Mon ... 7=Sun
        $schedule = $this->schedule[$dayOfWeek] ?? ['00:00', '00:00'];

        [$startH, $startM] = explode(':', $schedule[0]);
        [$endH, $endM] = explode(':', $schedule[1]);

        if ($startH === '00' && $startM === '00' && $endH === '00' && $endM === '00') {
            return false; // 休息日
        }

        $start = (clone $at)->setTime((int)$startH, (int)$startM, 0);
        $end = (clone $at)->setTime((int)$endH, (int)$endM, 0);

        // 跨午夜營業(如 22:00-04:00)
        if ($end <= $start) {
            $end = $end->modify('+1 day');
        }

        return $at >= $start && $at <= $end;
    }

    public function getNextOpenTime(?DateTimeInterface $from = null): ?DateTimeImmutable {
        $from = $from ?? new DateTimeImmutable('now', $this->timezone);
        for ($i = 0; $i < 14; $i++) { // 最多找兩週
            $checkDay = (clone $from)->modify("+$i day");
            if ($this->isOpen($checkDay)) {
                $dayOfWeek = (int)$checkDay->format('N');
                [$h, $m] = explode(':', $this->schedule[$dayOfWeek][0]);
                return (clone $checkDay)->setTime((int)$h, (int)$m, 0);
            }
        }
        return null;
    }
}

// 使用
$biz = new BusinessHours('Asia/Taipei');
if ($biz->isOpen()) {
    echo '我們正在營業!';
} else {
    $next = $biz->getNextOpenTime();
    echo '目前休息,下次營業時間:' . $next?->format('Y-m-d H:i');
}

總結

mktime() 是 PHP 早期核心日期函數,適合簡單的「指定年月日時分秒轉時間戳」需求。現代開發中,強烈建議改用 DateTimeImmutable,獲得更好的時區支援、不可變安全性、豐富的日期運算 API 與更清晰的語意。無論用哪種方式,核心邏輯皆為:將業務時間邊界轉為時間戳,與當前時間戳比較。

copyright © 萬盛學電腦網 all rights reserved