strftime() returns 不正确的首选时间格式

strftime() returns incorrect preferred time format

我在 FreeBSD 10.1 和 nginx 上安装了 PHP 7.0.8 (FPM) 运行。我需要按用户所在国家/地区的首选格式显示时间。

setlocale(LC_ALL, "ru_RU.UTF-8");
date_default_timezone_set("Europe/Moscow");
echo strftime('%X', time());
// Returns 21:23:12 (correct) because 24-hr format is preferred in Russia.

setlocale(LC_ALL, "en_US.UTF-8");
date_default_timezone_set("America/New_York");
echo strftime('%X', time());
// Returns 21:23:12 (incorrect) must return 9:23:12 pm as preferred format in U.S.

这看起来像是我的服务器或 PHP 版本的问题,因为 其他用户得到正确的结果。

locale -a return 包含 ru_RU.UTF-8en_US.UTF-8

echo setlocale(LC_ALL, "en_US.UTF-8") returns 正确的语言环境。

没有应用任何特殊配置。

请帮我解决这个问题。谢谢。

%X Preferred time representation based on locale, without the date

P.S。首选日期 %x 可以正确显示俄罗斯的 dd.mm.yyyy 和 U.S 的 mm/dd/yyyy。

摘自 /usr/share/i18n/locales/en_US ubuntu 12.04:

% Appropriate time representation (%X)
%       "%r"
t_fmt   "<U0025><U0072>"
%
% Appropriate AM/PM time representation (%r)
%       "%I:%M:%S %p"
t_fmt_ampm "<U0025><U0049><U003A><U0025><U004D><U003A><U0025><U0053><U0020>/
<U0025><U0070>"

请注意 %r 的 AM/PM 日期中的 %I

Unicode 中的 %X 是 25 70 即 %P.

来自 man date(为简单起见,已删除):

%H     hour (00..23)

%I     hour (01..12)

%p     locale's equivalent of either AM or PM; blank if not known

%P     like %p, but lower case

%r     locale's 12-hour clock time (e.g., 11:11:04 PM)

%R     24-hour hour and minute; same as %H:%M

%x     locale's date representation (e.g., 12/31/99)

%X     locale's time representation (e.g., 23:13:48)

阅读所有这些文件,它应该按预期工作,所以我怀疑您的设置使用不同的语言环境文件或回退到 C 语言环境(例如,如果语言环境文件不存在)

现在对于 setlocale,它会 return false 只有在你给出一个错误的变量(即 LCAL 而不是 LCALL)的情况下才会 return ] 什么系统 returns 对于其他情况:

Note: The return value of setlocale() depends on the system that PHP is running. It returns exactly what the system setlocale function returns.

On "linuxes" setlocale returns NULL 这并不总是被视为错误(参见 here),这可能是您问题的根源,我不能发誓,因为我没有 FreeBSD 运行 确认这一点。

在 FreeBSD 10.3 上相同:

php -r 'date_default_timezone_set("Europe/Paris"); var_dump(setlocale(LC_ALL, "en_US.UTF-8"), strftime("%X", time()));'
string(11) "en_US.UTF-8"
string(8) "15:03:07"

首先,ls -l /usr/share/locale/en_US.UTF-8/LC_TIME returns:

/usr/share/locale/en_US.UTF-8/LC_TIME@ -> ../en_US.ISO8859-1/LC_TIME

所以 en_US.UTF-8 实际上是 link 到 en_US.ISO8859-1.

的符号link

然后,如果我们查看 /usr/src/share/timedef/en_US.ISO8859-1.src(您需要安装源),我们会发现:

#
# X_fmt
#
%H:%M:%S

这解释了您期望 %I:%M:%S %p(或 %r)的实际结果。

可能的解决方案:

  • fill a bug report 如果您认为相关 and/or1 修补上面的文件然后重建世界(我猜)
  • 处理这个具体案例:

    echo strftime(0 === strpos(setlocale(LC_ALL, '0'), 'en_US') ? '%r' : '%X');
    
  • 更喜欢使用 IntlDateFormatter,它不依赖于系统语言环境(由 ICU 库假设)。例如:

    $timefmt = new IntlDateFormatter('en_US', IntlDateFormatter::NONE, IntlDateFormatter::MEDIUM);
    $timefmt->format(date_create());
    

1 似乎 X_fmttrunk and 11-STABLE[= 中的价值为 %I:%M:%S %p 27=]

更新:

  • 更改 X_fmt 的提交是 reverted 因为(即在 FreeBSD >= 11 上,X_fmt 仍然定义为 %H:%M:%S
  • 在 FreeBSD 11 上,定义时间格式的文件是 /usr/src/share/timedef/en_US.UTF-8.src(符号 link 到 en_US.ISO8859-1 语言环境已消失)