Почему date() работает в два раза быстрее, если мы устанавливаем часовой пояс из кода?

Вы заметили, что функция date() работает в 2 раза быстрее, чем обычно, если вы устанавливаете фактический часовой пояс внутри своего скрипта перед любым вызовом date()? Мне очень любопытно об этом.

Посмотрите на этот простой фрагмент кода:

<?php

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

?>

Он просто вызывает функцию date(), используя цикл for 100 000 раз. Результат, который я получаю, всегда составляет около 1,6 секунды (Windows, PHP 5.3.5), но…

Если я снова установлю тот же часовой пояс, добавив одну абсурдную строку перед запуском:

date_default_timezone_set(date_default_timezone_get());

Я получаю время ниже 800 мс; ~ в 2 раза быстрее (тот же сервер).

Я искал вокруг, чтобы найти какое-либо разумное объяснение такому поведению, но не имел никакого успеха. С моей точки зрения, эта дополнительная строка бесполезна, но PHP со мной не согласен.

Я пробовал этот тест на двух Linux-серверах (разные версии PHP) и получил разное время, но в пропорции ~6:1.

Примечание. Свойство date.timezone в php.ini установлено правильно (Европа/Париж).

Я искал здесь похожие вопросы и не нашел ничего похожего. Я также проверил руководство для функции date_default_time_zone() @ php. net и обнаружил, что я не только один заметил это, но до сих пор не могу понять, почему это происходит?

Кто-нибудь?


person Wh1T3h4Ck5    schedule 05.04.2011    source источник
comment
Ух ты. Поведение дублируется, 5.3.3 на CentOS 5.x (правда, ~0,65 и 0,31 секунды соответственно). Если это все еще происходит в 5.3.6 (я смогу протестировать позже на этой неделе, если никто не доберется до этого), я думаю, что стоит сообщить об ошибке.   -  person Charles    schedule 05.04.2011
comment
@Charles - да ... в Linux, php 5.3.2 - это 4.16 и 0.7 (без часового пояса всегда ~ 4 с, с часовым поясом 0,7 и меньше).   -  person Wh1T3h4Ck5    schedule 05.04.2011
comment
извините, это был PHP 5.2.13, на 5.3.2 это 2:1.   -  person Wh1T3h4Ck5    schedule 05.04.2011


Ответы (2)


Обновление для 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.

Я начал с предположения, что если вы не укажете часовой пояс для date, для его получения будет вызван date_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_set один раз, то guess_timezone выбирает быстрый путь чтения из переменной timezone (самое первое условное условие выполняется, и оно немедленно возвращается). В противном случае требуется некоторое время для определения часового пояса по умолчанию, который не кэшируется (я думаю, для простоты), и если вы делаете это в цикле, начинает отображаться задержка.

person Jon    schedule 05.04.2011
comment
Хорошо сделано. Так что в конце концов это не было ошибкой уровня PHP. Я удаляю свой. - person Pekka; 05.04.2011
comment
и поскольку ни один орган не запускает код 100 000 раз, они не утруждают себя кешированием предполагаемого часового пояса. - person nerkn; 05.04.2011
comment
Вау, спасибо, Джон, это действительно хорошая работа... Кажется, я смотрел не в том направлении. Я полностью удовлетворен этим ответом. Еще раз спасибо. - person Wh1T3h4Ck5; 05.04.2011
comment
@nerkn - да, я знаю, это смешно. Но это был мой способ получить наиболее подходящий результат. - person Wh1T3h4Ck5; 05.04.2011
comment
Я думал, что это будет что-то вроде этого. Спасибо за то, что вложили работу, необходимую для подтверждения. - person GordonM; 06.04.2011
comment
@Jon, интересно ... я этого не заметил ... спасибо за обновление. Как это работает сейчас? Угадайте, что он использует некоторую переменную, чтобы сделать значение TZ доступным из функций даты без угадывания (которое мы можем изменить вручную из кода). - person Wh1T3h4Ck5; 05.06.2012
comment
@ Wh1T3h4Ck5: есть две переменные timezone и default_timezone, которые могут повлиять на результат; если ни один из них не установлен, система по умолчанию использует UTC. - person Jon; 05.06.2012

Я предполагаю, что он должен определять часовой пояс для себя каждый раз, когда он вызывается, если он не указан явно, что увеличивает время выполнения функции.

Но на самом деле, имеет ли это значение? Сколько сценариев вы, вероятно, будете вызывать date() 100 000 раз за запуск?

person GordonM    schedule 05.04.2011
comment
Но зачем ему определять часовой пояс для себя, если в php.ini установлен часовой пояс по умолчанию? - person Pekka; 05.04.2011
comment
Этот вопрос не связан с ускорением, а скорее с внутренностями php - person nerkn; 05.04.2011