Laravel по-русски

Русское сообщество разработки на PHP-фреймворке Laravel.

Ты не вошёл. Вход тут.

#1 23.06.2016 22:45:00

Изменение временной зоны

Всем привет.

Народ нужна небольшая помощь, пытался разобраться сам но не получилось((

Задача следующая:
Для служебных целей пишу мини CRM. В CRM есть таблица со списком филиалов и в каждом филиале есть настройка timezone вида Europe/Moscow
Пытаюсь сделать так чтобы когда другой пользователь работая в каком-то филиале (добавлял посты, записи и другую информацию) добавлял время в поля created_at и updated_at или другие нужные поля с учетом временной зоны

Посоветовали для каждой модели с которой работает филиал добавить мутаторы... Хоть документацию прочитал но не особо понял как эту информацию применить((

Не в сети

#2 23.06.2016 22:58:42

Re: Изменение временной зоны

  1. Посоветовали для каждой модели с которой работает филиал добавить мутаторы…

Это отличный способ выстрелить себе в ногу. Моё мнение: что с экранированием HTML, что с форматированием дат и чисел и других вещей, которые могут иметь разное представление в зависимости от разных настроек (пользователя) или контекста (plain text, веб-страница) — всё это надо делать только и исключительно при выводе, а в базе хранить сырые значения, т.е. plain text, а не экранированный код, числа без форматирования (1234567.89, а не 1,234,567.89), дату в UTC (либо как unsigned int, либо как datetime, последнее удобней). Если делать иначе, то будет постоянная проблема: «а я уже сконвертировал это или нет?» и «в каком формате вот это значение?». Мутаторы неявны, про них постоянно забываешь и получается, что где-то дата не сконвертирована, а где-то сконвертирована дважды. Для вывода можно написать пару глобальных функций и использовать в шаблонах.

Есть другие мнения на этот счёт, но это лично моё, из богатого опыта.

Laravel использует Carbon для дат, с ним легко можно конвертировать их в разные временные зоны и форматы для вывода. Тут пример с изменением TZ.

Не в сети

#3 23.06.2016 23:19:33

Re: Изменение временной зоны

даты в timestamp в базе хранятся(2016-06-23 23:11:11)
Карбон читал, но блин ума не хватает доформатировать  вывод под нужную timezone...
пример $message->created_at  выводит объект Carbon

object(Carbon\Carbon)#236 (3) {
  ["date"]=>
  string(26) "2016-06-23 19:11:59.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(17) "America/Vancouver"
}

Хотел $message->created_at->timezone('тут зона') но не сработало


Хотя сработало, запутался уже))

Изменено deller (23.06.2016 23:28:53)

Не в сети

#4 23.06.2016 23:35:47

Re: Изменение временной зоны

Кjроче в app.php поставил 'timezone' => 'Europe/Moscow', и зону меняю при выводе как выше написал $message->created_at->timezone('тут зона')
Вроде все сходится...

Не в сети

#5 24.06.2016 00:12:43

Re: Изменение временной зоны

  1. Короче в app.php поставил ’timezone’ ⇒ ’Europe/Moscow’,

Поставь ’Etc/UTC’, меньше проблем будет.

Не в сети

#6 24.06.2016 09:43:54

Re: Изменение временной зоны

Proger_XP пишет:

}%>Поставь 'Etc/UTC', меньше проблем будет.

А в чем разница между просто UTC  и Etc/UTC?

Вроде разобрался...

В базу записывается время сервера(московское)
UTC настройка как я понял всегда отображает +0 от того что в базе записано т.е. записано 9,30(московское +3) покажет 6,30(+0)

Получается разделы отличные от филиалов будут отображать время в UTC  зоне, не лучше ли будет поставить по умолчанию Europe/Moscow в конфиг, чтобы по московскому времени общий контент показывать?

Не в сети

#7 24.06.2016 11:04:42

Re: Изменение временной зоны

  1. А в чем разница между просто UTC и Etc/UTC?

Если не ошибаюсь, Etc/UTC стандартный, а просто UTC специфичный для PHP. Но да, подходит любой из этих.

  1. Получается разделы отличные от филиалов будут отображать время в UTC зоне, не лучше ли будет поставить по умолчанию Europe/Moscow в конфиг, чтобы по московскому времени общий контент показывать?

Не надо лениться конвертировать значения при любом выводе. Лучше сделай новую настройку — зону для вывода, которая будет использоваться всегда, если нет причины использовать какую-то другую, и выстави там Москву, а зону для приложения (Laravel) используй UTC.

