萬盛學電腦網

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

PHP 日期時間運算實戰:strtotime 與 mktime 計算本週、上月、本季度等範圍

兩大核心函式對比

特性strtotime()mktime()
輸入方式英文自然語言文字分離的數值參數
可讀性極高較低
靈活度支援相對時間、複雜描述精確控制每個時間元件
效能需解析字串,稍慢直接運算,較快
適用場景動態時間描述、相對日期固定格式、高效能需求、避免歧義

strtotime() 相對時間語法速查

<?php
// 基準時間:當下
echo date("Y-m-d", strtotime("now"));           // 今天
echo date("Y-m-d", strtotime("today"));         // 今天 00:00:00

// 相對日期
echo date("Y-m-d", strtotime("-1 day"));        // 昨天
echo date("Y-m-d", strtotime("+1 week"));       // 下週同天
echo date("Y-m-d", strtotime("first day of this month")); // 本月 1 號
echo date("Y-m-d", strtotime("last day of last month"));  // 上月最後一天

// 週邊界(重點!)
echo date("Y-m-d", strtotime("monday this week"));    // 本週一
echo date("Y-m-d", strtotime("sunday this week"));    // 本週日
echo date("Y-m-d", strtotime("monday last week"));    // 上週一
echo date("Y-m-d", strtotime("sunday last week"));    // 上週日

// 季度邊界
echo date("Y-m-d", strtotime("first day of this quarter"));
echo date("Y-m-d", strtotime("last day of last quarter"));
?>

mktime() 精確控制邊界

語法:mktime(時, 分, 秒, 月, 日, 年),參數從右至左可省略(預設為當前時間)。

本週範圍(週一至週日)

<?php
// 取得本週一 00:00:00
$weekStart = mktime(0, 0, 0, date("m"), date("d") - date("w") + 1, date("Y"));
// 取得本週日 23:59:59
$weekEnd = mktime(23, 59, 59, date("m"), date("d") - date("w") + 7, date("Y"));

echo "本週起:" . date("Y-m-d H:i:s", $weekStart) . "<br>";
echo "本週迄:" . date("Y-m-d H:i:s", $weekEnd) . "<br>";
?>

原理:date("w") 回傳 0(日)~6(六),date("d") - date("w") + 1 即為本週一日期。

上週範圍

<?php
$lastWeekStart = mktime(0, 0, 0, date("m"), date("d") - date("w") + 1 - 7, date("Y"));
$lastWeekEnd = mktime(23, 59, 59, date("m"), date("d") - date("w") + 7 - 7, date("Y"));
?>

本月範圍

<?php
$monthStart = mktime(0, 0, 0, date("m"), 1, date("Y"));
$monthEnd = mktime(23, 59, 59, date("m"), date("t"), date("Y")); // date("t") 為本月總天數
?>

上月範圍

<?php
$lastMonthStart = mktime(0, 0, 0, date("m") - 1, 1, date("Y"));
$lastMonthEnd = mktime(23, 59, 59, date("m"), 0, date("Y")); // 第 0 天 = 上月最後一天
?>

本季度範圍

<?php
// 計算本季度首月 (1,4,7,10)
$currentMonth = (int)date("n");
$quarterStartMonth = $currentMonth - ($currentMonth - 1) % 3;

// 季度第一天
$quarterStart = mktime(0, 0, 0, $quarterStartMonth, 1, date("Y"));

// 季度最後一天:取下季第一天減 1 秒,或計算季度末月天數
$quarterEndMonth = $quarterStartMonth + 2;
$quarterEndDay = date("t", mktime(0, 0, 0, $quarterEndMonth, 1, date("Y")));
$quarterEnd = mktime(23, 59, 59, $quarterEndMonth, $quarterEndDay, date("Y"));

echo "本季度起:" . date("Y-m-d", $quarterStart) . "<br>";
echo "本季度迄:" . date("Y-m-d", $quarterEnd) . "<br>";
?>

現代最佳實踐:DateTime 類別(PHP 5.2+)

強烈建議使用 OOP 風格的 DateTime/DateTimeImmutable,功能更強、時區處理正確、可鏈式呼叫。

DateTime 相對時間修改

<?php
$now = new DateTime();

// 本週一
$weekStart = clone $now;
$weekStart->modify('monday this week')->setTime(0, 0, 0);

// 本週日
$weekEnd = clone $now;
$weekEnd->modify('sunday this week')->setTime(23, 59, 59);

// 本月第一天
$monthStart = clone $now;
$monthStart->modify('first day of this month')->setTime(0, 0, 0);

// 本月最後一天
$monthEnd = clone $now;
$monthEnd->modify('last day of this month')->setTime(23, 59, 59);

