
PHP date() 格式字符详解:如何输出指定的日期格式
PHP 的 date() 函数可以把 Unix 时间戳转换为指定格式的日期和时间。它适合输出日志时间、页面上的日期、文件名中的时间标记等。函数本身只负责格式化:它不会解析日期字符串,也不会替你处理本地化、时区转换或 HTML 转义。
本文介绍常用格式字符、参数和时区规则,并重点说明容易出错的年份、周数和安全边界。示例要求 PHP 7.0 或更高版本;其中使用格式字符 X 和 x 的例子要求 PHP 8.2 或更高版本。
一、基本用法:格式字符串加 Unix 时间戳
date() 的基本形式是:
<?php
date(string $format, ?int $timestamp = null): string
$format 是由格式字符和普通文本组成的字符串;$timestamp 是 Unix 时间戳,即从 1970-01-01 00:00:00 UTC 起经过的秒数。省略第二个参数,或传入 null,表示使用当前时间。
<?php
echo date('Y-m-d H:i:s');
// 例如:2025-03-08 14:05:09
输出结果取决于 PHP 当前设置的默认时区,因此不同服务器可能显示不同的时刻。要得到稳定、可预期的结果,应明确设置时区,或改用带有明确时区的日期时间对象。
二、常用格式字符速查
date() 的格式字符区分大小写。例如,m 是两位月份,M 是英文月份缩写;h 是 12 小时制小时,H 是 24 小时制小时。下表列出日常最常用的字符。
1. 日、星期与一年中的第几天
| 字符 | 含义 | 示例 |
|---|---|---|
d | 两位日期,范围 01–31 | 08 |
j | 不补零的日期,范围 1–31 | 8 |
D | 英文星期缩写 | Sat |
l(小写 L) | 英文星期全名 | Saturday |
N | ISO 星期编号,星期一为 1,星期日为 7 | 6 |
w | 星期编号,星期日为 0,星期六为 6 | 6 |
S | 英文日期序数后缀 | th |
z | 一年中的第几天,从 0 开始 | 66 |
W | ISO-8601 周数,范围通常为 01–53 | 10 |
S 输出的是英文后缀,如 1st 中的 st、2nd 中的 nd,不是本地化的“日”。z 从 0 开始,所以平年的 1 月 1 日是 0,12 月 31 日是 364;闰年最后一天是 365。
2. 月份
| 字符 | 含义 | 示例 |
|---|---|---|
F | 英文月份全名 | March |
m | 两位月份,范围 01–12 | 03 |
M | 英文月份缩写 | Mar |
n | 不补零的月份,范围 1–12 | 3 |
t | 该月的天数 | 31 |
3. 年份
| 字符 | 含义 | 说明 |
|---|---|---|
Y | 四位日历年 | 例如 2025 |
y | 两位日历年 | 例如 25 |
L | 是否为闰年 | 闰年输出 1,否则输出 0 |
o | ISO 周日期对应的周所属年份 | 用于与 W 配合 |
X | 带符号的扩展年份 | PHP 8.2 起可用 |
x | 不带符号的扩展年份 | PHP 8.2 起可用 |
一般日期展示使用 Y。计算 ISO 周日期时,应使用 o 配合 W,而不是把 Y 和 W 拼在一起。年初、年末的日期可能属于相邻的 ISO 周年,导致 Y 与 o 不同。
4. 时间
| 字符 | 含义 | 示例 |
|---|---|---|
a / A | 小写或大写的上午、下午标记 | pm / PM |
g / h | 12 小时制小时,不补零或补零 | 3 / 03 |
G / H | 24 小时制小时,不补零或补零 | 15 / 15 |
i | 两位分钟 | 05 |
s | 两位秒 | 09 |
u | 微秒,六位数字 | 123456 |
v | 毫秒,三位数字 | 123 |
B | Swatch Internet Time(互联网时间) | 范围为 000–999 |
对普通整数 Unix 时间戳调用 date('u'),通常会得到 000000,因为整数时间戳没有微秒部分。需要保留微秒时,可使用 DateTimeImmutable,或采用支持微秒的时间戳来源。date() 接收的时间戳以秒为单位;不要把毫秒时间戳直接传进去,否则会把单位不匹配的数值当作秒解释。
5. 时区
| 字符 | 含义 | 示例 |
|---|---|---|
e | 时区标识符 | Asia/Shanghai |
I | 是否处于夏令时 | 是为 1,否为 0 |
O | 与 UTC 的时差,小时和分钟之间无冒号 | +0800 |
P | 与 UTC 的时差,小时和分钟之间有冒号 | +08:00 |
T | 时区缩写 | 例如 UTC |
Z | 与 UTC 的时差,单位为秒 | 28800 |
时区缩写并不总是全球唯一或足以标识一个地区。需要明确地区规则时,优先使用 Asia/Shanghai 这类 IANA 时区标识符,而不是依赖缩写。
6. 预定义组合格式
| 字符 | 含义 | 示例形式 |
|---|---|---|
c | ISO 8601 日期时间格式 | 2025-03-08T14:05:09+08:00 |
r | RFC 2822 格式 | Sat, 08 Mar 2025 14:05:09 +0800 |
U | Unix 时间戳 | 以秒表示的时间戳 |
三、组合格式并转义普通字符
格式字符串中,未转义的格式字符会被替换成日期或时间的一部分。因此,要输出普通字母,必须在它前面加反斜杠。若要输出反斜杠本身,则需要额外转义。
<?php
$timestamp = strtotime('2025-03-08 14:05:09 UTC');
echo date('Y-m-d H:i:s', $timestamp);
// 2025-03-08 22:05:09(受默认时区影响)
echo date('Y年n月j日(l)', $timestamp);
// “年”“月”“日”是普通字符;l 会被替换为英文星期全名
echo date('Y-m-d \a\t H:i', $timestamp);
// 例如:2025-03-08 at 22:05
上例中的时间戳由 UTC 时间字符串生成,随后由 date() 按默认时区格式化。如果默认时区是 Asia/Shanghai,结果会比 UTC 快 8 小时。使用 strtotime() 解析字符串时,解析结果同样会受到字符串时区信息和默认时区的影响;对于关键业务逻辑,不要依赖含糊的自然语言日期。
如果格式中包含多个普通字母,应逐个转义,或使用不依赖 date() 的格式化方案。把用户输入直接当作格式字符串,也可能造成结果意外变化:其中任何字母形式的格式字符都可能被解释为日期指令。
四、明确设置时区
可在应用启动时通过 date_default_timezone_set() 设置 PHP 默认时区。时区名称应使用受支持的 IANA 标识符。
<?php
date_default_timezone_set('Asia/Shanghai');
echo date('Y-m-d H:i:s');
// 按上海时区输出当前时间
也可以在配置文件中设置 date.timezone。应用程序通常应在统一的入口或配置层设置时区,不要在多个业务函数中反复更改全局默认值。默认时区是进程级配置,修改它会影响后续未显式指定时区的日期操作。
若同一程序需要处理多个时区,使用 DateTimeImmutable 更清晰:每个对象都携带自己的时区,格式化时不会依赖全局默认时区。
<?php
$utc = new DateTimeImmutable('2025-03-08 14:05:09', new DateTimeZone('UTC'));
$shanghai = $utc->setTimezone(new DateTimeZone('Asia/Shanghai'));
echo $utc->format('Y-m-d H:i:s P');
// 2025-03-08 14:05:09 +00:00
echo $shanghai->format('Y-m-d H:i:s P');
// 2025-03-08 22:05:09 +08:00
setTimezone() 改变的是显示时区,不会改变同一个时间点。存储和传输时间时,可统一采用 UTC,再在展示层转换为用户所在时区。
五、ISO 周数与跨年边界
常见错误是将 Y 和 W 组合成“某年某周”,并假设两者始终属于同一年。ISO-8601 的周从星期一开始;每年的第 1 周是包含当年第一个星期四的那一周。因此,公历年初或年末的几天可能属于前一个或后一个 ISO 周年。
<?php
$date = new DateTimeImmutable('2021-01-01', new DateTimeZone('UTC'));
echo $date->format('Y-m-d');
// 2021-01-01
echo $date->format('o-\WW');
// 2020-W53
需要生成 ISO 周日期时,应使用 o(ISO 周年)与 W(ISO 周数),格式中的字面量 W 也要转义。若用于普通公历日期展示,应使用 Y-m-d;不要因为 ISO 周编号而替换日历年份。
六、参数限制与版本边界
- 时间戳单位是秒。
date()的第二个参数是整数 Unix 时间戳,不是毫秒、微秒,也不是日期字符串。日期字符串需要先解析,或直接交给日期时间对象。 - 参数类型应符合签名。 PHP 8 起,内置函数参数类型检查更加明确;不要依赖把字符串自动转换成整数等隐式行为。传入不合适的参数可能产生警告或类型错误,具体表现与 PHP 版本和调用方式有关。
- 省略时间戳表示当前时间。 未传第二个参数或传入
null时使用当前时刻。若程序需要固定、可测试的结果,应显式传入时间戳或日期时间对象。 - 时间戳表示范围受环境影响。 可表示的日期范围取决于 PHP 版本、平台及整数实现等因素。尤其是 32 位系统,时间戳范围可能比 64 位系统更受限;不要假设任意远古或遥远未来的时间都能在所有部署环境中表示。
- 较新的格式字符有版本要求。 微秒字符
u、扩展年份字符X和x需要核对目标 PHP 版本。本文示例中的X、x从 PHP 8.2 起支持;跨版本代码应查阅对应版本的 PHP 手册。
需要解析用户输入、检查日期有效性、处理日历运算或维护明确时区时,优先使用 DateTimeImmutable、DateTimeZone 等日期时间类,而不是把格式化函数当作通用日期处理工具。
七、本地化:date() 不会自动输出中文星期
date() 的 F、M、D、l 等字符输出英文名称。修改系统区域设置并不会让 date() 自动切换为中文。若需要面向用户的本地化日期,应使用 IntlDateFormatter,并确认服务器安装、启用了 PHP 的 intl 扩展。
<?php
$formatter = new IntlDateFormatter(
'zh_CN',
IntlDateFormatter::LONG,
IntlDateFormatter::NONE,
'Asia/Shanghai'
);
$date = new DateTimeImmutable('2025-03-08 14:05:09', new DateTimeZone('Asia/Shanghai'));
echo $formatter->format($date);
// 类似:2025年3月8日
IntlDateFormatter 的输出样式由区域设置和格式选项决定,适合本地化展示;date() 则适合需要明确、固定格式的场景。两者用途不同,不应把格式字符混用。
八、安全输出:格式化不是转义
date() 不会对 HTML、JavaScript、SQL 或 URL 内容做安全处理。它的格式字符串是格式指令,不是安全边界。通常应由程序自己指定固定格式,不要让用户任意提交格式字符串;如果确实要支持用户选择样式,应将输入映射到有限的预定义选项。
把格式化结果输出到 HTML 文本节点时,仍应按 HTML 上下文进行转义。即使日期结果主要由数字组成,也不要把“某个函数当前只会输出安全字符”当作可复用的安全保证。
<?php
date_default_timezone_set('Asia/Shanghai');
$dateText = date('Y-m-d H:i:s');
$safeDateText = htmlspecialchars(
$dateText,
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
echo '<time>' . $safeDateText . '</time>';
这里 htmlspecialchars() 用于 HTML 文本上下文。HTML 属性、JavaScript 字符串、CSS 和 URL 各有不同的编码规则,不能用同一种转义方式覆盖所有上下文。日期格式化也不能替代 SQL 参数化查询或其他输入验证措施。
九、常见错误检查清单
- 把分钟写成
m。m是月份;分钟是i。Y-m-d H:i:s是常用的年月日时分秒格式。 - 把 12 小时制和 24 小时制混用。
h、g是 12 小时制,通常配合a或A;H、G是 24 小时制。 - 忘记格式字符区分大小写。
D与d、M与m、Y与y各有不同含义。 - 在跨年周数中使用
Y。 ISO 周数应与o配对,而不是与日历年Y配对。 - 把毫秒当秒传给函数。 毫秒时间戳数值通常比秒时间戳大 1000 倍,直接传入会导致日期严重偏移。
- 忽略默认时区。 同一时间戳在不同时区的本地日期和时刻可能不同,跨时区系统应明确设置或转换时区。
- 认为
date()能输出本地化名称。 常用英文月份和星期格式字符不会自动变成中文,应使用国际化扩展处理本地化展示。 - 让用户输入直接决定格式或输出内容。 固定格式、白名单选项、正确的上下文转义,三者解决的是不同问题,不能相互替代。
十、实用格式示例
以下示例统一使用固定时间和时区,适合复制运行并观察格式字符的差异。
<?php
$date = new DateTimeImmutable('2025-03-08 15:04:05', new DateTimeZone('Asia/Shanghai'));
$formats = [
'数据库常用格式' => 'Y-m-d H:i:s',
'中文数字日期(星期英文)' => 'Y年n月j日(D)',
'英文短日期' => 'D, M j, Y',
'带时区偏移' => 'Y-m-d\TH:i:sP',
'ISO 周日期' => 'o-\WW-N',
];
foreach ($formats as $label => $format) {
echo $label . ': ' . $date->format($format) . PHP_EOL;
}
其中 \T 表示输出字面字符 T;o-\WW-N 输出 ISO 周年、周数和星期编号。若将这些内容放入网页,仍要按 HTML 上下文转义。
总结
用好 date(),关键是选对格式字符、明确时间戳单位,并控制时区。普通日期通常使用 Y-m-d,24 小时制时间通常使用 H:i:s;分钟是 i,不是 m;ISO 周数应与 o 配对。需要本地化、微秒精度或多时区计算时,应选择更合适的日期时间 API。最后,格式化结果与用户输入都不能绕过正确的上下文转义和安全输出规则。