Почему? Представь, что у зоны, которая у тебя стоит по умолчанию, сменилось DST (летнее время). Несколько лет назад в России то в один год объявляли, что летнего времени больше не будет, то возвращали обратно. Допустим, снова меняется DST и теперь оно есть. Теперь представь — приложение работает, база наполнена, всё хорошо. Потом ты обновляешь PHP, которая идёт уже с новой базой зон, где DST включено. И магическим образом всё время в базе сбивается. Если заметил не сразу (а такие вещи ничего кардинально не ломают и замечаются ой как не сразу), то в базе будет полный бардак: некоторые значения записаны в станом формате, т.е. время отстаёт на 1 час, а некоторые в новом. Механически исправить это сложно и есть большой риск путаницы, т.к. некоторые даже старые значения код мог обновить и ещё сильнее сбить.

Последовательность должна быть такая:

  • принимаем дату/время от пользователя — тут же конвертируем и его зоны в UTC
  • храним в базе только время в UTC, и работаем в коде (вычисляем разницу и пр.) тоже только над значениями в UTC
  • выводим время — конвертируем из UTC в нужную зону

Таким образом, всё что входит и выходит за периметр имеет свою зону, а всё что внутри — строго UTC.

Не в сети

#8 24.06.2016 12:27:40

Cheshirrski
masterМастер
Откуда: Днепр
Сообщений: 116
Статей: 2

Re: Изменение временной зоны

По-порядку:
В базе храним даты сервера. Это наша точка отсчета. Естественно их не трогаем. Т.е все события происходящие на сайте пишутся по времени сервера. Временную зону ставите какую вам удобно.
Если нужно отобразить поправку на локальное время пользователя, делаем мутатор. Не люблю это слово, т.к не понятно что конкретно оно делает, предпочитаю более привычные геттер (мутатор который изменяет данные при выводе) и сеттер (мутатор, который изменяет данные при вводе). В вашем случае делаем геттер для полей created_at и updated_at, но, т.к оригинальные created_at и updated_at могут пригодиться, сделаем это через псевдополя, пусть будут created_utc и updated_utc.
Сначала добавим в модель наши псевдополя

PHP
protected $appends = [
  
'created_utc',
  
'updated_utc',
];

Создадим геттеры

PHP
public function getCreatedUtcAttribute()
{
  
$timeZone = \\здесь получаем timezone пользователя.

  return \
Carbon\Carbon::createFromFormat('Y-m-d H:i:s'$this->getOriginal('created_at'))->timezone($timeZone);
}

public function 
getUpdatedUtcAttribute()
{
  
$timeZone = \\здесь получаем timezone пользователя.

  return \
Carbon\Carbon::createFromFormat('Y-m-d H:i:s'$this->getOriginal('updated_at'))->timezone($timeZone);
}

Все, теперь ваша модель имеет поля $model->created_utc и $model->updated_utc.

Теперь вот про это:
Вам не кажется что

  1. в базе хранить сырые значения

и

  1. принимаем дату/время от пользователя — тут же конвертируем и его зоны в UTC

противоречат друг другу?)

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

Вы просто их неправильно готовите

  1. Для вывода можно написать пару глобальных функций и использовать в шаблонах

Поздравляю, это называется геттер, и место ему в модели.

Изменено Cheshirrski (24.06.2016 12:29:28)

Не в сети

#9 24.06.2016 12:35:46

Re: Изменение временной зоны

  1. противоречат друг другу?)

Каким образом? Ввод от пользователя нормализуется (переводится из его зоны в UTC) и сохраняется в базу. В базе хранятся сырые, т.е. нормализованные значения.

  1. Вы просто их неправильно готовите

У вас то же решение, которое описывал я. И да, тоже не мутаторы (в моём понимании это методы с тем же именем, что и само поле, т.е. прозрачно подменяющие его значение для вызывающего кода).

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

Да, либо метод в модели.

Не в сети

#10 24.06.2016 16:43:39

Re: Изменение временной зоны

ооо Спасибо Вам большое за разъяснения!! И спасибо Cheshirrski за пример кода, буду пробовать.

Не в сети

#11 24.06.2016 17:55:12

Re: Изменение временной зоны

Все применил, работает!
С начало думал что 'Y-m-d H:i:s' это выводимый формат, потом дошло что это формат получаемый из базы, отформатировал добавив ->format('')

Еще 1 вопросик.

Можно ли создать мутатор(геттер) глобальным для определенных таблиц?
Конечно можно прописать все в каждой модели, но лично мне не нравится дублировать один и тот же код...

Не в сети

#12 24.06.2016 18:09:20

Re: Изменение временной зоны

Хотя вот не состыковка какая-то...

