Tato odpověď byla aktualizována, aby odpovídala odměně. Původní, neupravená odpověď je pod čarou.
Téměř všechny otázky, které přidal vlastník odměny, se týkají toho, jak by měly datatime MySQL a PHP interagovat v kontextu časových pásem.
MySQL je stále žalostné podpora časového pásma , což znamená, že inteligence musí být na straně PHP.
- Nastavte časové pásmo připojení MySQL do UTC, jak je zdokumentováno v odkazu výše. To způsobí, že všechna data a časy zpracovávají MySQL, včetně
NOW()
, je třeba s nimi zacházet rozumně. - Vždy používejte
DATETIME
, nikdy nepoužívejteTIMESTAMP
pokud výslovně nepožadujete zvláštní chování vTIMESTAMP
. Je to méně bolestivé než dříve.- Je to v pořádku ukládat čas Unixové epochy jako celé číslo, pokud máte například pro účely dědictví. Epochou je UTC.
- Preferovaný formát data a času MySQL je vytvořen pomocí řetězce formátu data PHP
Y-m-d H:i:s
- Převést vše PHP datatimes do UTC při jejich ukládání v MySQL, což je triviální věc, jak je uvedeno níže
- Datumy vrácené z MySQL lze bezpečně předat konstruktoru PHP DateTime. Nezapomeňte zadat také časové pásmo UTC!
- Převeďte PHP DateTime na místní časové pásmo uživatele on echo , ne dříve. Naštěstí porovnání DateTime a matematika s jinými DateTimes vezme v úvahu časové pásmo, ve kterém se každý nachází.
- Stále jste v souladu s rozmary databáze DST poskytované s PHP. Udržujte své opravy PHP a OS aktuální! Udržujte MySQL v blaženém stavu UTC, abyste odstranili jednu potenciální nepříjemnost DST.
To řeší nejvíce bodů.
Poslední věc je blbost:
- Co by měl někdo udělat, pokud již dříve vložil data (např. pomocí
NOW()
), aniž byste se museli starat o časové pásmo, abyste se ujistili, že vše zůstane konzistentní?
To je skutečná otrava. Jedna z dalších odpovědí poukázala na CONVERT_TZ
, i když osobně bych to udělal přeskakováním mezi nativním serverem a časovým pásmem UTC během výběru a aktualizací, protože jsem takový hardcore.
aplikace by také měla být schopna SET/Volba DST sama pro každého uživatele.
Nemusíte a neměli byste udělejte to v moderní době.
Moderní verze PHP mají DateTimeZone třídy, která zahrnuje možnost seznam pojmenovaných časových pásem . Pojmenovaná časová pásma umožňují uživateli vybrat si svou skutečnou polohu a mají systém automaticky určit jejich pravidla DST na základě tohoto umístění.
DateTimeZone můžete kombinovat s DateTime pro některé jednoduché, ale výkonné funkce. Můžete jednoduše uložit a používat vše vašich časových razítek v UTC ve výchozím nastavení a převést je na zobrazené časové pásmo uživatele.
// UTC default
date_default_timezone_set('UTC');
// Note the lack of time zone specified with this timestamp.
$nowish = new DateTime('2011-04-23 21:44:00');
echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 21:44:00
// Let's pretend we're on the US west coast.
// This will be PDT right now, UTC-7
$la = new DateTimeZone('America/Los_Angeles');
// Update the DateTime's timezone...
$nowish->setTimeZone($la);
// and show the result
echo $nowish->format('Y-m-d H:i:s'); // 2011-04-23 14:44:00
Při použití této techniky bude systém automaticky vyberte správná nastavení letního času pro uživatele, aniž byste se ho zeptali, zda se aktuálně nachází v letním čase.
Podobnou metodu můžete použít k vykreslení nabídky výběru. Časové pásmo můžete neustále měnit pro jeden objekt DateTime. Například tento kód vypíše zóny a jejich aktuální časy v tuto chvíli:
$dt = new DateTime('now', new DateTimeZone('UTC'));
foreach(DateTimeZone::listIdentifiers() as $tz) {
$dt->setTimeZone(new DateTimeZone($tz));
echo $tz, ': ', $dt->format('Y-m-d H:i:s'), "\n";
}
Proces výběru můžete výrazně zjednodušit použitím některých kouzel na straně klienta. Javascript má nepatrný, ale funkční Třída data se standardní metodou k postupu UTC během několika minut . Můžete to použít k zúžení seznamu pravděpodobných časových pásem za slepého předpokladu, že hodiny uživatele jsou správné.
Porovnejme tuto metodu s tím, že to uděláte sami. Ve skutečnosti byste museli vypočítat datum pokaždé manipulujete s datem a časem a navíc podsouváte uživateli volbu, o kterou se ve skutečnosti nebude starat. To není jen suboptimální, je to šílené bat-guano. Nutit uživatele, aby uvedli, kdy chtějí podporu DST, si žádá potíže a zmatky.
Dále, pokud byste k tomu chtěli použít moderní PHP DateTime a DateTimeZone framework, museli byste použijte zastaralé Etc/GMT...
řetězce časového pásma
místo pojmenovaných časových pásem. Tyto názvy zón mohou být z budoucích verzí PHP odstraněny, takže by nebylo rozumné to dělat. To vše říkám ze zkušenosti.
tl;dr :Použijte moderní sadu nástrojů, ušetříte si hrůzy datovací matematiky. Nabídněte uživateli seznam pojmenovaných časová pásma. Uložte svá data v UTC , které nebude letním časem nijak ovlivněno. Převeďte datum a čas na uživatelem vybrané pojmenované časové pásmo na displeji , ne dříve.
Jak bylo požadováno, zde je smyčka přes dostupná časová pásma zobrazující jejich posun vůči GMT v minutách. Vybral jsem zde minuty, abych demonstroval nešťastnou skutečnost:ne všechny offsety jsou v celých hodinách! Někteří ve skutečnosti přepínají během letního času o půl hodiny dopředu místo celé hodiny. Výsledný posun v minutách by měl shodují se s hodnotou Date.getTimezoneOffset
Javascriptu .
$utc = new DateTimeZone('UTC');
$dt = new DateTime('now', $utc);
foreach(DateTimeZone::listIdentifiers() as $tz) {
$local = new DateTimeZone($tz);
$dt->setTimeZone($local);
$offset = $local->getOffset($dt); // Yeah, really.
echo $tz, ': ',
$dt->format('Y-m-d H:i:s'),
', offset = ',
($offset / 60),
" minutes\n";
}