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

PHP 日期時間計算常見場景速查:相對時間、差值、本周判斷與 DateTime 最佳實踐

     編輯:PHP 相關知識

傳統時間戳運算 (strtotime + date)

1. 獲取 N 天/月/年前後的日期

<?php
$now = time();

// 30 天后
echo date('Y-m-d', strtotime('+30 days', $now));
// 3 月前
echo date('Y-m-d', strtotime('-3 months', $now));
// 1 年後
echo date('Y-m-d', strtotime('+1 year', $now));
// 具體組合
echo date('Y-m-d H:i:s', strtotime('+1 week 2 days 3 hours', $now));
// 本月第一天 00:00:00
echo date('Y-m-01 00:00:00');
// 本月最後一天 23:59:59
echo date('Y-m-t 23:59:59');
?>

2. 計算兩日期差值 (天/時/分/秒)

<?php
function diffHuman(string $start, string $end): array {
    $s = strtotime($start);
    $e = strtotime($end);
    $diff = abs($e - $s); // 秒數差

    return [
        'days'    => floor($diff / 86400),
        'hours'   => floor(($diff % 86400) / 3600),
        'minutes' => floor(($diff % 3600) / 60),
        'seconds' => $diff % 60,
        'total_seconds' => $diff,
    ];
}

print_r(diffHuman('2024-01-01', '2024-05-13 15:30:00'));
// Array ( [days]=>133 [hours]=>15 [minutes]=>30 [seconds]=>0 [total_seconds]=>11486100 )
?>

3. 判斷某日期是否為「本周」

<?php
function isThisWeek(string $date): bool {
    // date('W') 返回 ISO-8601 年份周號 (需同年份)
    return date('oW', strtotime($date)) === date('oW');
}

// 測試
var_dump(isThisWeek('2024-05-10')); // 視當前日期而定
?>

4. 獲取「三個月前」的日期 (注意月份天數不固定)

<?php
// ❌ 錯誤:固定 90 天
$wrong = date('Y-m-d', strtotime('-90 days'));

// ✅ 正確:月份運算自動處理月末邊界
$correct = date('Y-m-d', strtotime('-3 months'));
// 2024-05-13 -> 2024-02-13 (自動處理 2 月 29 天)
?>

現代最佳實踐:DateTime / DateTimeImmutable (PHP 5.2+ / 5.5+)

面向對象、不可變、時區感知、鏈式調用、支援 > 2038 年。

核心操作速查

<?php
// 創建
$dt = new DateTime('now', new DateTimeZone('Asia/Taipei'));
$dt = DateTime::createFromFormat('Y-m-d H:i:s', '2024-05-13 15:30:00', new DateTimeZone('UTC'));

// 格式化輸出
echo $dt->format('Y-m-d H:i:s'); // 2024-05-13 15:30:00
echo $dt->format('c');           // ISO 8601: 2024-05-13T15:30:00+08:00

// 修改 (可變對象)
$dt->modify('+1 month');
$dt->modify('last day of this month');
$dt->setTime(23, 59, 59);

// 不可變對象 (推薦,避免副作用)
$immutable = new DateTimeImmutable('now');
$nextWeek = $immutable->modify('+1 week'); // 返回新對象,$immutable 不變

// 差值計算
$start = new DateTime('2024-01-01');
$end   = new DateTime('2024-05-13');
$interval = $start->diff($end);
echo $interval->format('%a 天 %h 小時 %i 分鐘'); // 133 天 0 小時 0 分鐘
echo $interval->days; // 總天數 133
echo $interval->invert; // 1 表示 start > end

// 時區轉換
$utc = new DateTime('2024-05-13 07:30:00', new DateTimeZone('UTC'));
$utc->setTimezone(new DateTimeZone('Asia/Taipei'));
echo $utc->format('Y-m-d H:i:s'); // 2024-05-13 15:30:00

// 比較
$dt1 = new DateTime('2024-05-13');
$dt2 = new DateTime('2024-05-14');
var_dump($dt1 < $dt2); // bool(true) - 直接支援比較運算符
?>

常見業務場景封裝函數庫

<?php
class DateHelper {
    // 獲取指定日期所在週的開始(週一)與結束(週日)
    public static function weekRange(string $date = 'now'): array {
        $dt = new DateTime($date);
        $dt->modify('monday this week');
        $start = clone $dt;
        $end = clone $dt;
        $end->modify('sunday this week')->setTime(23, 59, 59);
        return ['start' => $start, 'end' => $end];
    }

    // 獲取指定月份範圍
    public static function monthRange(string $date = 'now'): array {
        $dt = new DateTime($date);
        $start = clone $dt;
        $start->modify('first day of this month')->setTime(0, 0, 0);
        $end = clone $dt;
        $end->modify('last day of this month')->setTime(23, 59, 59);
        return ['start' => $start, 'end' => $end];
    }

    // 年齡計算
    public static function age(string $birthdate): int {
        $birth = new DateTime($birthdate);
        $today = new DateTime('today');
        return $today->diff($birth)->y;
    }

    // 友好時間顯示 (幾秒前、幾分鐘前、昨天、具體日期)
    public static function humanDiff(string $datetime): string {
        $dt = new DateTime($datetime);
        $now = new DateTime('now');
        $diff = $now->getTimestamp() - $dt->getTimestamp();

        if ($diff < 60) return "{$diff}秒前";
        if ($diff < 3600) return floor($diff/60) . "分鐘前";
        if ($diff < 86400) return floor($diff/3600) . "小時前";
        if ($diff < 172800) return "昨天 " . $dt->format('H:i');
        if ($diff < 604800) return floor($diff/86400) . "天前";
        return $dt->format('Y-m-d');
    }

    // 判斷是否為工作日 (簡易版,不含節假日)
    public static function isWeekday(string $date = 'now'): bool {
        $dt = new DateTime($date);
        $w = (int)$dt->format('N'); // 1-7 (週一到週日)
        return $w >= 1 && $w <= 5;
    }
}
?>

避坑指南

坑點錯誤示例正確做法
時區未設置直接用 date() 報 Warningphp.ini 設 date.timezone 或 date_default_timezone_set()
strtotime 歧義格式'05/06/2024' 美/歐制不同用 DateTime::createFromFormat() 指定格式
月份加減邊界1月31日 +1月 = 3月3日用 modify('first day of +1 month') 或 last day of
夏令時切換時間差計算出現 23/25 小時天用 DateTime diff() 自動處理,或統一用 UTC 存儲計算
可變對象副作用$dt->modify() 影響原變量用 DateTimeImmutable 或 clone
32位系統 2038 年time() 返回負數升級 64位 PHP 或全面使用 DateTime

總結建議

  • 新代碼全面使用 DateTimeImmutable:類型安全、不可變、鏈式、時區正確。
  • 數據庫存儲統一用 UTC:DATETIME 或 TIMESTAMP 均存 UTC,展示層按用戶時區轉換。
  • 業務邏輯封裝為 Helper/Service 類:避免控制器中散落 strtotime/date 調用。
  • 複雜排程/日曆業務:引入 nesbot/carbon (Laravel 內建) 或 brick/date-time,功能更豐富。

掌握 DateTime 體系,告別時間戳字符串拼接的混亂時代,編寫經得起時區、夏令時、閏年、2038 年考驗的時間代碼。

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