wordpress中的时区问题

欢迎转载!转载时请注明出处:http://blog.csdn.net/nfer_zhuang/article/details/51285483

引言

我在一个项目中需要设置截至时间,但是发现过了截至时间后仍然有效,即如下的代码判断没有表现正常:

if ( strtotime("2016-04-15 23:00:00") > time() ) {
    // available, do something
}
else {
    // unavailable, stop do something
}

即,当过了设定的2016-04-15 23:00:00之后,仍然是可以操作的,因此当时为了暴力解决,直接在过了23点之后,加了一个false的逻辑与运算:

if ( strtotime("2016-04-15 23:00:00") > time() && false) {
    // available, do something
}

当然,在我写这篇总结的时候,就算去掉false的逻辑与运算,程序的运行也是符合预期的。现在就是分析和总结一下wordpress的时区问题。

php环境和shell环境的测试

测试代码如下:

<?php
echo "before set timezone PRC, get timezone:".date_default_timezone_get()."\n";
test();
date_default_timezone_set('PRC');
echo "\nafter set timezone PRC, get timezone:".date_default_timezone_get()."\n";
test();

function test() {
    echo " ".strtotime('2016-04-15 23:00:00')." strtotime('2016-04-15 23:00:00'):\n";
    echo " ".time()." time():\n";
    echo " ".exec('date +%s')." exec('date +%s'):\n";
}

运行结果如下:

before set timezone PRC, get timezone:Asia/Shanghai
 1460732400 strtotime('2016-04-15 23:00:00')
 1460807831 time()
 1460807831 exec('date +%s')

after set timezone PRC, get timezone:PRC
 1460732400 strtotime('2016-04-15 23:00:00')
 1460807831 time()
 1460807831 exec('date +%s')

而shell下的date命令运行结果如下:

# date 
Sat Apr 16 20:14:03 CST 2016

首先解释几个专有名词:

  • CST:百度百科显示CST可以表示四个时区的缩写:
    • 美国中部时间:Central Standard Time (USA) UT-6:00
    • 澳大利亚中部时间:Central Standard Time (Australia) UT+9:30
    • 中国标准时间:China Standard Time UT+8:00
    • 古巴标准时间:Cuba Standard Time UT-4:00

而在这里,我们可以使用借助date命令的另外一个参数来明确一下具体是哪个时区:

# date +%:z
+08:00

这样,我们确认了时区设置是对的,是中国的+08:00时区。

  • Asia/Shanghai:根据wikipedia的页面,这个属于国际上通用的北京时间
  • PRC:People’s Republic of China的缩写,而php的官方说明中是不建议使用PRC作为时区参数的,具体见页面

因此,我们上面的代码实际上是需要更正的:

扫描二维码关注公众号,回复: 3824983 查看本文章
<?php
echo "before set timezone Asia/Shanghai, get timezone:".date_default_timezone_get()."\n";
test();
date_default_timezone_set('Asia/Shanghai');
echo "\nafter set timezone Asia/Shanghai, get timezone:".date_default_timezone_get()."\n";
test();

function test() {
    echo " ".strtotime('2016-04-15 23:00:00')." strtotime('2016-04-15 23:00:00'):\n";
    echo " ".time()." time():\n";
    echo " ".exec('date +%s')." exec('date +%s'):\n";
}

而,设置时区前后的值都是”Asia/Shanghai”,因此获取的时间戳也是正常的。

wordpress环境测试

测试代码如下:

<?php
require("/home/nfer/git/WordPress/WordPress/wp-load.php");

echo "before set timezone Asia/Shanghai, get timezone:".date_default_timezone_get()."\n";
test();
date_default_timezone_set('Asia/Shanghai');
echo "\nafter set timezone Asia/Shanghai, get timezone:".date_default_timezone_get()."\n";
test();

function test() {
    echo " ".strtotime('2016-04-15 23:00:00')." strtotime('2016-04-15 23:00:00'):\n";
    echo " ".time()." time():\n";
    echo " ".exec('date +%s')." exec('date +%s'):\n";
}

运行结果如下:

before set timezone Asia/Shanghai, get timezone:UTC
 1460761200 strtotime('2016-04-15 23:00:00'):
 1460811693 time():
 1460811693 exec('date +%s')

after set timezone Asia/Shanghai, get timezone:Asia/Shanghai
 1460732400 strtotime('2016-04-15 23:00:00'):
 1460811693 time():
 1460811693 exec('date +%s'):

从运行结果上可以看到的是,在引入了wordpress相关文件后,获取的时区值竟然是UTC,所以对应的通过strtotime()解析出的时间戳也和北京时间差8个小时。

wordpress时区设置

