Why date() works twice as fast if we set time zone from code?

南楼画角 提交于 2019-11-28 08:03:29

Update for PHP 5.4:

As documented in the description of date_default_timezone_get, starting from PHP 5.4.0 the algorithm to guess the timezone from system information has been removed from the code (contrast with the PHP 5.3 source) so this behavior no longer exists.

Running the timing test on my dev server to see it in action, I got:

  • PHP 5.3.11: ~720ms
  • PHP 5.4.3: ~470ms

Original answer:

I 've just looked into PHP source. Specifically, all relevant code is in /ext/date/php_date.c.

I started with the assumption that if you don't provide a timezone for date, date_default_timezone_get is called to get one. Here's that function:

PHP_FUNCTION(date_default_timezone_get)
{
    timelib_tzinfo *default_tz;

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

OK, so what does get_timezone_info look like? This:

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;
}

What about guess_timezone? Here it is:

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";
}

OK, so how does that interact with date_default_timezone_set? Let's look at that function:

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;
}

Long story short: if you call date_default_timezone_set once, then guess_timezone takes the fast path of reading from the timezone variable (the very first conditional is satisfied, and it returns immediately). Otherwise it takes some time to work out the default timezone, which is not cached (I guess for simplicity), and if you do that in a loop the delay starts to show.

I'd imagine it has to determine the timezone for itself each time it's called unless explicitly specified, which adds to the function runtime.

But really, does it matter? How many scripts are you likely to make that call date() 100,000 times per run?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!