Вы заметили, что функция 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 и обнаружил, что я не только тот, кто это заметил, но все еще не понимает, почему это происходит?
Кто угодно?
Как date_default_timezone_get
в описании date_default_timezone_get
, начиная с PHP 5.4.0, алгоритм, позволяющий угадать часовой пояс из системной информации , был удален из кода (в отличие от источника PHP 5.3), поэтому этого поведения больше не существует.
Запустив тест времени на моем сервере dev, чтобы увидеть его в действии, я получил:
Я просто заглянул в источник 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 раз за каждый запуск?