svisch
@svisch

Как лучше работать в postgresql и php с временными зонами?

Здравствуйте. Помогите пожалуйста разобраться как лучше реализовать отображение даты и времени с учетом временной зоны пользователя, а то изучая этот вопрос запутался. Раньше использовал временную метку Unix и не заморачивался с зонами, теперь решил разобраться.
Итак, вот вопросы или утверждения, к которым я пришел в процессе изучения этого вопроса :
1. Дату и время в бд надо хранить в UTC. Вопрос - в timestamp with timezone(далее timstampTZ) или timestap without timezone (далее timestamp)?
2. Почему UTC, а не время сервера? В случае выбора timestamp понятно - удобнее делать смещение по сохраненным настройкам временной зоаны пользователя. А вот в случае timestampTZ нет, ведь там уже задано смещение.
3. Сервер (ОС, PHP, БД) настраивать на UTC время, или достаточно просто в нем его сохранять?
4. Если timestamp, то какой смысл и практическое применение типа данных timestampTZ ?
5. Пробовал сохранить в поле бд timestampTZ время с учетом смещения используя
new DateTime('now', new DateTimeZone('Europe/Moscow'))
- в базу попадает все равно время UTC. Почему? (бд настроена на UTC)

Буду благодарен за ответы на вопросы и подсказки лучших практик работы с временными зонами. Спасибо!
  • Вопрос задан
  • 409 просмотров
Пригласить эксперта
Ответы на вопрос 2
@tukreb
Лучше работать через DateTimeImmutable. А от DateTimeизбавляться где возможно.
Для PostgreSQL DateTimeImmutable это - TIMESTAMP(0) WITHOUT TIME ZONE
https://stackoverflow.com/questions/67536245/datet...
https://github.com/symfony/symfony/issues/47580

Вы всегда сможете прибавить к времени часовой пояс, там где вам нужно.
Ответ написан
Комментировать
gzhegow
@gzhegow
aka "ОбнимиБизнесмена"
1) Сначала в консольке там есть timezone + dpkg-reconfigure, там гуглим как сделать UTC
2) потом в приложении date_default_timezone_set() тоже ставим в UTC, и все новые даты (new \DateTime) у вас будут создаваться в UTC.
3) не забываем, что таймзона есть и в большинстве СУБД. Это бы всё да тестировать как одно на другое влияет, дескать приводит или не приводит, но для начала можно тупо везде это выставить.

* Я предполагаю что если передать со своей таймзоной в БД - оно сохранит верно, а вот если не передать ничего то пустая дата создастся в указанной таймзоне, что при запросе выдаст например 00.00.0000 00:00:00+02:00 вместо нуля по UTC, тоже касается любителей сравнивать с NOW() или проставлять по дефолту CURRENT_TIMESTAMP(), они скорее всего будут работать по настройке.

На практике я не встречал тех, кто упарывается в таймзону и в UTC. Попробовал когда-то в это топить - дали по шапке "у нас так не принято в компании". Каво? Некоторые ставят принудительно Europe/Moscow только в пхп и думают что все сделали (ведь на сервере время вполне может быть например Amsterdam, и что самое интересное - таймзона при сборке пхп из исходников шьется вовнутрь и может быть вообще другая, и тут уже каша начинается, ты не знаешь и не можешь это исправить) и потом с этим долбятся, потому чтоб поменять в системе - надо контейнеры делать, а девопсом 90% из них не является, а контейнеры они понимают только на словах, а еще это надо просчитывать на что повлияет, а они боятся, и может даже правильно... Короче. Вам нужно понять принцип как её устанавливать, а не пытаться затребовать у всех UTC. Но если вы с начала проект делаете и сами собираете окружение - то поставить не лишне.

В итоге конвертировать их придется только при выводе в таймзону пользователя. Для этого пишем какой-то класс Calendar и там метод formatDate(), он на вход может принимать таймзону, которую вам пользователь заранее аяксом передал и вы её положили в сессию например или в кеш на 15 минут.

Стоит не забывать что бывают формы, где нужно указать время, то есть принять время от юзера. В этом случае он будет указывать его в своем поясе и тут надо превращать в ваш, серверный пояс, как вы пишете - в UTC. В пыхе есть несколько способов сломать себе мозг на датах и один из них заключается примерно в следующем - в зависимости от того, примените вы таймзону в качестве аргумента или установите её с помощью сеттера - будут произведены разные действия.

Если аргумент, то пыха посчитает, что это таймзона, в которой вы написали дату. При этом прикол в том, что если вы парсили дату из текста, там уже может быть таймзона, соответственно она либо перепишется, либо попробует пересчитаться, напишите файлик и поиграйтесь.

Если же сеттером - то "к этой таймзоне нужно конвертировать". Все бы хорошо, если бы не третий неявный параметр - date_default_timezone_get(), ведь сеттер отталкивается от него, что не всегда очевидно. То есть вы вроде пытались сделать UTC, а в итоге получили разницу между временем сервера и переданной датой, особенно понимая, что сама дата изначально в тексте могла содержать временную зону и получили неожиданный результат.

Для этого вам надо открыть файлик и написать все возможные варианты с таймзонами и вывести их var_dump, чтобы понять принцип что на что влияет.

Иммутаблы это ересь для тех, кто пока ещё не умеет работать с датами или не понимает ООП.

Разница между иммутаблом и не иммутаблом в том, что первый по результатам любого действия возвращает другой объект с датой, чтобы вы случайно не изменили старую. Если вы понимаете, что времядата это объект, то вы этого не сделаете. Точно так же как вызывая какой-нибудь $config->set(); вы понимаете что вы меняете конфиг, а не создаете новый. Вполне можно использовать clone/new чтобы создать несколько дат перед тем как начать их менять.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы