Отсчет мирового времени ведется по усреднённому времени Гринвича с аббревиатурой GMT, которое после стандартизации именуется Всемирным координированным временем UTC.
Время UTC соответствует нулевому часовому поясу "00:00", от которого на восток отсчитываются часовые пояса до +14:00 и на запад до -11:00 (фактические используемые пояса).
Ниже изложены принципы и правила работа со временем в PHP и MySQL.
The Current Epoch Unix Timestamp + Converter //www.unixtimestamp.com
//
MySQL
В MySQL предусмотрено два принципиально отличных типа, предназначенных для работы с датой и временем, это TIMESTAMP и DATETIME. Представление TIMESTAMP и DATETIME и операции с ними очень похожи, но хранимые значения принципиально отличаются.
Часовой пояс Timezone
Хотя в общем смысле момент времени события для всех наблюдателей должен быть одним, часовой пояс отражает локальный сдвиг часов наблюдателя для пояса, в котором он находится, чем создает сложности в работе со временем.
В большинстве случаев преобразование значения времени к строке и обратное преобразование выполняется с коррекцией смещения времени часового пояса сеанса. Это происходит в IDE при представлении результатов запросов, поэтому в таблице отображаются не совсем те данных, которые реально хранятся в DB!
Представление данных не изменяются в сеансе часового пояса UTC, который можно установить:
1 |
SET time_zone = 'UTC'; |
Актуальные значения часового пояса сервера и сеанса отражаются глобальными переменными @@global.time_zone и @@session.time_zone:
1 2 |
SELECT @@global.time_zone, @@session.time_zone; -> SYSTEM | UTC |
TIMESTAMP
Концептуально TIMESTAMP наиболее точно представляет суть времени. Это знаковое целое число ведущее отсчет секунд от 1 января 1970 года. Отсчет времени ведется по GMT/UTC/"00:00", который является абсолютным, и не зависит от временной зоны.
Физически TIMESTAMP хранится целым числом x32, что накладывает ограничения на диапазон допустимых дат от '1901-12-13 20:45:52' до '2038-01-19 03:14:07'.
Представление TIMESTAMP в строковом виде, например Unix-формате '2023-07-05 23:47:18 +03:00', осуществляется динамически в соответствии с установленным часовым поясом, поэтому определенное значение TIMESTAMP может получать различные строковые представления в разных временных зонах. Этот же феномен возникает во время обратного преобразования строкового представления к значению TIMESTAMP, которое может отличаться в зависимости от установленной временной зоны.
Запись
Правильная запись актуального времени события в журнал:
1 |
INSERT INTO log (gmt, event) VALUES(UTC_TIMESTAMP(),"Glory to Ukraine!"); |
DATETIME
Семейство типов DATETIME представляет значение времени в формате строки от '1000-01-01 00:00:00' до '9999-12-31 23:59:59', которое хранится в формате упакованного числа. Такой способ не зависит от временной зоны, его представление всегда постоянно и соответствует сохраненному значению.
До версии MySQL 5.6.4 тип DATETIME сохранялся числом 8byte по шаблону YYYYMMDDHHMMSS, новые версии использую 5byte.
В семейство входят типы YEAR, DATE, TIME предназначенные сохранять и представлять соответствующий фрагмент времени.
Значение можно задать строковым литералом, при этом указание временной зоны игнорируется. !!!
1 2 3 4 5 6 |
SELECT UNIX_TIMESTAMP(TIMESTAMP('2023-04-05 06:07:08')), TIMESTAMP('2023-04-05 06:07:08'), TIMESTAMP('2023-04-05 06:07:08')+0, CAST('2023-04-05 06:07:08' AS DATETIME) -> 1680664028 | 2023-04-05 06:07:08 | 20230405060708 | 2023-04-05 06:07:08 |
Функции
//dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html
- Представление
- DATE_FORMAT(date,format)/
- date значение типа DATETIME, TIMESTAMP или VARCHAR приводимый к первым типам
- format строка произвольного сочетания символов и спецификаторов:
- %a Abbreviated weekday name (Sun..Sat)
%b Abbreviated month name (Jan..Dec)
%c Month, numeric (0..12)
%D Day of the month with English suffix (0th, 1st, 2nd, 3rd, …)
%d Day of the month, numeric (00..31)
%e Day of the month, numeric (0..31)
%f Microseconds (000000..999999)
%H Hour (00..23)
%h Hour (01..12)
%I Hour (01..12)
%i Minutes, numeric (00..59)
%j Day of year (001..366)
%k Hour (0..23)
%l Hour (1..12)
%M Month name (January..December)
%m Month, numeric (00..12)
%p AM or PM
%r Time, 12-hour (hh:mm:ss followed by AM or PM)
%S Seconds (00..59)
%s Seconds (00..59)
%T Time, 24-hour (hh:mm:ss)
%U Week (00..53), where Sunday is the first day of the week; WEEK() mode 0
%u Week (00..53), where Monday is the first day of the week; WEEK() mode 1
%V Week (01..53), where Sunday is the first day of the week; WEEK() mode 2; used with %X
%v Week (01..53), where Monday is the first day of the week; WEEK() mode 3; used with %x
%W Weekday name (Sunday..Saturday)
%w Day of the week (0=Sunday..6=Saturday)
%X Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V
%x Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v
%Y Year, numeric, four digits
%y Year, numeric (two digits)
%% A literal % character
%x x, for any “x” not listed above
- %a Abbreviated weekday name (Sun..Sat)
- STR_TO_DATE(string, format) Format a date from a string with STR_TO_DATE and return a date/datetime value. The string is what you want to reformat, while the format is a combination of specifiers that describe each element of the string.
- DATE_FORMAT(date,format)/
- Конвертирование
- UNIX_TIMESTAMP([date]) возвращает метку времени UNIX заданной даты или текущего момента, представленную знакомым числом секунд от '1970-01-01 00:00:00 UTC'
- FROM_UNIXTIME(uts[,format]) для заданной unix-метки времени uts возвращает соответствующее значение даты для часового пояса сеанса, тип значения DATETIME или VARCHAR определяется вторым необязательным параметром
- format строковое значение
- при опущенном параметре функция возвращает значение DATETIME
12SELECT FROM_UNIXTIME(1680664028, '%m*%d*%Y %H*%i*%S')-> 04*05*2023 06*07*08 - format строковое значение
- CONVERT_TZ(dt,from_tz,to_tz) для значения dt типа DATETIME в часовом поясе from_tz возвращает измененное значение для часового пояса to_tz
- EXTRACT(unit FROM date) извлекает числовое значение фрагмента даты date, заданного спецификатором unit (см. DATE_ADD(), DATE_SUB())
- DATE(expr) извлекает DATE (без времени) из DATETIME, VARCHAR и т.п.
- Операции над значениями
- ADDDATE/DATE_ADD(date, INTERVAL value unit) Add a time/date value to a date expression with the DATE_ADD or ADDDATE function. Replace date with the date expression you want to add a time/date to. The value unit is the time/date you want to add. It needs to be expressed in a numeric value along with the unit of the value.
- SUBTIME(datetime,timevalue)
DATE_SUB(date, INTERVAL value unit) Subtract a time/date value to a date expression with the DATE_SUB or SUBDATE function - DATEDIFF(expr1,expr2)
- MAKEDATE(year,day)
- MAKETIME(hour, minute, second)
- !!!
- TIMESTAMPADD(unit,value,datetime);
- TIMESTAMPDIFF(unit,datetime1,datetime2);
PHP
В PHP не предусмотрен базовый тип для работы с датой и временем. PHP использует метку системного времени Unix, выраженную знаковым целым числом секунд от начала отсчета Unix-времени 1 января 1970 00:00:00 GMT.
Штатными средствами к метке времени применимо прямое и обратное преобразование к строке в Unix-формате '1970-01-01 00:00:00', которая соответствует шаблону 'Y-m-d H:i:s'.
Развитые средства работы с датой и временем предоставляет класс DateTime.
Функції для роботи з датою та часом
//
- time(): int возвращает текущую метку системного времени Unix, которая равна количеству секунд, прошедших с начала эпохи Unix (1 января 1970 00:00:00 GMT) до текущего времени
- hrtime(bool $as_number = false): array|int|float|false возвращает время высокого разрешения системы, отсчитываемое с произвольной точки времени, полученная временная метка неизменна и не может быть отрегулирована (рекомендуется для измерения производительности)
- as_number по умолчанию возвращает array [секунды, наносекунды]
- в значении true устанавливает тип результата int выражающее наносекунды 10-9
- точность значения ±10ns или еще хуже
- в значении true устанавливает тип результата int выражающее наносекунды 10-9
- as_number по умолчанию возвращает array [секунды, наносекунды]
- microtime(bool $as_float = false): string|float возвращает текущую метку времени Unix с микросекундами. Эта функция доступна только на операционных системах, в которых есть системный вызов gettimeofday().
- idate(string $format, ?int $timestamp = null): int|false возвращает из метки времени timestamp часть заданную параметром format:
- базовые: d День месяца, m Месяц, y Год столетия, Y Год, o Год ISO-8601, h Час/12, H Час/24, i Минута, s Секунда
- периодные: N День недели Пн(1)..Вс(7), w День недели Вс(0)..Сб(6), W Неделя года, z День года
- вспомогательные: t Дней в месяце, I летнее время, L високосный год, Z Смещение секунд часового пояса
- специальные: U Секунд от начала эпохи UNIX, B текущий бит суток в системе Swatch Internet Time (1/1000 суток = 86.4 секунд)
- getdate(?int $timestamp = null): array возвращает ассоциативный массив, содержащий информацию о дате и времени, представленной меткой времени timestamp
- date(string $format, ?int $timestamp = null): string представляет метку времени timestamp строкой по шаблону в строке format, допускающему форматные символы:
- format форматный шаблон состоит и произвольной комбинации форматных символов //php.net/manual/ru/datetime.format.php
наиболее часто используются:- d День 01..31, D День неделиMon..Sun, j День 1..31
- m Месяц 01..12, M Месяц Jan..Dec, n Месяц 1..12
- Y Год, y Год века 00..99
- H Час 00..23, h Час 01..12, G Час 0..23
- i Минута 00..59
- s Секунда 00..59
- timestamp метка времени для представления; если не указана, будет использована текущая метка из time()
- ВНИМАНИЕ! представление метки времени выполняется для текущего часового пояса сервера! Это значит, что одно значение timestamp будет представлено по-разному в зависимости от установленной timezone:
12345echo date('Y-m-d H:i:s', time()); // 2023-04-01 01:23:45date_default_timezone_set('UTC');echo date('Y-m-d H:i:s', time()); // 2023-03-31 22:23:45date_default_timezone_set('Europe/Kyiv');echo date('Y-m-d H:i:s', time()); // 2023-04-01 01:23:45
- ВНИМАНИЕ! представление метки времени выполняется для текущего часового пояса сервера! Это значит, что одно значение timestamp будет представлено по-разному в зависимости от установленной timezone:
- format форматный шаблон состоит и произвольной комбинации форматных символов //php.net/manual/ru/datetime.format.php
- strtotime(string $datetime, ?int $baseTimestamp = null): int|false возвращает метку времени
- по строке представления даты и времени '2022-10-12 19:44:59.000000'
- по строке относительного формата '+30 sec -20 min +10 hour -5 day +2 month -1 year' //php.net/manual/ru/datetime.formats.relative.php
- date_parse(string $datetime): array парсит строковое представление даты в ассоциативный массив
- mktime( int $hour, ?int $minute = null, ?int $second = null, ?int $month = null, ?int $day = null, ?int $year = null ): int|false Возвращает метку времени Unix для заданной даты
- допустимы нулевые и отрицательные значения месяца и дня, которые означают отступ от единичного значения (например: 0 месяц - це грудень попереднього року, -1 день - це передостанній день попереднього місяця)
- також негативні значення доступні для годин, хвилин і секунд
- дивись gmmktime(...) яка повертае локальну мітку часу Unix для часу за Грінвічем
Часовий пояс
Роль часового пояса (временной зоны) в документации прописана недостаточно внятно, что может привести к серьезным ошибкам в мульти-зонных системах.
- date_default_timezone_set(string $timezoneId): bool устанавливает часовой пояс по умолчанию для всех функций даты/времени в скрипте
- timezoneId строковый идентификатор часового пояса вида 'Europe/Kyiv', 'UTC' и другие
- timezone_abbreviations_list(): array возвращает каскадный ассоциативный массив, содержащий флаг перехода на летнее время, смещение и имя часового пояса; содержит все исторические случаи использования аббревиатур, что может привести к правильным, но запутанным записям., включая некоторые противоречия, поэтому список для создания списка выбора часового пояса пользователем не подходит
- 'timezone_id' => 'Etc/GMT',
- 'timezone_id' => 'Europe/Kiev',
- //
Классы DateTime, DateTimeZone
//
Классы DateTime
Объект класса DateTime реализует метку времени и методы.
- public __construct(string $datetime = "now", ?DateTimeZone $timezone = null ) конструктор позволяет сразу задать значение метки времени и часовой пояс
- datetime задает начальное значение метки времени великим множеством способов
- 'now' и по умолчанию устанавливает метку в текущее временя UTC, при этом часовой пояс заданные в timezone на значение метки не повлияет
1$dt = new DateTime('now', new DateTimeZone('Europe/Kiev')); - строкой в Unix-формает 'Y-m-d H:i:s' для заданного в timezone часового пояса
1$newYearUTC = new DateTime('2023-01-01 03:00', new DateTimeZone('Europe/Kiev')); - строкой из форматов и их комбинаций
//www.php.net
12$my_yesterday_noon = new DateTime('yesterday noon', new DateTimeZone('Europe/Kiev'));$next_week_friday_evening_show = new DateTime('next week friday 20:30');- 'yesterday' | 'today' | 'tomorrow'
- 'midnight'' | 'noon'
- 'weekday' | 'weekdays'
- 'sunday' | 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday' | 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'
- 'first' | 'second' | 'third' | 'fourth' | 'fifth' | 'sixth' | 'seventh' | 'eighth' | 'ninth' | 'tenth' | 'eleventh' | 'twelfth'
- 'next' | 'last' | 'previous' | 'this'
- 'now' и по умолчанию устанавливает метку в текущее временя UTC, при этом часовой пояс заданные в timezone на значение метки не повлияет
- timezone //
- 'UTC', 'Europe/Kiev', Europe/Brussels, Asia/Jerusalem, America/New_York
//
- 'UTC', 'Europe/Kiev', Europe/Brussels, Asia/Jerusalem, America/New_York
- datetime задает начальное значение метки времени великим множеством способов
- public setDate( int $year, int $month, int $day ): DateTime
- public setISODate( int $year, int $week, int $dayOfWeek = 1 ): DateTime
- public setTime( int $hour, int $minute, int $second = 0, int $microsecond = 0 ): DateTime
- public static createFromFormat(string $format, string $datetime,?DateTimeZone $timezone = null ): DateTime|false создает объект DateTime из строки произвольного формата
- public getTimestamp(): int возвращает смещение установленной
- public setTimezone( DateTimeZone $timezone): DateTime
- public getOffset(): int возвращает смещение часового пояса в секундах от UTC
12$kyiv_dt = new DateTime( 'first day of July', new DateTimeZone( 'Europe/Kiev' ));echo $kyiv_dt->getOffset(); // 10800 - public add( DateInterval $interval ): DateTime сдвигает вперед метку времени на интервал
- public sub( DateInterval $interval ): DateTime сдвигает назад метку времени на интервал
- public diff( DateTimeInterface $targetObject, bool $absolute = false ): DateInterval
public format( string $format ): string
public getOffset(): int
Класс часового пояса DateTimeZone
Объективно существует 24 часовых пояса. Отсчет исторически ведется от положения Британской Гринвичской королевской обсерватории. Это - пояс GMT. Новый современный отсчет поясов называется Всемирное координированное время UTC, и он полностью эквивалентен GMT.
По многим организационным причинам официальных и неофициальных часовых поясов существует около 200:
- UTC Coordinated Universal Time UTC±00
- GMT Greenwich Mean Time UTC±00
- EET Eastern European Time UTC+2/GMT+2
- EEST Eastern European Summer Time UTC+3/GMT+3
- IST Israel Standard TimeUTC+2/GMT+2
- IDT Israel Daylight Time UTC+3 GMT+3
- и другие //en.wikipedia.org/wiki/List_of_time_zone_abbreviations
//
- public __construct(string $timezone)
- public getLocation(): array|false
- public getName(): string
- public getOffset(DateTimeInterface $datetime): int
123$Kyiv_timezone = new DateTimeZone('Europe/Kiev');echo $dtz->getOffset(new DateTime('2023-06-31')); // 10800echo $dtz->getOffset(new DateTime('2023-12-31')); // 7200 - public getTransitions(int $timestampBegin = PHP_INT_MIN,int $timestampEnd = PHP_INT_MAX): array|false возвращает в заданных границах времени все изменения/переходы для часового пояса
- данные возвращаются в форме индексированного массива ассоциативных массивов, каждый из которых содержит значения с индексами:
- ts метка времени начала действия изменения/перехода
- time время метки в строковом Unix-формате
- offset смещение от UTC в секундах
- isdst признак летнего времени
- abbr аббревиатура часового пояса
Наприклад відображення історичної події переходу України 30 червня 1990 року з московського часового поясу за літнім часом (MSD) до поясу Eastern European Summer Time (EEST):
12$demo = (new DateTimeZone('Europe/Kiev'))->getTransitions(strtotime( '1990-01-01' ), strtotime( '1991-12-31' ));результат у json-поданні:
123456789101112131415161718192021222324252627282930[{"ts": 631141200,"time": "1989-12-31T21:00:00+0000","offset": 10800,"isdst": false,"abbr": "MSK"},{"ts": 638319600,"time": "1990-03-24T23:00:00+0000","offset": 14400,"isdst": true,"abbr": "MSD"},{"ts": 646783200,"time": "1990-06-30T22:00:00+0000","offset": 10800,"isdst": true,"abbr": "EEST"},{"ts": 686102400,"time": "1991-09-29T00:00:00+0000","offset": 7200,"isdst": false,"abbr": "EET"}]
- данные возвращаются в форме индексированного массива ассоциативных массивов, каждый из которых содержит значения с индексами:
- public static listIdentifiers(): array возвращает индексированный массив строковых идентификаторов всех часовых поясов (~420 поясов в 11 группах)
DateTimeImmutable
DateTimeInterface
DateTime
DateTimeImmutable
DateTimeInterface
DateTimeZone
Интернет ресурсы
Unix Timestamp
The Current Epoch Unix Timestamp онлайн конвертер //www.unixtimestamp.com
Веб-сервис geoPlugin
API //www.geoplugin.com/webservices/php
Вызов api выполняется http-запросом
1 |
http://www.geoplugin.net/php.gp?ip=46.185.12.123 |
В содержание ответа находится php-сериализация ассоциативного массива:
- geoplugin_request '46.185.12.123' обработанный ip-адрес
- geoplugin_status 200 код успешного ответа
- geoplugin_delay '1ms' время выполнения
- geoplugin_credit 'Some of the returned data includes GeoLite data created by MaxMind, available from <a href=\\'http://www.maxmind.com\\'>http://www.maxmind.com</a>.'
- geoplugin_city 'Kyiv' город
- geoplugin_region 'Kyiv City' регион/область
- geoplugin_regionCode '30' код региона/области
- geoplugin_regionName 'Kyiv City' имя региона/области
- geoplugin_areaCode '' код территории
- geoplugin_dmaCode '' ?
- geoplugin_countryCode 'UA' код страны
- geoplugin_countryName 'Ukraine' название страны
- geoplugin_inEU 0 признак вхождения в Евросоюз (0: нет, 1: да)
- geoplugin_euVATrate false налоговый признак
- geoplugin_continentCode 'EU' код континента
- geoplugin_continentName 'Europe' название континента
- geoplugin_latitude '50.458' широта
- geoplugin_longitude '30.5303' долгота
- geoplugin_locationAccuracyRadius '200' радиус размещения
- geoplugin_timezone 'Europe/Kyiv' часовой пояс
- geoplugin_currencyCode 'UAH' код национальной валюты
- geoplugin_currencySymbol '₴' символ национальной валюты
- geoplugin_currencySymbol_UTF8 '₴' символ национальной валюты в кодировке UTF-8
- geoplugin_currencyConverter '36.8532' текущий курс национальной валюты к EURO
//
1 2 3 |
$geo_response = unserialize( file_get_contents( 'http://www.geoplugin.net/php.gp?ip=' . $_SERVER['REMOTE_ADDR'] ))); $time_zone = new DateTimeZone( $geo_response['geoplugin_timezone'] ); |
Веб-сервіс ip-api
Fast, accurate, reliable. Free for non-commercial use, no API key required. Easy to integrate, available in JSON, XML, CSV, Newline, PHP. Serving more than 1 billion requests per day, trusted by thousands of businesses
Джерела
Datetime или timestamp lexxscorp@habr.com (Jun 4, 2009)