为什么 date() 如果我们从代码中设置时区,其工作速度会快一倍?

2022-08-30 14:18:45

您是否注意到,如果您在任何调用之前在脚本中设置实际时区,则函数的工作方式比平时快2倍?我对此非常好奇。date()date()

看看这段简单的代码:

<?php

  $start = microtime(true);
  for ($i = 0; $i < 100000; $i++) date('Y-m-d H:i:s');
  echo (microtime(true) - $start);

?>

它只是使用循环调用函数100,000次。我得到的结果总是在1.6秒左右(Windows,PHP 5.3.5),但是......date()for

如果我再次设置相同的时区,在开始之前添加一个荒谬的行:

date_default_timezone_set(date_default_timezone_get());

我得到的时间低于800ms;速度提高约 2 倍(同一服务器)。

我四处寻找这种行为的任何合理解释,但没有成功。从我的角度来看,这行额外的行是无用的,但PHP不同意我的观点。

我已经在两个linux服务器(不同的PHP版本)上尝试了这个测试,并得到了不同的结果时间,但比例约为6:1

注意:php.ini中的 date.timezone 属性已正确设置(欧洲/巴黎)。

我在这里搜索相关问题,没有找到类似的东西。我还检查了手册中的date_default_time_zone()函数@php.net,发现我不仅是一个注意到这一点的人,但仍然不明白为什么会发生这种情况?

任何人?


答案 1

PHP 5.4 的更新:

date_default_timezone_get 的描述中所述,从 PHP 5.4.0 开始,从代码中猜测系统信息时区的算法已从代码中删除(与 PHP 5.3 源代码相反),因此此行为不再存在。

在我的开发服务器上运行计时测试以查看其运行情况,我得到了:

  • PHP 5.3.11: ~720 毫秒
  • PHP 5.4.3: ~470 毫秒

原答:

我刚刚研究了PHP源代码。具体来说,所有相关代码都在 /ext/date/php_date.c 中。

我首先假设,如果你不为 提供时区,则调用它来获取一个时区。这是该函数datedate_default_timezone_get

PHP_FUNCTION(date_default_timezone_get)
{
    timelib_tzinfo *default_tz;

    default_tz = get_timezone_info(TSRMLS_C);
    RETVAL_STRING(default_tz->name, 1);
}

好吧,那看起来是什么样子的呢?get_timezone_info

PHPAPI timelib_tzinfo *get_timezone_info(TSRMLS_D)
{
    char *tz;
    timelib_tzinfo *tzi;

    tz = guess_timezone(DATE_TIMEZONEDB TSRMLS_CC);
    tzi = php_date_parse_tzfile(tz, DATE_TIMEZONEDB TSRMLS_CC);
    if (! tzi) {
        php_error_docref(NULL TSRMLS_CC, E_ERROR, "Timezone database is corrupt - this should *never* happen!");
    }
    return tzi;
}

怎么样?这里是:guess_timezone

static char* guess_timezone(const timelib_tzdb *tzdb TSRMLS_DC)
{
    char *env;

    /* Checking configure timezone */
    if (DATEG(timezone) && (strlen(DATEG(timezone)) > 0)) {
        return DATEG(timezone);
    }
    /* Check environment variable */
    env = getenv("TZ");
    if (env && *env && timelib_timezone_id_is_valid(env, tzdb)) {
        return env;
    }
    /* Check config setting for default timezone */
    /*  ..... code omitted ....... */
#if HAVE_TM_ZONE
    /* Try to guess timezone from system information */
    /*  ..... code omitted ....... */
#endif
#ifdef PHP_WIN32
    /*  ..... code omitted ....... */
#elif defined(NETWARE)
    /*  ..... code omitted ....... */
#endif
    /* Fallback to UTC */
    php_error_docref(NULL TSRMLS_CC, E_WARNING, DATE_TZ_ERRMSG "We had to select 'UTC' because your platform doesn't provide functionality for the guessing algorithm");
    return "UTC";
}

好吧,那么它与date_default_timezone_set是如何相互作用的呢让我们看一下该函数

PHP_FUNCTION(date_default_timezone_set)
{
    char *zone;
    int   zone_len;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &zone, &zone_len) == FAILURE) {
        RETURN_FALSE;
    }
    if (!timelib_timezone_id_is_valid(zone, DATE_TIMEZONEDB)) {
        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Timezone ID '%s' is invalid", zone);
        RETURN_FALSE;
    }
    if (DATEG(timezone)) {
        efree(DATEG(timezone));
        DATEG(timezone) = NULL;
    }
    DATEG(timezone) = estrndup(zone, zone_len);
    RETURN_TRUE;
}

长话短说:如果你调用一次,然后从变量中读取快速路径(第一个条件得到满足,它立即返回)。否则,需要一些时间来计算出默认时区,该时区不会被缓存(为了简单起见,我猜),如果您在循环中执行此操作,则延迟开始显示。date_default_timezone_setguess_timezonetimezone


答案 2

我想每次调用它时都必须确定自己的时区,除非显式指定,这会增加函数运行时。

但实际上,这重要吗?每次运行可能会使该调用 date() 达到 100,000 次的脚本?


推荐