PHP 日期
date() formatting, timestamps, the DateTime class, timezones and intervals.
Timestamps and formatting
PHP stores Unix timestamps. date() formats them into local time, depending on the default timezone.
<?php
date_default_timezone_set("Asia/Shanghai");
$ts = 1700000000;
echo date("Y-m-d H:i:s", $ts), "\n";
echo date("Y/m/d", $ts), "\n";
echo date("D, d M Y", $ts), "\n";
echo date("N l", $ts), "\n";
echo date("t", $ts), "\n";
echo date("L", $ts), "\n";2023-11-15 06:13:20
2023/11/15
Wed, 15 Nov 2023
3 Wednesday
30
0The same 1700000000 is 2023-11-14 22:13:20 in UTC and 2023-11-15 06:13:20 in Shanghai — different days. The classic cross-timezone bug.
Format characters
| Char | Meaning | Example |
|---|---|---|
| Y | 四位年 | 2023 |
| m | 两位月 | 11 |
| d | 两位日 | 15 |
| H | 24 小时制 | 06 |
| i | 分 | 13 |
| s | 秒 | 20 |
| N | 星期几 1-7 | 3 |
| w | 星期几 0-6(周日为 0) | 3 |
| t | 本月天数 | 30 |
| L | 是否闰年 | 0 / 1 |
| U | Unix 时间戳 | 1700000000 |
| c | ISO 8601 | 2023-11-15T06:13:20+08:00 |
strtotime
strtotime() parses human-readable descriptions into timestamps — handy for relative arithmetic.
<?php
date_default_timezone_set("Asia/Shanghai");
$ts = 1700000000;
echo strtotime("+1 week", $ts), "\n";
echo date("Y-m-d", strtotime("+1 week", $ts)), "\n";
echo date("Y-m-d", strtotime("-1 day", $ts)), "\n";
echo date("Y-m-d", strtotime("+1 month", $ts)), "\n";
echo date("Y-m-d", strtotime("last day of this month", $ts)), "\n";
echo date("Y-m-d H:i:s", strtotime("2023-11-15 08:00:00")), "\n";1700604800
2023-11-22
2023-11-14
2023-12-15
2023-11-30
2023-11-15 08:00:00The DateTime class
DateTime is the OOP approach with chaining, timezones and intervals. Prefer it in new code.
<?php
$tz = new DateTimeZone("Asia/Shanghai");
$d = new DateTime("2023-11-15 06:13:20", $tz);
echo $d->format("Y-m-d H:i:s"), "\n";
$d->modify("+1 month");
echo $d->format("Y-m-d H:i:s"), "\n";
// 月末加一个月会溢出
$e = new DateTime("2024-01-31");
$e->modify("+1 month");
echo $e->format("Y-m-d"), "\n";
// 日期差
$a = new DateTime("2023-11-15");
$b = new DateTime("2024-02-20");
$diff = $a->diff($b);
echo $diff->days, "\n";
echo $diff->format("%m 个月 %d 天"), "\n";2023-11-15 06:13:20
2023-12-15 06:13:20
2024-03-02
97
3 个月 5 天2024-01-31 +1 month yields 2024-03-02 because February has no 31st. For month-end to month-end use modify("last day of +1 month").Everyday operations
<?php
$tz = new DateTimeZone("Asia/Shanghai");
// 今天零点
$today = new DateTime("today", $tz);
echo $today->format("Y-m-d H:i:s"), "\n";
// 本周一
$mon = new DateTime("monday this week", $tz);
echo $mon->format("Y-m-d"), "\n";
// 两个日期之间所有天
$start = new DateTime("2023-11-15");
$end = new DateTime("2023-11-18");
$period = new DatePeriod($start, new DateInterval("P1D"), $end->modify("+1 day"));
foreach ($period as $day) {
echo $day->format("m-d") . " ";
}
echo "\n";2023-11-15 00:00:00
2023-11-13
11-15 11-16 11-17 11-18DatePeriod excludes the end, so modify("+1 day") is needed to include the final day — its most common gotcha.
Timezone rules
- Store UTC —— keep timestamps or UTC datetimes in the database
- Convert on display —— render in the user's timezone
- Set the default ——
date_default_timezone_set()ordate.timezonein php.ini - Don't compare date strings —— use
DateTimeobjects
UTC is the safest default; convert at the display layer.时间戳与格式化
PHP 内部用 Unix 时间戳(1970-01-01 00:00:00 UTC 起的秒数)。date() 把时间戳格式化成本地时间,结果取决于默认时区。
<?php
date_default_timezone_set("Asia/Shanghai");
$ts = 1700000000;
echo date("Y-m-d H:i:s", $ts), "\n";
echo date("Y/m/d", $ts), "\n";
echo date("D, d M Y", $ts), "\n";
echo date("N l", $ts), "\n";
echo date("t", $ts), "\n";
echo date("L", $ts), "\n";2023-11-15 06:13:20
2023/11/15
Wed, 15 Nov 2023
3 Wednesday
30
0同一个 1700000000,在 UTC 下是 2023-11-14 22:13:20,在上海是 2023-11-15 06:13:20。差 8 小时,日期都不一样——这是跨时区系统最容易出错的地方。
格式字符速查
| 字符 | 含义 | 示例 |
|---|---|---|
| Y | 四位年 | 2023 |
| m | 两位月 | 11 |
| d | 两位日 | 15 |
| H | 24 小时制 | 06 |
| i | 分 | 13 |
| s | 秒 | 20 |
| N | 星期几 1-7 | 3 |
| w | 星期几 0-6(周日为 0) | 3 |
| t | 本月天数 | 30 |
| L | 是否闰年 | 0 / 1 |
| U | Unix 时间戳 | 1700000000 |
| c | ISO 8601 | 2023-11-15T06:13:20+08:00 |
strtotime
strtotime() 把人类可读的时间描述转成时间戳,做相对时间运算最方便。
<?php
date_default_timezone_set("Asia/Shanghai");
$ts = 1700000000;
echo strtotime("+1 week", $ts), "\n";
echo date("Y-m-d", strtotime("+1 week", $ts)), "\n";
echo date("Y-m-d", strtotime("-1 day", $ts)), "\n";
echo date("Y-m-d", strtotime("+1 month", $ts)), "\n";
echo date("Y-m-d", strtotime("last day of this month", $ts)), "\n";
echo date("Y-m-d H:i:s", strtotime("2023-11-15 08:00:00")), "\n";1700604800
2023-11-22
2023-11-14
2023-12-15
2023-11-30
2023-11-15 08:00:00DateTime 类
DateTime 是面向对象的做法,支持链式调用、时区、区间计算,比一堆函数清楚。新代码优先用它。
<?php
$tz = new DateTimeZone("Asia/Shanghai");
$d = new DateTime("2023-11-15 06:13:20", $tz);
echo $d->format("Y-m-d H:i:s"), "\n";
$d->modify("+1 month");
echo $d->format("Y-m-d H:i:s"), "\n";
// 月末加一个月会溢出
$e = new DateTime("2024-01-31");
$e->modify("+1 month");
echo $e->format("Y-m-d"), "\n";
// 日期差
$a = new DateTime("2023-11-15");
$b = new DateTime("2024-02-20");
$diff = $a->diff($b);
echo $diff->days, "\n";
echo $diff->format("%m 个月 %d 天"), "\n";2023-11-15 06:13:20
2023-12-15 06:13:20
2024-03-02
97
3 个月 5 天2024-01-31 +1 month 得到 2024-03-02,因为 2 月没有 31 号,PHP 往后溢出。想要「月末对月末」用 last day of next month 或 modify("last day of +1 month")。常用日期操作
<?php
$tz = new DateTimeZone("Asia/Shanghai");
// 今天零点
$today = new DateTime("today", $tz);
echo $today->format("Y-m-d H:i:s"), "\n";
// 本周一
$mon = new DateTime("monday this week", $tz);
echo $mon->format("Y-m-d"), "\n";
// 两个日期之间所有天
$start = new DateTime("2023-11-15");
$end = new DateTime("2023-11-18");
$period = new DatePeriod($start, new DateInterval("P1D"), $end->modify("+1 day"));
foreach ($period as $day) {
echo $day->format("m-d") . " ";
}
echo "\n";2023-11-15 00:00:00
2023-11-13
11-15 11-16 11-17 11-18DatePeriod 的结束时间是开区间,所以要 modify("+1 day") 才能包含最后一天。这是它最常见的坑。
时区处理原则
- 存 UTC —— 数据库里一律存 UTC 时间或时间戳
- 显示时转换 —— 按用户所在时区渲染
- 配置默认时区 ——
date_default_timezone_set()或 php.ini 的date.timezone - **别用
date()比较** —— 字符串比较日期只在同格式同格式下成立,用DateTime对象比
UTC 是最安全的默认值,显示层再转。设成 Asia/Shanghai 看似省事,但服务器换地区就会全线偏移。