Ukládat DateTime vždy jako GMT
- michal.lohnisky
- Člen | 64
Ahoj,
používám Nette ~2.2.0. Při ukládání do sloupce typu datetime v MySQL
používám klasicky \Nette\Utils\DateTime. Protože programuju aplikaci, kterou
používají lidé v různých časových pásmech, chci si do DB ukládat čas
v timezoně GMT (jak kdyby bez timezony). Proto bych potřeboval aby se těsně
před uložením / těsně
po výběru z DB konvertovala timezone na/z GMT z/na timezone uživatele
(PHP). Nepřipadá mi jednoduché podědit třídy MySqlDriver
a
ResultSet
a nacpat je zase do Nette.
Jde to nastavit nějak jednoduše na jednom nebo na dvou místech?
- michal.lohnisky
- Člen | 64
Spřízněné téma jsem našel na Hrachově blogu. Tuto funkcionalitu lze implementovat docela jednoduše. Nastavení by probíhalo v neonu:
database:
options:
utc: true #defaultně false, abychom se vyhnuli BC breaku
Při ukládání do DB by se DateTime naklonoval a upravila by se timezona na
UTC ->setTimezone(new \DateTimeZone('UTC'))
, případně na tu,
kterou aktuálně DB používá. Při výběru z DB by se nastavila timezona,
pomocí které jsme do DB ukládali, a upravila by se na právě používanou
v PHP
(->setTimezone(new \DateTimeZone(date_default_timezone_get()))
).
Klidně připravím i pull request, ale nepadají mě dvě místa, kam tuto funkcionalitu umístit:
- Vrstva DB driverů – při ukládání a při výběru
- Vrstva vyšší, společná pro všechny drivery – při ukládání a při výběru
Mohl by se k tomu vyjádřit někdo povolanější (@DavidGrudl @hrach) ?
- MartyIX
- Člen | 217
A co mít prostě všechna data v DB uložená v UTC a používat komponenty, které zobrazí přepočtený čas podle uživatelovi timezony? To je určitě jednodušší (méně bugů) a přijde mi to i čistější než si svazovat mysql driver s uživatelem (Nebudou na to zapomínat ostatní vývojáři? Nepovede to k ošklivým chybám v nějakých připadech? Co třeba cron skripty, kde asi nemáš přihlášeného žádného uživatele? Nezkomplikuje ti to logování chování aplikace?).
Samozřejmě si to ale udělěj, jak ti to bude nejlépe vyhovovat. Já se obecně těmto řešením, která globálně mění chování aplikace s tím, že to ušetří práci, vyhybám. Neosvědčilo se mi to – čas jsem tím víc zabil, než že bych ho ušetřil. :-)
- hrach
- Člen | 1838
@michal.lohnisky: mozno se inspirovat u nextras/dbal, ktery to ma vyreseno. pro ukladani ma dva specialni modifikatory a umoznuje potrebne nastaveni driveru pro zpetnou kompatibility, kdyz nekdo do datetime (tj. bez podpory timezony) ukladal timezonovany udaj.
@MartyIX cela logika kolem tohoto je nesmiru slozita a napsat clanek a knihovnu neni jednoduche. Proc neukladat data v UTC? Protoze (v napr. v MySQL):
- kvuli selectu a funkcim jako TIMEDIFF je treba mit nastavenou potrebnou timezonu
- pokud mam ne-UTC working timezonu, mysql neumi (nema na to syntaxu) ulozit do timestampu datum v jinym timezone, nez je working.
- typy
datetime
aspol. working timezonou vubec ovlivneny vubec. Coz je na jednu stranu vyhoda, jak pises, na druhou stranu db uz neumi ty vychytavky, jako TIMEDIFF, ktery v ne-utc timezonach zohlednuje ty veci, ktery chces (letni casy, historicke souvislosti, etc.).
- michal.lohnisky
- Člen | 64
@hrach mě přivedl na následující myšlenku implementace:
Jak by to mělo fungovat:
database:
options:
connectionTz: 'UTC' # defaultně date_default_timezone_get() nastavená při připojení k DB
applicationTz: null # defaultně NULL (NULL znamená aktuálně nastavená date_default_timezone_get() při provádění query)
Při vytvoření spojení se nastaví DB timezone na
connectionTz
. Ukládání do DB a výběr z DB pak probíhá
následovně (příklad je pro MySQL):
+-------------------------------------------------------------------------+---------------------------------------------------------------------------------------+
ukládání do DB | výběr z DB
+-------------------------------------------------------------------------+---------------------------------------------------------------------------------------+
moje aplikace | Nette | databáze | Nette | moje aplikace
----------------------+-------------------------+--------------------------------------------------+----------------------------------------------+----------------
| | | |
datetime, date, time: | | | |
´´´´´´´´´´´´´´´´´´´´ | | | |
-------> jakákoliv Tz +> modify to connectionTz +----------------------> SAVE ---------------------+> set connectionTz -> modify to applicationTz +--------------->
| | | |
timestamp: | | | |
´´´´´´´´´ | | | |
-------> jakákoliv Tz +> modify to connectionTz +> modify to UTC -> SAVE -> modify to connectionTz +> set connectionTz -> modify to applicationTz +--------------->
| | | |
Připravil jsem zatím tento commit. Pokud by se Vám to líbilo, je třeba doimplementovat tyto věci do ostatních driverů.
Toto řešení je zpětně kompatibilní, stejně jak psal @hrach
ohledně nextras/dbal
(tj. když někdo do datetime ukládal
timezonový údaj). Dále lze při přihlášení uživatele pomocí
date_default_timezone_set()
nastavit jeho vlastní timezonu a dále
pak už jen používat ->format('j.n.Y H:i:s')
bez jakéhokoliv
helperu a vše bude v čase, který uživatel očekává.
Vidí někdo nějaký problém?
Editoval michal.lohnisky (17. 4. 2015 12:36)