PHP date() 格式字符详解:如何输出指定的日期格式

星座9小时前发布 jdkjadmin
0 00
PHP date() 格式字符详解:如何输出指定的日期格式

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–3108
j不补零的日期,范围 1–318
D英文星期缩写Sat
l(小写 L)英文星期全名Saturday
NISO 星期编号,星期一为 1,星期日为 76
w星期编号,星期日为 0,星期六为 66
S英文日期序数后缀th
z一年中的第几天,从 0 开始66
WISO-8601 周数,范围通常为 01–5310

S 输出的是英文后缀,如 1st 中的 st、2nd 中的 nd,不是本地化的“日”。z 从 0 开始,所以平年的 1 月 1 日是 0,12 月 31 日是 364;闰年最后一天是 365。

2. 月份

字符含义示例
F英文月份全名March
m两位月份,范围 01–1203
M英文月份缩写Mar
n不补零的月份,范围 1–123
t该月的天数31

3. 年份

字符含义说明
Y四位日历年例如 2025
y两位日历年例如 25
L是否为闰年闰年输出 1,否则输出 0
oISO 周日期对应的周所属年份用于与 W 配合
X带符号的扩展年份PHP 8.2 起可用
x不带符号的扩展年份PHP 8.2 起可用

一般日期展示使用 Y。计算 ISO 周日期时,应使用 o 配合 W,而不是把 Y 和 W 拼在一起。年初、年末的日期可能属于相邻的 ISO 周年,导致 Y 与 o 不同。

4. 时间

字符含义示例
a / A小写或大写的上午、下午标记pm / PM
g / h12 小时制小时,不补零或补零3 / 03
G / H24 小时制小时,不补零或补零15 / 15
i两位分钟05
s两位秒09
u微秒,六位数字123456
v毫秒,三位数字123
BSwatch 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. 预定义组合格式

字符含义示例形式
cISO 8601 日期时间格式2025-03-08T14:05:09+08:00
rRFC 2822 格式Sat, 08 Mar 2025 14:05:09 +0800
UUnix 时间戳以秒表示的时间戳

三、组合格式并转义普通字符

格式字符串中,未转义的格式字符会被替换成日期或时间的一部分。因此,要输出普通字母,必须在它前面加反斜杠。若要输出反斜杠本身,则需要额外转义。

<?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 参数化查询或其他输入验证措施。

九、常见错误检查清单

  1. 把分钟写成 m。m 是月份;分钟是 i。Y-m-d H:i:s 是常用的年月日时分秒格式。
  2. 把 12 小时制和 24 小时制混用。h、g 是 12 小时制,通常配合 a 或 A;H、G 是 24 小时制。
  3. 忘记格式字符区分大小写。D 与 d、M 与 m、Y 与 y 各有不同含义。
  4. 在跨年周数中使用 Y。 ISO 周数应与 o 配对,而不是与日历年 Y 配对。
  5. 把毫秒当秒传给函数。 毫秒时间戳数值通常比秒时间戳大 1000 倍,直接传入会导致日期严重偏移。
  6. 忽略默认时区。 同一时间戳在不同时区的本地日期和时刻可能不同,跨时区系统应明确设置或转换时区。
  7. 认为 date() 能输出本地化名称。 常用英文月份和星期格式字符不会自动变成中文,应使用国际化扩展处理本地化展示。
  8. 让用户输入直接决定格式或输出内容。 固定格式、白名单选项、正确的上下文转义,三者解决的是不同问题,不能相互替代。

十、实用格式示例

以下示例统一使用固定时间和时区,适合复制运行并观察格式字符的差异。

<?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。最后,格式化结果与用户输入都不能绕过正确的上下文转义和安全输出规则。

© 版权声明

相关文章