
PHP date() 怎么用:日期格式、常见参数与示例
PHP 的 date() 用于把一个时间点格式化为字符串。它适合生成页面上的日期、时间、文件名中的时间标记,以及采用固定格式的日志内容。它本身不负责解析用户输入、不提供本地化翻译,也不会自动验证某个日期字符串是否真实存在。
掌握 date(),重点是分清格式字符串里的特殊字符、时间戳的单位和时区的来源。本文从基本用法讲起,再说明周数、闰年、微秒等容易出错的边界,并给出适用于常见场景的代码。
一、基本语法与参数
date() 的基本形式如下:
<?php
date(string $format, ?int $timestamp = null): string
?>
$format:必填的格式字符串,由格式字符和普通文本组成。$timestamp:可选的 Unix 时间戳,以秒为单位。省略或传入null时,使用当前时间。- 返回值:格式化后的字符串。
下面的代码可以直接运行:
<?php
date_default_timezone_set('Asia/Shanghai');
echo date('Y-m-d H:i:s');
// 例如:2025-03-08 14:30:00,具体时间取决于运行时的当前时刻
$timestamp = 1700000000;
echo date('Y-m-d H:i:s', $timestamp);
// 2023-11-15 06:13:20
?>
时间戳表示从 Unix 纪元时刻 1970-01-01 00:00:00 UTC 起经过的秒数。它不是毫秒,也不是一个可以直接阅读的日期字符串。若拿到的是毫秒时间戳,应先换算为秒;否则输出的日期会远超预期。
<?php
$milliseconds = 1700000000000;
// 先换算为秒。若原始数据可能包含小数,可按业务要求保留或取整。
$seconds = (int) ($milliseconds / 1000);
echo date('Y-m-d H:i:s', $seconds);
?>
不要把日期字符串直接当作 $timestamp 传入。需要解析字符串时,应使用 DateTimeImmutable::createFromFormat() 或其他明确的解析方法,并检查解析结果及错误信息。
二、最常用的日期格式字符
date() 的格式规则由单个字符表示。未转义的字母可能有特殊含义,因此不能随意把格式字符串当作普通文本拼接。
| 字符 | 含义 | 示例 |
|---|---|---|
Y | 四位年份 | 2025 |
y | 两位年份 | 25 |
m | 两位月份,范围 01 至 12 | 03 |
n | 不补零的月份 | 3 |
d | 两位月份中的日期 | 08 |
j | 不补零的日期 | 8 |
H | 24 小时制小时,补零 | 09、21 |
G | 24 小时制小时,不补零 | 9、21 |
h | 12 小时制小时,补零 | 09 |
g | 12 小时制小时,不补零 | 9 |
i | 分钟,补零 | 05 |
s | 秒,补零 | 07 |
a / A | 小写或大写的上午、下午标记 | pm / PM |
例如,常见的年月日和 24 小时制时间格式是 Y-m-d H:i:s。格式中的连字符、空格和冒号会原样输出:
<?php
date_default_timezone_set('Asia/Shanghai');
echo date('Y-m-d H:i:s');
// 例如:2025-03-08 14:05:07
echo date('Y年n月j日 G:i');
// 例如:2025年3月8日 14:5
?>
i 是分钟,m 是月份;这两个字符最容易写混。写成 H:m:s 会把月份放到分钟的位置,不会得到预期结果。
三、星期、月份长度与闰年
星期和周数有多套定义,使用时应先确定业务规则:
D:英文星期缩写,如Mon。l(小写字母 L):英文星期全称,如Monday。N:ISO-8601 星期数字,星期一为1,星期日为7。w:星期数字,星期日为0,星期六为6。W:ISO-8601 周数,补零的两位数。o:ISO 周数对应的周所属年份,可能与日历年份Y不同。
如果系统需要按 ISO 周展示“周次”,应该把 o 和 W 配套使用,而不是简单地把 Y 与 W 拼在一起。年初、年末的日期可能属于相邻的 ISO 周年。
<?php
date_default_timezone_set('UTC');
$timestamp = strtotime('2021-01-01 12:00:00 UTC');
echo date('Y-m-d', $timestamp);
// 2021-01-01
echo date('o-\WW', $timestamp);
// 2020-W53
?>
t 返回当前月份的天数,L 返回当前年份是否为闰年:闰年为 1,平年为 0。
<?php
date_default_timezone_set('UTC');
$timestamp = strtotime('2024-02-10 12:00:00 UTC');
echo date('Y-m-t', $timestamp);
// 2024-02-29
echo date('L', $timestamp);
// 1,表示闰年
?>
需要生成某个月的末日时,可以用 t,不必自行写“二月有几天”的判断。但如果程序要处理严格的日历业务,仍应使用清晰的日期对象和业务规则,避免通过字符串拼接或模糊解析来推算日期。
四、时区:不要依赖服务器默认值
date() 根据 PHP 当前配置的默认时区解释并格式化时间。若没有显式设置,PHP 会使用配置中的 date.timezone;不同服务器、容器或开发环境的配置可能不同。同一时间戳因此可能输出不同的本地日期和时间。
在应用入口明确设置时区,可以让行为更可预测:
<?php
date_default_timezone_set('Asia/Shanghai');
echo date('Y-m-d H:i:s');
?>
也可以在配置文件中设置 date.timezone。生产应用通常应统一约定:数据库和系统内部时间按 UTC 保存或传递,展示时再转换到用户所处时区。使用 UTC 格式化时,可以这样写:
<?php
date_default_timezone_set('UTC');
echo date('Y-m-d\TH:i:sP');
// 例如:2025-03-08T06:30:00+00:00
?>
格式字符 e 可输出时区标识,例如 UTC 或 Asia/Shanghai;T 输出时区缩写,缩写可能含混,不宜单独用来标识时区;P 输出带冒号的 UTC 偏移量,如 +08:00;Z 输出以秒表示的 UTC 偏移量。
时区偏移并不总是固定的。采用夏令时的地区可能在一年中的不同日期使用不同偏移,因此不要把某个城市简单写死为固定的 +HH:MM。应使用有效的 IANA 时区名称,并在需要时让日期对象根据指定时区处理。
<?php
$zone = new DateTimeZone('America/New_York');
$time = new DateTimeImmutable('2025-07-01 12:00:00', $zone);
echo $time->format('Y-m-d H:i:s P e');
?>
注意,上例的时间字符串没有携带时区,因此会被解释为纽约当地时间。若输入本来就是一个绝对时间点,应在解析时提供明确的偏移或时区,避免把 UTC 时间误当成本地时间。
五、格式中的普通文本要转义
date() 会把格式字符串中的特定字母当作格式字符。想输出字母本身,需要在该字符前加反斜杠。例如,要生成 ISO 风格的时间字面量 T,可写作 \T:
<?php
date_default_timezone_set('UTC');
echo date('Y-m-d\TH:i:sP');
// 例如:2025-03-08T06:30:00+00:00
?>
PHP 字符串本身也有转义规则,因此要同时考虑 PHP 字符串和 date() 格式的转义。单引号字符串中,下面这种写法会让 date() 收到反斜杠和字母 T:
<?php
$format = 'Y-m-d\TH:i:s';
echo date($format);
?>
如果要输出较长的固定文本,逐个转义字母容易漏掉。可将不需要格式化的部分放在格式字符串之外,或者使用 DateTimeImmutable::format() 时同样遵循格式字符转义规则。对用户可配置的格式尤其要谨慎:格式字符可能改变输出含义,不能把它当作普通文本模板。
六、适合机器交换的格式
系统间交换日期时间时,尽量使用带时区信息、含义明确的格式,避免使用像 03/08/25 这样容易产生歧义的写法。PHP 提供了常用格式字符:
c:ISO 8601 风格的日期时间,包含偏移量。r:RFC 2822 风格的日期时间。U:Unix 时间戳,以十进制表示。
<?php
date_default_timezone_set('UTC');
$timestamp = 1700000000;
echo date('c', $timestamp);
// 2023-11-14T22:13:20+00:00
echo date('r', $timestamp);
// Tue, 14 Nov 2023 22:13:20 +0000
echo date('U', $timestamp);
// 1700000000
?>
需要符合某个接口约定时,应以该接口的规范为准,确认是否要求 UTC、是否要求毫秒、是否允许偏移量等。仅仅使用 c 并不代表一定满足所有接口的日期时间要求。
七、微秒与毫秒的边界
date() 接收的是以秒为单位的整数时间戳,不能通过它保留高精度的微秒时间。格式字符 u 在 date() 中会输出 000000;v 会输出 000。即使格式字符串写了这些字符,也不会让秒级时间戳获得真实的微秒或毫秒信息。
如果确实需要输出微秒,应使用支持微秒的时间对象,例如 DateTimeImmutable:
<?php
$time = DateTimeImmutable::createFromFormat(
'Y-m-d H:i:s.u',
'2025-03-08 14:30:15.123456',
new DateTimeZone('UTC')
);
if ($time === false) {
throw new RuntimeException('日期解析失败');
}
echo $time->format('Y-m-d H:i:s.u');
// 2025-03-08 14:30:15.123456
?>
在 PHP 中,date() 格式中的 u 和日期对象的微秒能力不可混为一谈。若数据来源只有整数秒,再改用日期对象也不会凭空获得真实的小数秒。
八、日期解析、无效日期与宽松输入
date() 只负责格式化,不验证一个用户提交的字符串。例如,2025-02-30 不是有效的日历日期。若把它交给宽松解析函数,解析器可能会将其归一化到另一个日期,而不是按业务要求直接拒绝。因此,涉及预约、账单周期或生日等输入时,必须显式校验。
可以使用 DateTimeImmutable::createFromFormat() 按指定格式解析,并检查解析错误,同时比较解析后的值与原始输入:
<?php
$input = '2025-02-30';
$date = DateTimeImmutable::createFromFormat(
'!Y-m-d',
$input,
new DateTimeZone('UTC')
);
$errors = DateTimeImmutable::getLastErrors();
// PHP 8.2 及之后,若没有警告或错误,getLastErrors() 可能返回 false。
$hasErrors = $errors !== false
&& ($errors['warning_count'] > 0 || $errors['error_count'] > 0);
if (
$date === false
|| $hasErrors
|| $date->format('Y-m-d') !== $input
) {
echo '日期无效';
} else {
echo $date->format('Y-m-d');
}
?>
这里的 ! 会把未指定的字段重置到默认值,避免当前时分秒等信息残留。对于只接受 YYYY-MM-DD 的场景,严格格式和回读比较能帮助拦截部分自动修正后的非法输入。实际校验还应根据业务要求处理输入类型、空值和范围。
strtotime() 能解析许多英文日期表达式,但其输入规则相对宽松,诸如“下周一”这样的表达式还依赖调用时刻和时区。它适合受控输入或表达式解析,不适合不经校验地充当严格的用户日期验证器。
九、什么时候选 date(),什么时候用 DateTimeImmutable
只需要把已有时间戳格式化为简单字符串时,date() 通常够用。下面这些场景则更适合使用 DateTimeImmutable 或相关日期时间类:
- 需要解析指定格式,并判断输入是否合法。
- 需要从一个时间点转换到另一个时区。
- 需要进行日期加减、比较,或处理带微秒的时间。
- 希望避免直接修改已有日期对象,降低共享状态带来的意外变化。
例如,将 UTC 时间点显示为纽约当地时间:
<?php
$instant = new DateTimeImmutable(
'2025-03-08T18:00:00+00:00'
);
$newYorkTime = $instant->setTimezone(
new DateTimeZone('America/New_York')
);
echo $newYorkTime->format('Y-m-d H:i:s P e');
?>
DateTimeImmutable 的方法返回新的对象,不会直接改写原对象。进行多步转换时,明确保存返回值可以让代码更易理解。
十、中文月份和星期不是 date() 的本地化功能
D、l、M、F 等字符输出的是英文星期或月份名称。设置系统区域、修改时区,或调用 setlocale(),都不会让 date() 自动输出中文月份名称。
如果需要按语言和地区格式化日期,例如中文星期、当地习惯的月份顺序或地区化数字,应考虑 PHP 的 IntlDateFormatter(需要相应的 Intl 扩展)。这类需求属于国际化格式化,而不是单纯使用 date() 的格式字符。
如果页面只需要固定的中文年月日,也可以组合数字和中文文本:
<?php
date_default_timezone_set('Asia/Shanghai');
echo date('Y年n月j日');
?>
这适用于固定的中文展示格式,不适合需要多语言、地区习惯切换或复杂本地化规则的应用。
十一、输出安全:格式化日期不等于转义内容
date() 会生成字符串,但它不是 HTML 转义函数,也不会自动对输入进行过滤。普通的固定格式日期通常只包含数字、分隔符和固定文本;不过,如果输出内容还包括用户可控的格式字符串、标签文本或其他数据,仍应根据输出上下文进行编码。
在 HTML 页面中输出动态文本,通常应使用 htmlspecialchars(),并选择合适的字符集与标志位:
<?php
date_default_timezone_set('Asia/Shanghai');
$displayDate = date('Y-m-d H:i:s');
echo htmlspecialchars(
$displayDate,
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
?>
这并不意味着日期格式化本身能防止跨站脚本攻击。HTML、JavaScript、URL、CSS 和 SQL 各自有不同的安全边界,必须针对最终使用场景处理数据。不要因为一个值“看起来像日期”就把它直接拼进 SQL;数据库查询应使用参数化语句。文件名中的时间戳也不能代替权限检查或路径校验。
如果格式字符串由用户提供,应限制允许的格式,避免用户控制输出内容或利用格式规则改变程序行为。更稳妥的做法是由程序端选定格式,再将格式化结果作为普通数据输出。
十二、常见错误速查
| 常见问题 | 原因 | 建议 |
|---|---|---|
| 分钟位置显示成月份 | 把分钟写成了 m,正确字符是 i | 日期用 m,分钟用 i |
| 时间差了数小时 | 默认时区与预期时区不同,或把本地时间当成 UTC | 明确设置时区,并区分时间点与本地时间 |
| 日期远在未来 | 把毫秒时间戳当作秒使用 | 确认单位,并在必要时将毫秒换算为秒 |
| 微秒输出为零 | date() 使用秒级时间戳,u 不会补出真实微秒 | 使用带微秒的日期对象和真实高精度数据 |
| 年初周数显示不正确 | 把 ISO 周数 W 和日历年份 Y 混用 | 用 o-W 表示 ISO 周年和周数 |
| 字母被替换成数字或英文 | 格式中的字母被当作特殊格式字符 | 用反斜杠转义要原样输出的字符 |
| 中文星期没有生效 | date() 的星期名称不是本地化输出 | 使用 Intl 扩展或固定的中文展示规则 |
| 无效日期被“自动调整” | 采用了宽松解析,没有严格检查日期有效性 | 使用指定格式解析,并检查警告、错误和回读结果 |
总结
date() 的核心用途是把 Unix 秒级时间戳按默认时区和指定格式转换成字符串。记住 Y-m-d H:i:s 中年月日、小时、分钟和秒的字符差异,明确设置时区,并在格式字符串中转义字面字符,就能覆盖大多数简单展示需求。
如果涉及严格输入校验、时区转换、日期运算、微秒或多语言格式化,应选择相应的日期时间类或 Intl 功能。无论日期如何生成,输出到 HTML、SQL 或其他上下文时,都要遵循对应的编码与安全规则。

![AIGC 虚拟主播工具套装:打造身临其境、引人入胜的虚拟主播体验 (AIGC虚拟人生成主要包括[填空1] 和[填空2]两个方面)](http://n.sinaimg.cn/sinakd20230503ac/20/w1000h620/20230503/ed35-e732bd38ef39d4ee8f4ee16c30ec52a9.jpg)



