PHP 时区数据库损坏错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8751221/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 05:25:26  来源:igfitidea点击:

PHP Timezone database is corrupt error

phpwordpressubuntutimezone

提问by Timnit Gebru

I have a wordpress website which suddenly stopped working today. When I look at the logs I see and error:

我有一个 wordpress 网站,今天突然停止工作。当我查看日志时,我看到并出错:

[error] [client 50.78.108.177] PHP Fatal error: strtotime(): Timezone database is corrupt - this should neverhappen!

[错误] [客户50.78.108.177] PHP致命错误:的strtotime():时区数据库已损坏-这应该永远不会发生!

After reading up on google one person said that they discovered a permissions problem in /usr/share/zoneinfo. I tried changing the permissions to 777, 775, 770 and I still keep on getting the same error. I am running php PHP 5.3.2 on Ubuntu 10.04.3 LTS. Any suggestions or recommendations would be helpful.If all else fails I'm going to try downgrading to an earlier version of php but I wanted to try other things before doing that.

在谷歌上阅读后,有人说他们在/usr/share/zoneinfo 中发现了权限问题。我尝试将权限更改为 777、775、770,但仍然出现相同的错误。我在 Ubuntu 10.04.3 LTS 上运行 php PHP 5.3.2。任何建议或建议都会有所帮助。如果一切都失败了,我将尝试降级到早期版本的 php,但我想在这样做之前尝试其他事情。

thanks, Timnit

谢谢,蒂姆尼特

Update
just in case it helps: the error points to strtotimein the function below

更新
以防万一它有帮助:错误指向strtotime下面的函数

function mysql2date( $dateformatstring, $mysqlstring, $translate = true ) {
    $m = $mysqlstring;
    if ( empty( $m ) )
            return false;

    if ( 'G' == $dateformatstring )
            return strtotime( $m . ' +0000' );

    $i = strtotime( $m );

    if ( 'U' == $dateformatstring )
            return $i;

    if ( $translate )
            return date_i18n( $dateformatstring, $i );
    else
            return date( $dateformatstring, $i );
}

Update#2:
for now I have fixed the problem by simply having the function above return false;without performing anything. However I still haven't figured out the root cause of the problem.

更新#2:
现在我通过简单地使用上面的函数return false;而不执行任何操作来解决这个问题。但是我仍然没有弄清楚问题的根本原因。

update#3:

更新#3:

var_dump($dateformatstring)

string(5) "d.m.y" string(1) "m" string(5) "d.m.y" string(1) "m" string(5) "d.m.y" string(1) "m"

string(5) "dmy" string(1) "m" string(5) "dmy" string(1) "m" string(5) "dmy" string(1) "m"

var_dump($mysqlstring)

string(19) "2011-10-20 05:35:01" string(19) "2011-10-20 05:35:01" string(19) "2011-10-20 05:25:22" string(19) "2011-10-20 05:25:22" string(19) "2011-10-19 05:10:06" string(19) "2011-10-19 05:10:06"

字符串(19) "2011-10-20 05:35:01" 字符串(19) "2011-10-20 05:35:01" 字符串(19) "2011-10-20 05:25:22" 字符串( 19) "2011-10-20 05:25:22" 字符串(19) "2011-10-19 05:10:06" 字符串(19) "2011-10-19 05:10:06"

update#4:
there is another code snippet that is generating the error log below:

更新#4
还有另一个代码片段正在生成下面的错误日志:

PHP Fatal error: date(): Timezone database is corrupt - this should neverhappen! in /srv/www/motionthink.com/public_html/wp-admin/includes/class-wp-filesystem-direct.php on line 346,referer: wp_root_directory/wp-admin/plugins.php?plugin_status=upgrade

PHP 致命错误:date():时区数据库已损坏 - 这绝不应该 发生!在第 346 行的/srv/www/motionthink.com/public_html/wp-admin/includes/class-wp-filesystem-direct.php 中引用者:wp_root_directory/wp-admin/plugins.php?plugin_status=upgrade

