方法一:strtotime + date (標準做法)
<?php
$dateStr = '2011-12-12';
// 1. 解析字串為 Unix 時間戳
$timestamp = strtotime($dateStr);
// false 表示解析失敗
if ($timestamp === false) {
throw new InvalidArgumentException("無效日期格式: $dateStr");
}
// 2. 格式化為資料庫標準格式 (YYYY-MM-DD)
$dbDate = date('Y-m-d', $timestamp);
// 或含時間: date('Y-m-d H:i:s', $timestamp)
echo $dbDate; // 2011-12-12
// 常見格式支援
$formats = [
'2011-12-12',
'12/12/2011',
'12-12-2011',
'December 12, 2011',
'12 Dec 2011',
'20111212',
'now',
'+1 week',
'last Monday',
];
foreach ($formats as $f) {
echo "$f => " . date('Y-m-d', strtotime($f)) . "\n";
}
?>
方法二:DateTime 物件導向 (PHP 5.2+ 推薦)
<?php
$dateStr = '2011-12-12';
try {
// 自動偵測格式
$dt = new DateTime($dateStr);
$dbDate = $dt->format('Y-m-d');
echo $dbDate; // 2011-12-12
} catch (Exception $e) {
echo "解析失敗: " . $e->getMessage();
}
// 指定格式解析 (更嚴格、效能更好)
$dt = DateTime::createFromFormat('Y-m-d', $dateStr);
if ($dt) {
echo $dt->format('Y-m-d');
}
// 時區處理
$dt = new DateTime('2011-12-12 12:00:00', new DateTimeZone('Asia/Taipei'));
$dt->setTimezone(new DateTimeZone('UTC'));
echo $dt->format('Y-m-d H:i:s'); // 轉 UTC 存入 DB
?>
方法三:手動解析 (不推薦,教學用)
<?php
// 原始文章的第二種方式:手動切割
$date = '2011-12-12';
$year = (int)substr($date, 0, 4);
$month = (int)substr($date, 5, 2);
$day = (int)substr($date, 8, 2);
// 使用 mktime 組合 (已棄用參數順序注意)
$_date = date('M-d-Y', mktime(0, 0, 0, $month, $day, $year));
// 結果: Dec-12-2011 (非標準 DB 格式)
// 正確組合
$dbDate = sprintf('%04d-%02d-%02d', $year, $month, $day);
?>
資料庫欄位類型對應
| MySQL/PostgreSQL 類型 | PHP 格式化 | 範例 |
DATE | Y-m-d | 2024-01-15 |
DATETIME / TIMESTAMP | Y-m-d H:i:s | 2024-01-15 14:30:00 |
TIME | H:i:s | 14:30:00 |
YEAR | Y | 2024 |
最佳實踐建議
- 統一用 UTC 存儲:應用層處理時區轉換,DB 僅存 UTC
- 使用 DateTime 物件:型別安全、可鏈式操作、支援微秒 (PHP 7.1+)
- 輸入驗證:
DateTime::createFromFormat() 嚴格檢查格式
- 預處理語句:直接傳入 DateTime 物件 (PDO 支援) 或格式化字串
<?php
// PDO 範例
$stmt = $pdo->prepare('INSERT INTO events (name, event_date) VALUES (?, ?)');
$dt = new DateTime('2024-12-25 18:00:00', new DateTimeZone('Asia/Taipei'));
$stmt->execute(['Christmas', $dt->format('Y-m-d H:i:s')]);
// 或直接傳入 DateTime (需 PDO::ATTR_EMULATE_PREPARES = false)
$stmt->execute(['Christmas', $dt]);
?>
常見陷阱
| 程式碼 | 問題 | 修正 |
date('Y-m-d', '2024-01-15') | date() 第二參數需為整數時間戳 | date('Y-m-d', strtotime('2024-01-15')) |
strtotime('31-02-2024') | 回傳 false 但不報錯 | 檢查回傳值 !== false |
new DateTime('2024/13/01') | 拋出 Exception | try-catch 捕獲 |
| 時區不一致 | 伺服器/DB/應用層時區不同導致偏移 | 統一設定 date_default_timezone_set('UTC') |