время в базе записано как  17:25:10 это время сервера, оно же и Московское
прогоняю через геттер, timezone получает значение временной зоны филиала Europe/Moscow и прибавляет к времени в базе  +3 часа
получается 20:25:10 что не правильно((

Не в сети

#13 25.06.2016 17:46:07

Cheshirrski
masterМастер
Откуда: Днепр
Сообщений: 116
Статей: 2

Re: Изменение временной зоны

Скорее всего вы забыли поставить в config/app.php ’timezone’ ⇒ ’Europe/Moscow’, еще подозрение на ->format('') — по идее это вообще не должно работать. В каком формате вы храните время? Почитайте документацию на Carbon и выбирайте подходящий метод, ссылку вам выше давали.

  1. Можно ли создать мутатор(геттер) глобальным для определенных таблиц?

Можно, в Laravel можно все что можно в PHP. Но я бы не советовал. Смысл в том, чтобы сделать из модели единственную точку работы с данными. Чтобы не бегать по представлениям, хелперам, контроллерам, в поисках «откуда это взялось», и запись $model->created_utc означала ровно то, что написано.

Не в сети

#14 25.06.2016 22:14:41

Re: Изменение временной зоны

  1. Хотя вот не состыковка какая-то…
  2. время в базе записано как 17:25:10 это время сервера, оно же и Московское
  3. прогоняю через геттер, timezone получает значение временной зоны филиала Europe/Moscow и прибавляет к времени в базе +3 часа
  4. получается 20:25:10 что не правильно((

Именно такие нестыковки у вас будут кругом и рядом, если в базе будет не UTC.

Не в сети

#15 25.06.2016 22:18:48

Re: Изменение временной зоны

Да, поставил в app Europe/Moscow и все стало на свои места, вроде время в разных зонах везде совпадает.

Мне представляется следующий механизм такого результата:
1. В базу записывается время по серверному времени(на сервере оно равно Europe/Moscow)
2. Настройка в app говорит Carbon в какой зоне выводить время
3. Если стоит UTC то Carbon считает что в базе время записано как UTC и ничего не меняет, но оно же там серверное(Московское)
4. И получается когда делаем return \Carbon\Carbon::createFromFormat('Y-m-d H:i:s', $this->getOriginal('created_at'))->timezone($timeZone); зона применяется к серверному(Московскому), дважды Московское получается если можно так выразится))
5. Если ставим в app Europe/Moscow (как на сервере) говорим Carbon что у нас сервер по Московскому времени работает и отсюда уже пляшем, конвертим в другие зоны как надо.

Не в сети

#16 26.06.2016 13:01:23

Роман

Re: Изменение временной зоны

в базу только в UTC, почему Вы противитесь этому ?

#17 26.06.2016 14:49:23

Re: Изменение временной зоны

Да  я не противлюсь)) оно само туда как Московское записывается)

Не в сети

#18 26.06.2016 14:59:04

Re: Изменение временной зоны

Вы (народ) шаблонно думаете. в этом ваша проблема.

deller, работай на сервере от app::Europe/Moscow как и настроил, а в профиль клиента добавь зону и работай от неё.
если зоны не указано или это гость - подключай GeoIP на стороне операционки и/или пакажом от лары, - им и определяй какое время показать.
Карбонатом только выводи результаты во view, считать правильное время надо только во время отображения, ибо это вычисляемые данные, которые формируют информацию.

Изменено hzone (26.06.2016 14:59:50)

Не в сети

#19 01.02.2017 23:29:09

Re: Изменение временной зоны

Столкнулся с похожей проблеммой... мозг сломал... smile
Вот наткнулся на просторах инета: Easy timezones in Laravel with Carbon https://andrew.cool/blog/49/Easy-timezo … ith-Carbon ,
Мне кажется толковое решение, попробую...

Не в сети

#20 02.02.2017 17:45:16

Re: Изменение временной зоны

Aleh пишет:

Столкнулся с похожей проблеммой... мозг сломал... smile
Вот наткнулся на просторах инета: Easy timezones in Laravel with Carbon https://andrew.cool/blog/49/Easy-timezo … ith-Carbon ,
Мне кажется толковое решение, попробую...

Вообщем я пошел по пути hzone(Спасибо!), на сервере от app::UTC...

Не в сети

#21 09.05.2018 16:16:19

Re: Изменение временной зоны

php artisan config:cache

Не в сети

#22 10.05.2018 15:11:40

Re: Изменение временной зоны

Не прямо по теме, но связано.

Если кто-то не в курсе, тип mysql datetime хранит буквальное время, как написали. В отличие от timestamp который как-бы в абсолютном значение по Гринвичу, а его текстовое представление транслируется туда и обратно согласно сессионной таймзоны. Это не очень важно пока не поменялась зона на сервере. А поменяться она может запросто если у вас production, staging и local не одно и то же и вы периодически копируете базу wink Или можно словить нежданчик при переезде.

У нас временные зоны для php и для mysql принудительно выставляются одинаковыми и могут быть переделаны через параметр в .env, как принято в Laravel smile

config/app.php
    'timezone'  => env('TIMEZONE', 'our default timezone'),

config/database.php

    'connections' => [
        'mysql' => [
...
            'timezone'  => env('TIMEZONE', 'our default timezone'),

Изменено artoodetoo (10.05.2018 15:35:25)


There are two hard things in computer science: cache invalidation, naming things, and off-by-one errors.

Не в сети

Подвал раздела