☰
php strftime函数的详细用法
2026/10/8 17:20:16 网站建设 项目流程

前言


strftime()是 PHP 里按本地化格式输出时间的函数:它根据当前设置的区域(locale),把日期时间格式化成"2026年10月6日 星期二"或者"Tuesday, 6 October 2026"这样的形式。它和date()的分工是:date()只输出英文和固定格式,strftime()会跟随 locale 变化,星期名、月份名、日期顺序都会变。


但必须先把最关键的一条讲清楚:strftime()和gmstrftime()在 PHP 8.1.0 中已被废弃(deprecated),计划在 PHP 9.0 移除。在 PHP 8.1 及以上调用它会产生E_DEPRECATED通知。官方给出的迁移方向是:不依赖区域的就换成date()或DateTime::format(),需要本地化的就换成IntlDateFormatter::format()(需要intl扩展)。


也就是说,这个函数现在只有两类读者:一是在 PHP 8.0 及更早版本上维护老项目的开发者,二是正在把老代码迁移出去的人。本文既讲它的完整用法——因为老项目还得看懂、还得改对——也讲迁移的目标写法。


另外要澄清一个常见误解:strftime()的输出不是 PHP 自己实现的,而是直接调用 C 库的strftime()。所以同一段格式串在不同操作系统上结果可能不一样,这是它的固有缺陷,也正是它被废弃的原因之一。官方手册明确说明:并非所有转换说明符都被各种 C 库支持,不被支持的那些在 PHP 里也不可用。


一、基本用法与 locale 设置


函数签名的形状是strftime($format, $timestamp = null):第一个参数是格式串,第二个是可选的 Unix 时间戳,省略时用当前时间。gmstrftime()参数完全一样,区别只在于它按 UTC 解释时间戳,而strftime()按本地时区。


<?php // 适用于 PHP 7.0 ~ 8.0;PHP 8.1 起本函数已废弃
$ts = mktime(15, 30, 0, 10, 6, 2026);

echo strftime('%Y-%m-%d %H:%M:%S', $ts), PHP_EOL;
echo gmstrftime('%Y-%m-%d %H:%M:%S', $ts), PHP_EOL; // 按 UTC 输出

要让输出真的"本地化",必须先设置区域,而且必须指定LC_TIME这个类别:


<?php // 适用于 PHP 7.0 ~ 8.0;PHP 8.1 起本函数已废弃
$ts = mktime(0, 0, 0, 10, 6, 2026);

// setlocale 成功返回新的 locale 字符串,失败返回 false
$old = setlocale(LC_TIME, '0'); // 先记下当前设置

$ok = setlocale(LC_TIME, 'zh_CN.UTF-8'); // Linux 上的中文区域名
if ($ok === false) {
// Windows 上的区域名格式不同,常见写法有 'chs'、'Chinese_China.936'
$ok = setlocale(LC_TIME, 'chs');
}
if ($ok === false) {
echo '区域设置失败,将退回英文输出', PHP_EOL;
}

echo strftime('%Y年%m月%d日 %A', $ts), PHP_EOL;

setlocale(LC_TIME, $old); // 用完恢复,避免影响后续逻辑

setlocale()的返回值一定要检查——区域设置失败时它返回false,而strftime()仍然会正常输出,只是输出的是默认的英文。这是最典型的"代码没报错但结果不对"的情形。另外,区域名在不同系统上差异很大:Linux 上是zh_CN.UTF-8这种形式,Windows 上是chs、Chinese_China.936之类;而且服务器上没装对应的 locale 包时(精简版 Docker 镜像尤其常见)zh_CN.UTF-8会直接失败。这也是为什么依赖strftime()做本地化在生产环境里非常脆弱。


二、转换说明符全表


strftime()的转换说明符来自 C 标准,以百分号开头,大部分字母的含义和date()完全不同(例如%m是月份而不是分钟,分钟是%M),照date()的习惯往里填必错。




说明符含义示例输出



