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

PHP 日期時間處理實戰:靈活解析、運算與格式化的工具函數庫

     編輯:PHP 相關知識

PHP 內建 DateTime 類已功能強大,但在維護舊專案或處理非標準格式(如「2025-04-15 14:30:00」、「15/04/2025」、「2:30 PM」)時,仍需靈活的解析與運算封裝。本文提供兩個實用函數:strToTime() 兼容多格式字串轉時間戳,dateAdd() 模仿 VB/VBA DateAdd 進行區間運算。

1. 通用字串轉時間戳:strToTime()

<?php
/**
 * 將多種常見日期時間字串解析為 Unix 時間戳
 * 支援格式:
 *   - 已是數字時間戳 → 直接返回
 *   - "Y-m-d H:i:s" / "Y/m/d H:i:s" / "Y-m-d" / "Y/m/d"
 *   - "m/d/Y H:i:s A" (美式 12 小時制)
 *   - 其他 strtotime() 可識別格式
 *
 * @param string|int $datetime
 * @return int Unix 時間戳,失敗返回 0
 */
function strToTime(string|int $datetime): int
{
    // 已是數字時間戳
    if (is_numeric($datetime)) {
        return (int)$datetime;
    }

    $datetime = trim($datetime);
    if ($datetime === '') return 0;

    // 嘗試直接用 strtotime(支援極多格式)
    $ts = strtotime($datetime);
    if ($ts !== false) {
        return $ts;
    }

    // 兜底:手動解析常見非標準格式
    // 格式: "m/d/Y h:i:s A" 如 "04/15/2025 02:30:00 PM"
    if (preg_match('/^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})\s+(\d{1,2}):(\d{2}):(\d{2})\s*(AM|PM)?$/i', $datetime, $m)) {
        $month = (int)$m[1];
        $day   = (int)$m[2];
        $year  = (int)$m[3];
        $hour  = (int)$m[4];
        $min   = (int)$m[5];
        $sec   = (int)$m[6];
        $ampm  = strtoupper($m[7] ?? '');

        if ($ampm === 'PM' && $hour < 12) $hour += 12;
        if ($ampm === 'AM' && $hour === 12) $hour = 0;

        return mktime($hour, $min, $sec, $month, $day, $year);
    }

    // 純日期 "m/d/Y" 或 "d/m/Y" (簡單啟發式)
    if (preg_match('/^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})$/', $datetime, $m)) {
        // 無法確定日/月順序,預設月/日/年 (美式);如需歐式請調整
        return mktime(0, 0, 0, (int)$m[1], (int)$m[2], (int)$m[3]);
    }

    return 0;
}
?>

使用範例

<?php
$tests = [
    1744700000,
    '2025-04-15 14:30:00',
    '2025/04/15',
    '04/15/2025 02:30:00 PM',
    '15-04-2025',
    'invalid',
];

foreach ($tests as $t) {
    $ts = strToTime($t);
    echo "$t  →  " . ($ts ? date('Y-m-d H:i:s', $ts) : '解析失敗') . "\n";
}
?>

2. 日期區間運算:dateAdd()

模仿 DateAdd(interval, number, date),支援年、季、月、日、時、分、秒。

<?php
/**
 * 日期時間加減運算
 *
 * @param string $interval 區間類型:yyyy|q|m|y|d|w|ww|h|n|s
 * @param int    $number   加減數量(負數為減)
 * @param string|int $date 基準時間(字串或時間戳)
 * @return int 運算後的 Unix 時間戳
 */
function dateAdd(string $interval, int $number, string|int $date): int
{
    $ts = strToTime($date);
    if ($ts === 0) return 0;

    $dt = new DateTime('@' . $ts);
    $dt->setTimezone(new DateTimeZone('UTC')); // 內部用 UTC 運算,避免 DST 影響

    $interval = strtolower($interval);
    $mod = '';

    switch ($interval) {
        case 'yyyy': $mod = $number . ' years'; break;
        case 'q':    $mod = ($number * 3) . ' months'; break; // 季度 = 3個月
        case 'm':    $mod = $number . ' months'; break;
        case 'y':    // 年內天數 = 日
        case 'd':    // 日
        case 'w':    // 星期幾 (同日)
            $mod = $number . ' days'; break;
        case 'ww':   $mod = ($number * 7) . ' days'; break; // 週 = 7天
        case 'h':    $mod = $number . ' hours'; break;
        case 'n':    $mod = $number . ' minutes'; break;
        case 's':    $mod = $number . ' seconds'; break;
        default:
            throw new InvalidArgumentException("Unsupported interval: $interval");
    }

    $dt->modify($mod);
    return $dt->getTimestamp();
}
?>

使用範例

<?php
$base = '2025-04-15 14:30:00';

echo "基準: " . date('Y-m-d H:i:s', strToTime($base)) . "\n";
echo "+1 年: "      . date('Y-m-d H:i:s', dateAdd('yyyy', 1, $base)) . "\n";
echo "+2 季: "      . date('Y-m-d H:i:s', dateAdd('q', 2, $base)) . "\n";
echo "+3 月: "      . date('Y-m-d H:i:s', dateAdd('m', 3, $base)) . "\n";
echo "+10 天: "     . date('Y-m-d H:i:s', dateAdd('d', 10, $base)) . "\n";
echo "+2 週: "      . date('Y-m-d H:i:s', dateAdd('ww', 2, $base)) . "\n";
echo "+5 小時: "    . date('Y-m-d H:i:s', dateAdd('h', 5, $base)) . "\n";
echo "+30 分: "     . date('Y-m-d H:i:s', dateAdd('n', 30, $base)) . "\n";
echo "-15 秒: "     . date('Y-m-d H:i:s', dateAdd('s', -15, $base)) . "\n";
?>

現代最佳實踐:直接使用 DateTime Immutable

新專案建議直接使用 PHP 5.5+ 提供的 DateTimeImmutable,鏈式操作、不可變、時區安全:

<?php
$dt = new DateTimeImmutable('2025-04-15 14:30:00', new DateTimeZone('Asia/Taipei'));

$dt1 = $dt->modify('+1 year')->modify('+3 months');
$dt2 = $dt->add(new DateInterval('P10D'));        // +10 天
$dt3 = $dt->sub(new DateInterval('PT5H30M'));     // -5小時30分

echo $dt1->format('Y-m-d H:i:s') . "\n";
echo $dt2->format('Y-m-d H:i:s') . "\n";
echo $dt3->format('Y-m-d H:i:s') . "\n";

// 計算差值
$diff = $dt1->diff($dt);
echo "相差: {$diff->y}年 {$diff->m}月 {$diff->d}天 {$diff->h}時 {$diff->i}分 {$diff->s}秒\n";
?>

常見陷阱提醒

  • 時區:strtotime/mktime 使用 date.timezone 設定;跨時區運算請明確設置 DateTimeZone。
  • 夏令時 (DST):modify('+1 day') 在 DST 切換日可能非 24 小時;用 UTC 運算再轉時區可避免。
  • 月份溢位:+1 month 在 1/31 會變 3/3 (或 2/28);DateTime 行為為「盡量保留日,超出則歸入下月最後一天」。
  • 輸入驗證:永不信任用戶輸入的日期格式,解析前嚴格正則或白名單驗證。

無論是維護舊碼還是編寫新功能,掌握 strToTime() 兼容解析與 DateTimeImmutable 現代運算,讓日期時間處理不再是 Bug 溫床。

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