309         function dirlist($path, $include_hidden = true, $recursive = false) {
  310                 if ( $this->is_file($path) ) {
  311                         $limit_file = basename($path);
  312                         $path = dirname($path);
  313                 } else {
  314                         $limit_file = false;
  315                 }
  316 
  317                 if ( ! $this->is_dir($path) )
  318                         return false;
  319 
  320                 $dir = @dir($path);
  321                 if ( ! $dir )
  322                         return false;
  323 
  324                 $ret = array();
  325 
  326                 while (false !== ($entry = $dir->read()) ) {
  327                         $struc = array();
  328                         $struc['name'] = $entry;
  329 
  330                         if ( '.' == $struc['name'] || '..' == $struc['name'] )
  331                                 continue;
  332 
  333                         if ( ! $include_hidden && '.' == $struc['name'][0] )
  334                                 continue;
  335 
  336                         if ( $limit_file && $struc['name'] != $limit_file)
  337                                 continue;
  338 
  339                         $struc['perms']         = $this->gethchmod($path.'/'.$entry);
  340                         $struc['permsn']  = $this->getnumchmodfromh($struc['perms']);
  341                         $struc['number']        = false;
  342                         $struc['owner']         = $this->owner($path.'/'.$entry);
  343                         $struc['group']         = $this->group($path.'/'.$entry);
  344                         $struc['size']          = $this->size($path.'/'.$entry);
  345                         $struc['lastmodunix']= $this->mtime($path.'/'.$entry);
  346                         $struc['lastmod']   = date('M j',$struc['lastmodunix']);
  347                         $struc['time']          = date('h:i:s',$struc['lastmodunix']);
  348                  $struc['type']          = $this->is_dir($path.'/'.$entry) ?   'd:'f';
  349 

Update#5:
doing a php -i | fgrep -i datereturns

更新#5:
php -i | fgrep -i date退货

Build Date => Dec 13 2011 18:43:02

date
date/time support => enabled
date.default_latitude => 31.7667 => 31.7667
date.default_longitude => 35.2333 => 35.2333
date.sunrise_zenith => 90.583333 => 90.583333
date.sunset_zenith => 90.583333 => 90.583333
date.timezone => no value => no value

构建日期 => 2011 年 12 月 13 日 18:43:02

date
date/time support => enabled
date.default_latitude => 31.7667 => 31.7667
date.default_longitude => 35.2333 => 35.2333
date.sunrise_zenith => 90.583333 => 90.583333
date.sunset_zenith => 90.583333 => 90.583333
date.timezone => no value => no value

then I edited the php.ini file to set the timezone to "America/Los Angeles" and got this output

然后我编辑了 php.ini 文件,将时区设置为“美国/洛杉矶”并得到了这个输出

date/time support => enabled
date.default_latitude => 31.7667 => 31.7667
date.default_longitude => 35.2333 => 35.2333
date.sunrise_zenith => 90.583333 => 90.583333
date.sunset_zenith => 90.583333 => 90.583333
date.timezone => America/Los_Angeles => America/Los_Angeles

I then restarted apache2. I still get the error

然后我重新启动了apache2。我仍然收到错误

采纳答案by Timnit Gebru

The problem was file permissions. I gave the apache2 user read & execute access to usr/share/zoneinfo and etc/localtime. Before, I hadn't set the parents of local time to the right permissions as well. i.e. I only changed the permissions of localtime and zoneinfo without changing the permissions of their parent directories. So stupid! Stepping away from a problem and getting back to it is always useful.

问题是文件权限。我给了 apache2 用户对 usr/share/zoneinfo 和 etc/localtime 的读取和执行访问权限。之前,我也没有将本地时间的父级设置为正确的权限。即我只更改了 localtime 和 zoneinfo 的权限,而没有更改其父目录的权限。那么蠢!远离问题并回到它总是有用的。

回答by CoderChris

This issue can also occur when using php-fpm in chroot mode, the solution in this case to be to create something like /usr/share/zoneinfo/Europe in your chroot dir then copy your TZ file in to it e.g. London

在 chroot 模式下使用 php-fpm 时也会出现此问题,在这种情况下的解决方案是在 chroot 目录中创建类似 /usr/share/zoneinfo/Europe 的内容,然后将您的 TZ 文件复制到其中,例如 London

回答by LSerni

Root cause: one of the zoneinfo files could not be opened.

根本原因:无法打开 zoneinfo 文件之一。

also caused by: too many open files.

还有一个原因:打开的文件太多。

I had the same problem today on Ubuntu 14.04.01-LTS "Trusty Tahr", and tried the other answers with no benefit. Permissions were OK, the files were there, the content was as expected.

我今天在 Ubuntu 14.04.01-LTS“Trusty Tahr”上遇到了同样的问题,并尝试了其他答案但没有任何好处。权限正常,文件在那里,内容符合预期。

At last I resolved to run the script from within a command line harness, so that I could try with strace. And this was the result:

最后我决定从命令行工具中运行脚本,以便我可以尝试使用strace. 这就是结果:

openat(AT_FDCWD, "/usr/share/zoneinfo/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = -1 EMFILE (Too many open files)
open("/usr/share/zoneinfo/zone.tab", O_RDONLY) = -1 EMFILE (Too many open files)
stat("/usr/share/zoneinfo/Europe/Rome", {st_mode=S_IFREG|0644, st_size=2652, ...}) = 0
open("/usr/share/zoneinfo/Europe/Rome", O_RDONLY) = -1 EMFILE (Too many open files)
write(1, "\nFatal error: Unknown: Timezone "..., 104) = 104

What is happening

怎么了

When PHP "accesses the zoneinfo database" it actually tries to open a directory and some files. If some of these operations fail, the "zoneinfo corrupt" message appears, but it simply means that the PHP process could not open those files:

当 PHP “访问 zoneinfo 数据库”时,它实际上会尝试打开一个目录和一些文件。如果其中某些操作失败,则会出现“zoneinfo 损坏”消息,但这仅表示 PHP 进程无法打开这些文件:

  • they were not there (chroot jail, zoneinfo install error)
  • they were not there, nor should they be: "Europe/Roem" is not a valid timezone but a typo.
  • they were there, but with wrong permissions.
  • they were there, but the process isn't authorized (SELinux, AppArmor, ...)
  • they were there, but the fopenoperation is temporarily not working
  • 他们不在那里(chroot jail,zoneinfo 安装错误)
  • 他们不在那里,也不应该在那里:“欧洲/罗马”不是一个有效的时区,而是一个错字。
  • 他们在那里,但权限错误。
  • 他们在那里,但该过程未经授权(SELinux、AppArmor 等)
  • 他们在那里,但fopen操作暂时不起作用

My case was the last one: the realproblem was that the script was opening too many temporary files, and leaving them open while running. There is a limit on how many files can be opened at the same time, and the zoneinfo file was the proverbial last straw. A quick fix temporarily resolved the problem while I bounced the "too many files" problem to the developer responsible.

我的情况是最后一个:真正的问题是脚本打开了太多临时文件,并在运行时让它们保持打开状态。可以同时打开多少个文件是有限制的,众所周知,zoneinfo 文件是最后一根稻草。当我将“文件太多”问题退回给负责的开发人员时,快速修复暂时解决了该问题。

Actually I suspect that this alsopoints to PHP continuously opening and closing the zoneinfo database instead of cachingit, but it's an investigation for another day.

实际上我怀疑这指向 PHP 不断打开和关闭 zoneinfo 数据库而不是缓存它,但这是另一天的调查。

Intermittent errorThe "number of open files" thingy is per process, not per PHP script. So there are two (at least) scenarios which could lead to a hard-to-diagnose, possibly intermittent/nonreproducible error:

间歇性错误“打开文件的数量”是每个进程,而不是每个 PHP 脚本。因此,有两种(至少)情况可能导致难以诊断的、可能是间歇性/不可重现的错误:

  • a slow resource leak by some long-running process, e.g. under Racket.
  • a resource hogging by another script or subroutine running in the same process, and possibly not even related to PHP at all.
  • 某些长时间运行的进程导致缓慢的资源泄漏,例如在 Racket 下。
  • 在同一进程中运行的另一个脚本或子程序占用的资源,甚至可能与 PHP 根本无关

A PHP script that, right or wrong, allocates 800 files could work okay until it meets another subprocess that has allocated 224 files. The limit of 1024 open files per process is reached and in that casethe process fails with a mysterious error (which only refers, cryptically at that, to the very last symptom in a long chain of concurrent causes).

一个分配了 800 个文件的 PHP 脚本可以正常工作,直到它遇到另一个分配了 224 个文件的子进程。达到了每个进程 1024 个打开文件的限制,在这种情况下,进程失败并出现一个神秘的错误(这只是指一长串并发原因中的最后一个症状)。

Apache: too many web sites.

Apache:网站太多。

Apache running with mod_php5will cause files accessed by PHP to be opened by the Apache process. But the Apache process also keeps its log filesopen, and every process has a handle to every log file.

Apache 运行mod_php5将导致 PHP 访问的文件被 Apache 进程打开。但是 Apache 进程也保持其日志文件打开,并且每个进程都有每个日志文件的句柄。

So if you have 200 web sites, each with an independent access_log, say /var/www/somesite/logs/access_log, each process will start with some 210 handles already taken for housekeeping, leaving some 800 free for PHP to use.

因此,如果您有 200 个网站,每个网站都有一个独立的 access_log,比如说/var/www/somesite/logs/access_log,每个进程将开始使用大约 210 个已经用于内务处理的句柄,剩下大约 800 个可供 PHP 使用。

This can lead to a situation where the development server (with one site) works, and the production server (with 200 sites installed) does not, if the script needs to allocate 900 temporary files at once.

如果脚本需要一次分配 900 个临时文件,这可能会导致开发服务器(一个站点)工作,而生产服务器(安装了 200 个站点)不工作。

Dirty diagnostics (on Unix/Linux): glob/proc/self/fdand count()the result. Ugly as sin, but it gives a ballpark figure on how many file descriptors are actually open.

脏诊断(在 Unix/Linux 上)glob/proc/self/fdcount()结果。像罪一样丑陋,但它给出了实际打开了多少文件描述符的大致数字。

Quick and dirty fix (on Unix/Linux): increase the fdlimit on per-process open files, bringing it to 1024 (of course you need to be root). It's more a matter for Server Fault.

快速和肮脏的修复(在 Unix/Linux 上):增加每个进程打开文件的 fdlimit,使其达到 1024(当然你需要是 root)。这更像是Server Fault 的问题

回答by Mike Purcell

You mention 'downgrading', did you recently upgrade? In PHP 5.3.x you are forced to set a valid value for date.timezonein your php.ini file.

你提到“降级”,你最近升级了吗?在 PHP 5.3.x 中,您被迫date.timezone在 php.ini 文件中设置有效值。

If you didn't recently upgrade you try resolving the issue by re-installing the tzdatapackage. I work exclusively with CentOS, so I am not sure what the name of Ubuntu's package manager is, but I'm pretty sure tzdatais standard across distros.

如果您最近没有升级,请尝试通过重新安装tzdata软件包来解决问题。我专门使用 CentOS,所以我不确定 Ubuntu 的包管理器的名称是什么,但我很确定它tzdata是跨发行版的标准。

$ -> yum reinstall tzdata # switch 'yum' for Ubuntu package manager
$ -> rm -f /etc/localtime
$ -> ln -sf /usr/share/zoneinfo/UTC /etc/localtime # 'UTC' can be replaced with what you prefer
$ -> date # check to see that it stuck

You may want to restart your httpd after this to ensure timezone info is picked up.

您可能需要在此之后重新启动 httpd 以确保获取时区信息。

-- Edit

- 编辑

Looks like the culprit is your date_i18n() function, which is always being called, unless calling code specifically passes a 3rd arg of 'false'. I ran your code through some test data with $translate set to false, and worked fine.

看起来罪魁祸首是你的 date_i18n() 函数,它总是被调用,除非调用代码专门传递了“false”的第三个参数。我通过将 $translate 设置为 false 的一些测试数据运行了您的代码,并且运行良好。

function mysql2date( $dateformatstring, $mysqlstring, $translate = true ) {

    $translate = false;
    ...
    if ( $translate )
        return 'date_i18n would have been called';
        //return date_i18n( $dateformatstring, $i );
    ...
}

$testPatterns = array(
    array(
        'dateformatstring'  => 'd.m.y',
        'mysqlstring'       => '2011-10-20 05:35:01'
    ),
    array(
        'dateformatstring'  => 'm',
        'mysqlstring'       => '2011-10-20 05:35:01'
    ),
    array(
        'dateformatstring'  => 'd.m.y',
        'mysqlstring'       => '2011-10-20 05:25:22'
    )
);

foreach ($testPatterns as $testPattern) {

    // Not passing arg to over-ride $translate, forces call to date_i18n()
    var_dump(mysql2date($testPattern['dateformatstring'], $testPattern['mysqlstring']));

    // Forcing $translate to false, makes date() call which works fine
    var_dump(mysql2date($testPattern['dateformatstring'], $testPattern['mysqlstring'], false));
}

回答by fatih

may be this can help you PHP – Set Timezone

也许这可以帮助你PHP – 设置时区

回答by Adnan

I change localtime file for my GMT setting like mv /usr/share/zoneinfo/Asia/Karachi localtime Then followinf error occurs.

我更改了我的 GMT 设置的本地时间文件,例如 mv /usr/share/zoneinfo/Asia/Karachi localtime 然后出现 followinf 错误。

date_default_timezone_get(): Timezone database is corrupt - this should neverhappen!

date_default_timezone_get():时区数据库已损坏-这应该永远不会发生!

Solution : Revert you local time setting. Tips : Always keep a backup copy of your old local time so that you can recover any expected OS / Software issue

解决方案:恢复本地时间设置。提示:始终保留旧本地时间的备份副本,以便您可以恢复任何预期的操作系统/软件问题