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

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

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

 <?php $start = microtime(true); for ($i = 0; $i < 100000; $i++) date('Ymd H:i:s'); echo (microtime(true) - $start); ?> 

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

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

 date_default_timezone_set(date_default_timezone_get()); 

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

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

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

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

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

Кто угодно?

Обновление для PHP 5.4:

Как date_default_timezone_get в описании date_default_timezone_get , начиная с PHP 5.4.0, алгоритм, позволяющий угадать часовой пояс из системной информации , был удален из кода (в отличие от источника PHP 5.3), поэтому этого поведения больше не существует.

Запустив тест времени на моем сервере dev, чтобы увидеть его в действии, я получил:

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

Оригинальный ответ:

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

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

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