%a星期缩写Tue

%A星期全称Tuesday

%b/%h月份缩写Oct

%B月份全称October

%c首选的日期时间表示Tue Oct 6 15:30:00 2026

%C世纪(两位)20

%d月内第几天,两位补零06

%e月内第几天,空格补位6

%H小时,24 小时制,两位15

%I小时,12 小时制,两位03

%j年内第几天,三位补零279

%m月份,两位10

%M分钟,两位30

%p上下午标记PM

%S秒,两位00

%uISO 星期几,1 到 7(周一为 1)2

%w星期几,0 到 6(周日为 0)2

%U年内周数,以周日为一周开始40

%VISO 8601 周数41

%W年内周数,以周一为一周开始40

%x首选日期表示10/06/26

%X首选时间表示15:30:00

%y两位年份26

%Y四位年份2026

%z与 UTC 的偏移+0800

%Z时区名或缩写CST

%n/%t换行 / 制表符—

%%字面量百分号%



用一张表就能看出几个容易搞混的点:%m是月、%M是分;%d是日、%D在部分 C 库里是%m/%d/%y的简写(并不是"日"的另一种写法);%U和%W的区别只在于"一周从周日还是周一开始",而%V才是 ISO 8601 周数,跨年附近三者结果可能差一周。


一个实测脚本,用固定时间戳把常见说明符一次性打出来(注意%e、%T、%R、%D等在 Windows 上不受支持,官方手册已明确说明这一点,所以在 Windows 上跑这段会看到原样输出或异常结果):


<?php // 适用于 PHP 7.0 ~ 8.0;PHP 8.1 起本函数已废弃
$ts = mktime(15, 30, 0, 10, 6, 2026); // 2026-10-06 15:30:00

$specs = ['%Y', '%y', '%m', '%d', '%H', '%M', '%S', '%A', '%a', '%B',
'%j', '%u', '%w', '%U', '%V', '%W', '%z', '%Z', '%%'];

foreach ($specs as $s) {
printf("%-4s => %s\n", $s, strftime($s, $ts));
}

三、迁移到date()与IntlDateFormatter


官方给出的迁移路径分成两条,判断标准只有一句话:输出要不要跟随语言环境。


不需要本地化(只用数字、或者英文缩写就够)的,直接换成DateTime::format(),格式字符沿用date()的那一套:




strftime()date()/DateTime::format()说明



%Y-%m-%dY-m-d最常用的日期

%H:%M:%SH:i:s注意分钟从%M变成i

%Y%m%d%H%M%SYmdHis拼文件名、时间戳串

%Al英文星期全称

%aD英文星期缩写

%BF英文月名

%jz加 1date('z')从 0 开始,需注意差一

%pAAM / PM

%zO与 UTC 的偏移

%ZT时区缩写;e是时区标识符



<?php // 适用于 PHP 8.0+
$dt = new DateTimeImmutable('2026-10-06 15:30:00');

echo $dt->format('Y-m-d H:i:s'), PHP_EOL; // 2026-10-06 15:30:00
echo $dt->format('l, j F Y'), PHP_EOL; // Tuesday, 6 October 2026(英文)

需要真正本地化(中文月份名、中文星期、按区域习惯排列的日期)的,用IntlDateFormatter,它由 ICU 驱动,不依赖操作系统的 locale 安装情况,这也是它比strftime()可靠的地方:


<?php // 适用于 PHP 8.0+,需要 intl 扩展
$ts = (new DateTimeImmutable('2026-10-06 15:30:00'))->getTimestamp();
$fmt = new IntlDateFormatter(
'zh_CN', // 区域
IntlDateFormatter::LONG, // 日期样式
IntlDateFormatter::SHORT, // 时间样式
'Asia/Shanghai', // 时区
IntlDateFormatter::GREGORIAN
);

echo $fmt->format($ts), PHP_EOL; // 2026年10月6日 15:30(随 ICU 版本略有差异)

