【问题标题】:strftime() returns incorrect preferred time formatstrftime() 返回不正确的首选时间格式
【发布时间】:2016-11-05 15:55:09
【问题描述】:

我在 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") 返回正确的语言环境。

没有应用特殊配置。

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

%X 基于区域设置的首选时间表示,不带日期

附:首选日期%x 可以正确显示俄罗斯的 dd.mm.yyyy 和美国的 mm/dd/yyyy

【问题讨论】:

  • 在这里工作:ideone.com/c1fGzZ
  • 在我的服务器上不起作用,完全相同的脚本。我在 FreeBSD 10.1 上有 php70-7.0.8。 locale -a 返回安装的完全相同的语言环境。
  • echo setlocale(LC_ALL, "en_US.UTF-8") 返回正确的语言环境。
  • 这一定是服务器的问题
  • setlocale 不会返回 false(如果检测到任何类型的错误配置或错误)。

标签: php locale freebsd strftime


【解决方案1】:

在 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 返回:

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

所以 en_US.UTF-8 实际上是 en_US.ISO8859-1 的符号链接。

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

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

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

可能的解决方案:

  • fill a bug report 如果你认为它是相关的和/或1 修补上面的文件然后重建世界(我猜)
  • 处理这种特殊情况:

    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_fmttrunktrunk11-STABLE 中被赋值为%I:%M:%S %p

更新:

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

【讨论】:

    【解决方案2】:

    摘自 ubuntu 12.04 上的 /usr/share/i18n/locales/en_US

    % 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>"
    

    请注意上午/下午日期中的%I %r。

    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,只有在您提供错误变量(即LCAL 而不是LCALL)时才会返回false,但会返回系统在其他情况下返回的内容:

    注意:setlocale() 的返回值取决于 PHP 使用的系统 在跑。它返回的正是系统 setlocale 函数 返回。

    在“linuxes”上setlocale 返回NULL,这并不总是被视为错误(请参阅here),这可能是您问题的根源,我不能发誓,因为我没有运行 FreeBSD 来确认这点。

    【讨论】:

      猜你喜欢
      • 2019-01-25
      • 1970-01-01
      • 2014-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-13
      • 1970-01-01
      • 2017-11-28
      相关资源
      最近更新 更多