回到wordpress的设置页面,我们发现实际上是已经设置了+8:00时区的,具体见图:
这里写图片描述
那么,依然已经设置了正确的时区,为什么还会出现时区异常呢?

wordpress文件引入

查看上面的测试代码,我们引入的是wp-load.php文件,打开这个文件,实际上是做了以下两个事情:

/** Define ABSPATH as this file's directory */
define( 'ABSPATH', dirname("/home/nfer/git/WordPress/WordPress/wp-load.php") . '/' );

/** The config file resides in ABSPATH */
require_once( ABSPATH . 'wp-config.php' );

因此,我们需要进一步查看wp-config.php文件,除了和数据库设置相关的,关键的是又引入了wp-settings.php文件。当我们打开wp-settings.php文件时,关键问题出现了,在第42行有一下代码:

// WordPress calculates offsets from UTC.
date_default_timezone_set( 'UTC' );

经过,测试,去除这一行代码,测试程序运行正常,符合预期,看来问题就出在这里了。

官方说明

https://wordpress.org/support/topic/why-does-wordpress-set-timezone-to-utc

This is mostly a holdover from when WordPress supported PHP 4.

PHP 4 didn’t have a way to set timezones like that, and so do timezone related calculations. In order to work around this, WordPress implemented its own time calculation code.

Originally, the timezone setting was limited to selecting +1 hours, +2 hours, etc. It was annoying and you had to change it twice a year for daylight savings time.

So 4 years ago, around WordPress 2.8 or so, I wrote the patch that added the new method of choosing timezone. If PHP 5 was on the server, it would get the timezone information from PHP and then apply the calculations automatically to handle daylight savings. This is where the timezone_string came from.

However, because PHP 4 was still being supported, the old code for calculating timezone was retained, and to make all WordPress instances behave identically, the default timezone was set to UTC. My first patch actually did set the date_default correctly, but this caused strange and inconsistent behavior because much of the core assumed UTC for date() calls and the like.

This may be fixed in the future, but it’s a big job. Lots of code to deal with and adjust to the new way of doing things. It’s not a priority though, because it works for now. However, it is a bit of a minor performance hit (see http://core.trac.wordpress.org/ticket/23132), so that might affect things.

Note that you should not call date_default_timezone_set to change the timezone, because much of the core still assumes UTC when doing calculations. This can affect things like scheduling of posts and cause other strange behavior.

Instead, you should use the functions in WordPress to retrieve times, such as current_time, for example. Alternatively, if you do change the date default, make sure to change it back to what it was before after you’re done.

看来,官方说明中还不建议调用date_default_timezone_set函数来修改时区,如果要获取时间,可以调用wordpress内建函数,比如current_time()

但是,我们这里是需要解析一个时间字符串,这种情况下,wordpress没有提供响应的内建函数,怎么办呢?

修正的方法

同样是查看wordpress内建函数的实现,比如mysql2date()的源码如下:

function mysql2date( $format, $date, $translate = true ) {
    if ( empty( $date ) )
        return false;

    if ( 'G' == $format )
        return strtotime( $date . ' +0000' );

    $i = strtotime( $date );

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

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

注意,这里如果if ( 'G' == $format )条件满足的话,它主动对$date添加了时区部分,那么是不是我们也需要指定具体的时区呢?
修改后的代码如下:

<?php
require("/home/nfer/git/WordPress/WordPress/wp-load.php");

echo "before set timezone Asia/Shanghai, get timezone:".date_default_timezone_get()."\n";
test();
date_default_timezone_set('Asia/Shanghai');
echo "\nafter set timezone Asia/Shanghai, get timezone:".date_default_timezone_get()."\n";
test();

function test() {
    echo " ".strtotime('2016-04-15 23:00:00 +0800')." strtotime('2016-04-15 23:00:00 +0800')\n";
    echo " ".time()." time()\n";
    echo " ".exec('date +%s')." exec('date +%s')\n";
}

注意,在日期字符串中,我们主动加入了北京时区" +0800",那么这次的运行结果呢?

before set timezone Asia/Shanghai, get timezone:UTC
 1460732400 strtotime('2016-04-15 23:00:00 +0800')
 1460821607 time()
 1460821607 exec('date +%s')

after set timezone Asia/Shanghai, get timezone:Asia/Shanghai
 1460732400 strtotime('2016-04-15 23:00:00 +0800')
 1460821607 time()
 1460821607 exec('date +%s')

请注意,这次两次通过strtotime()解析的字符串都是一样的了。Bingo!

总结

这次的问题实际上是WordPress兼容老版本的问题,属于WordPress开发上的一个坑。

猜你喜欢

转载自blog.csdn.net/nfer_cn/article/details/51285483