// 也可以用自定义模式串,符号含义与 strftime 不同
$fmt2 = new IntlDateFormatter('zh_CN', IntlDateFormatter::NONE,
IntlDateFormatter::NONE, 'Asia/Shanghai', IntlDateFormatter::GREGORIAN,
'yyyy年MM月dd日 EEEE');
echo $fmt2->format($ts), PHP_EOL;

注意IntlDateFormatter的模式串符号(yyyy、MM、dd、EEEE)用的是 ICU 的语法,和strftime()的%Y、%m完全是两套东西,别混着写。ICU 的输出在不同 ICU 版本间可能有细微差别,所以对格式有严格断言的地方(比如测试用例)要预期到这种差异。


如果你的运行环境实在装不了intl,还有一个折中办法:把中文星期、中文月份做成常量数组,用date('w')、date('n')去查表。这样做的好处是完全可控、不依赖任何扩展和系统 locale;代价是要自己维护,且只有你支持的那几种语言。


常见坑点



  1. ❌ 在 PHP 8.1 及以上的项目里继续调用strftime(),然后被E_DEPRECATED通知刷屏


✅ 迁到date()/DateTime::format()或IntlDateFormatter;strftime()计划在 PHP 9.0 移除。



  1. ❌ 把date()的格式字符直接塞给strftime()


✅ 两套符号完全不同:strftime('%m')是月份,date('m')也是月份,但strftime('%M')是分钟而date('M')却是月份缩写——必须逐个对照。



  1. ❌ 调了setlocale()但不检查返回值,也不设LC_TIME


✅ 只设LC_TIME;检查返回值是否为false,失败时要有降级方案。



  1. ❌ 依赖%e、%T、%R、%D这类说明符做跨平台输出,或者用strftime()处理 1970 年之前的时间


✅ 官方手册说明并非所有说明符都被各平台 C 库支持,%e、%T、%R、%D等在 Windows 上不受支持;负时间戳(1970 年之前)在 Windows 和部分系统上同样不被支持。跨平台一律改用date()体系。



  1. ❌ 用%U或%W当 ISO 周数


✅ ISO 8601 周数是%V;%U(周日起)与%W(周一起)在跨年附近可能与%V相差一周。



  1. ❌ 用strftime()输出"当前时间戳"或做时间运算


✅ 时间运算用DateTime系列做加减(modify()、add()),格式化和运算是两件事。



  1. ❌ 以为strftime()输出的一定是本地语言


✅ 区域设置失败时它会安静地输出默认英文;服务器缺 locale 包(精简 Docker 镜像常见)时会直接命中这个情况。



  1. ❌ 在同一个请求里到处setlocale()却不恢复


✅setlocale()改的是进程级设置,用完恢复原值,否则会把同一请求里其他模块的格式化输出一起改掉。


总结




维度结论



语言支持strftime()已废弃(PHP 8.1 起产生E_DEPRECATED),计划 PHP 9.0 移除

配套函数gmstrftime()同样已废弃,差别只在于按 UTC 解释时间戳

本地化前提必须先setlocale(LC_TIME, ...)并检查返回值

平台差异实现直接来自 C 库,说明符支持情况随平台变化;Windows 缺若干说明符

输出格式百分号说明符,与date()的格式字符完全不是一套

不本地化的替代date()/DateTime::format()

要本地化的替代IntlDateFormatter(需intl扩展,由 ICU 驱动)

最隐蔽的坑区域设置失败时静默输出英文,"没报错但结果不对"



如果你手上是 PHP 8.1 及以上的项目,结论很干脆:不要再用strftime(),也别引第三方兼容包去"续命"——那些包用 ICU 重实现,输出和原来的 C 库版本本来就有差异,不如一次性迁到IntlDateFormatter或date()。如果是 PHP 8.0 及更早的老项目要长期维护,那么至少做到两件事:所有setlocale()都检查返回值,所有平台特定的说明符都不要用——这样等你要迁移的时候,工作量会小得多。





需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询