strtotime 支援極其豐富的自然語言相對時間:
<?php
echo date('Y-m-d', strtotime('first day of this month')); // 本月 1 號
echo date('Y-m-d', strtotime('last day of this month')); // 本月最後一天
echo date('Y-m-d', strtotime('first day of last month')); // 上月 1 號
echo date('Y-m-d', strtotime('last day of last month')); // 上月最後一天
echo date('Y-m-d', strtotime('first day of next month')); // 下月 1 號
echo date('Y-m-d', strtotime('last day of next month')); // 下月最後一天
echo date('t', strtotime('this month')); // 本月天數
echo date('t', strtotime('last month')); // 上月天數
?>
<?php
/**
* 獲取指定月份首尾日期
* @param string $baseDate 基準日期 (任意格式 strtotime 可解析)
* @param int $monthOffset 月份偏移:0=本月, -1=上月, 1=下月
* @return array ['first'=>'Y-m-d', 'last'=>'Y-m-d', 'days'=>int]
*/
function getMonthRange(string $baseDate = 'now', int $monthOffset = 0): array {
$first = date('Y-m-01', strtotime("$baseDate $monthOffset month"));
$last = date('Y-m-t', strtotime($first)); // 't' = 該月天數
return [
'first' => $first,
'last' => $last,
'days' => (int)date('t', strtotime($first)),
];
}
// 使用
print_r(getMonthRange('2024-01-15', 0)); // 本月
print_r(getMonthRange('2024-01-15', -1)); // 上月
print_r(getMonthRange('2024-01-15', 1)); // 下月
/* 輸出:
Array([first]=>2024-01-01 [last]=>2024-01-31 [days]=>31)
Array([first]=>2023-12-01 [last]=>2023-12-31 [days]=>31)
Array([first]=>2024-02-01 [last]=>2024-02-29 [days]=>29) // 閏年自動處理
*/
<?php
function getMonthRangeOO(string $baseDate = 'now', int $monthOffset = 0): array {
$dt = new DateTime($baseDate);
$dt->modify("first day of $monthOffset month"); // 負數自動處理
$first = $dt->format('Y-m-d');
$dt->modify('last day of this month');
$last = $dt->format('Y-m-d');
$days = (int)$dt->format('t');
return compact('first', 'last', 'days');
}
| 錯誤寫法 | 問題 | 正確寫法 |
|---|---|---|
date('Y-m-d', strtotime('-1 month')) | 1月31日 -1月 → 12月31日 (正確),但 3月31日 -1月 → 2月28/29日 (非預期) | strtotime('first day of last month') |
date('Y-m-d', strtotime('+1 month')) | 1月31日 +1月 → 3月3日 (因 2月無31日) | strtotime('first day of next month') |
手動拼接 date('Y-m-01', strtotime("$year-$month-01")) | 月份 0 或 13 導致 1970 年 | 使用 modify('first day of ...') 自動跨年 |
<?php
function getMonthOptions(int $startYear = 2020, int $endYear = 2030): array {
$options = [];
for ($y = $startYear; $y <= $endYear; $y++) {
for ($m = 1; $m <= 12; $m++) {
$value = sprintf('%04d-%02d', $y, $m);
$label = date('Y年m月', strtotime($value));
$options[$value] = $label;
}
}
return $options;
}
// HTML
echo '';
<?php
function buildMonthWhere(string $column, string $baseDate = 'now', int $offset = 0): string {
$range = getMonthRange($baseDate, $offset);
// 包含首尾全天:>= 首日 00:00:00 AND < 下月首日 00:00:00
$nextMonthFirst = date('Y-m-d', strtotime($range['first'] . ' +1 month'));
return "`$column` >= '{$range['first']} 00:00:00' AND `$column` < '$nextMonthFirst 00:00:00'";
}
// 使用
$where = buildMonthWhere('created_at', '2024-01-15', -1);
// `created_at` >= '2023-12-01 00:00:00' AND `created_at` < '2024-01-01 00:00:00'
composer require nesbot/carbon
<?php
use Carbon\Carbon;
$now = Carbon::now();
echo $now->startOfMonth()->format('Y-m-d'); // 本月首日
echo $now->endOfMonth()->format('Y-m-d'); // 本月末日
echo $now->copy()->subMonth()->startOfMonth(); // 上月首日
echo $now->daysInMonth; // 本月天數
// 區間迭代
$period = CarbonPeriod::create('2024-01-01', '2024-12-31', '1 month');
foreach ($period as $date) {
echo $date->format('Y-m') . "\n";
}
Carbon 提供不可變物件、鏈式調用、國際化、測試輔助 (Carbon::setTestNow()),是現代 PHP 日期處理首選。