// 本季度
$quarterStart = clone $now;
$quarterStart->modify('first day of this quarter')->setTime(0, 0, 0);

$quarterEnd = clone $now;
$quarterEnd->modify('last day of this quarter')->setTime(23, 59, 59);

echo "本週:{$weekStart->format('Y-m-d')} ~ {$weekEnd->format('Y-m-d')}<br>";
echo "本月:{$monthStart->format('Y-m-d')} ~ {$monthEnd->format('Y-m-d')}<br>";
echo "本季:{$quarterStart->format('Y-m-d')} ~ {$quarterEnd->format('Y-m-d')}<br>";
?>

DateTimeImmutable(不可變,更安全)

<?php
$now = new DateTimeImmutable();
$weekStart = $now->modify('monday this week')->setTime(0, 0, 0); // 回傳新物件
$weekEnd = $now->modify('sunday this week')->setTime(23, 59, 59);
// $now 本身不變
?>

實用工具函式庫

<?php
/**
 * 取得常用時間範圍的起迄時間戳
 * @return array ['range_name' => ['start' => timestamp, 'end' => timestamp]]
 */
function getCommonDateRanges(): array {
    $now = new DateTimeImmutable();
    $ranges = [];
    
    // 今天
    $ranges['today'] = [
        'start' => $now->setTime(0, 0, 0)->getTimestamp(),
        'end'   => $now->setTime(23, 59, 59)->getTimestamp(),
    ];
    
    // 昨天
    $yesterday = $now->modify('-1 day');
    $ranges['yesterday'] = [
        'start' => $yesterday->setTime(0, 0, 0)->getTimestamp(),
        'end'   => $yesterday->setTime(23, 59, 59)->getTimestamp(),
    ];
    
    // 本週 (週一~週日)
    $ranges['this_week'] = [
        'start' => $now->modify('monday this week')->setTime(0, 0, 0)->getTimestamp(),
        'end'   => $now->modify('sunday this week')->setTime(23, 59, 59)->getTimestamp(),
    ];
    
    // 上週
    $ranges['last_week'] = [
        'start' => $now->modify('monday last week')->setTime(0, 0, 0)->getTimestamp(),
        'end'   => $now->modify('sunday last week')->setTime(23, 59, 59)->getTimestamp(),
    ];
    
    // 本月
    $ranges['this_month'] = [
        'start' => $now->modify('first day of this month')->setTime(0, 0, 0)->getTimestamp(),
        'end'   => $now->modify('last day of this month')->setTime(23, 59, 59)->getTimestamp(),
    ];
    
    // 上月
    $ranges['last_month'] = [
        'start' => $now->modify('first day of last month')->setTime(0, 0, 0)->getTimestamp(),
        'end'   => $now->modify('last day of last month')->setTime(23, 59, 59)->getTimestamp(),
    ];
    
    // 本季度
    $ranges['this_quarter'] = [
        'start' => $now->modify('first day of this quarter')->setTime(0, 0, 0)->getTimestamp(),
        'end'   => $now->modify('last day of this quarter')->setTime(23, 59, 59)->getTimestamp(),
    ];
    
    // 本年
    $ranges['this_year'] = [
        'start' => $now->modify('first day of January')->setTime(0, 0, 0)->getTimestamp(),
        'end'   => $now->modify('last day of December')->setTime(23, 59, 59)->getTimestamp(),
    ];
    
    return $ranges;
}

// 使用範例
$ranges = getCommonDateRanges();
foreach ($ranges as $name => $range) {
    echo "$name: " . date('Y-m-d H:i:s', $range['start']) . " ~ " . date('Y-m-d H:i:s', $range['end']) . "<br>";
}
?>

時區陷阱與解決

<?php
// ❌ 錯誤:依賴伺服器預設時區
date_default_timezone_set('Asia/Taipei'); // 必須明確設定

// ✅ DateTime 明確指定時區
$taipei = new DateTime('now', new DateTimeZone('Asia/Taipei'));
$utc = new DateTime('now', new DateTimeZone('UTC'));

// php.ini 設定預設時區(推薦)
// date.timezone = "Asia/Taipei"
?>

總結建議

  1. 新專案一律使用 DateTime/DateTimeImmutable
  2. strtotime() 適合簡單相對時間、解析使用者輸入
  3. mktime() 適合固定格式、高效能批次運算、避免歧義
  4. 永遠明確設定時區(php.ini 或 DateTimeZone)
  5. 邊界計算用「首日 00:00:00」到「末日 23:59:59」涵蓋完整範圍
  6. 資料庫查詢建議用 WHERE created_at >= :start AND created_at <= :end
copyright © 萬盛學電腦網 all rights reserved