Ta odpowiedź została zaktualizowana, aby uwzględnić nagrodę. Oryginalna, nieedytowana odpowiedź znajduje się pod linią.
Prawie wszystkie pytania dodane przez właściciela bounty dotyczą tego, w jaki sposób daty i godziny MySQL i PHP powinny współdziałać w kontekście stref czasowych.
MySQL wciąż jest żałosny obsługa stref czasowych , co oznacza, że inteligencja musi być po stronie PHP.
- Ustaw swoją strefę czasową połączenia w MySQL do UTC, jak udokumentowano w powyższym linku. Spowoduje to, że wszystkie daty i godziny obsługiwane przez MySQL, w tym
NOW()
, które należy traktować rozsądnie. - Zawsze używaj
DATETIME
, nigdy nie używajTIMESTAMP
chyba że bardzo wyraźnie wymagasz specjalnego zachowania wTIMESTAMP
. Jest to mniej bolesne niż kiedyś.- Jest w porządku do przechowywania czasu epoki Uniksa jako liczby całkowitej, jeśli masz do, na przykład do celów odziedziczonych. Epoka to UTC.
- Preferowany format daty i godziny MySQL jest tworzony przy użyciu ciągu formatu daty PHP
Y-m-d H:i:s
- Konwertuj wszystkie Daty PHP do UTC podczas przechowywania ich w MySQL, co jest trywialną rzeczą, jak opisano poniżej
- Datetimes zwrócone z MySQL można bezpiecznie przekazać do konstruktora PHP DateTime. Pamiętaj, aby przejść również w strefie czasowej UTC!
- Konwertuj PHP DateTime na lokalną strefę czasową użytkownika na echo , nie wcześniej. Na szczęście porównanie DateTime i matematyka z innymi DateTimes uwzględni strefę czasową, w której każda z nich się znajduje.
- Nadal spełniasz kaprysy bazy danych DST dostarczanej z PHP. Aktualizuj swoje poprawki PHP i OS! Utrzymuj MySQL w błogim stanie UTC, aby usunąć jedną potencjalną irytację czasu letniego.
To dotyczy większości punktów.
Ostatnia rzecz to doozy:
- Co powinien zrobić ktoś, kto wcześniej wstawił dane (np. za pomocą
NOW()
) bez martwienia się o strefę czasową, aby upewnić się, że wszystko pozostaje spójne?
To prawdziwa irytacja. Jedna z innych odpowiedzi wskazywała na CONVERT_TZ
, chociaż osobiście zrobiłbym to, przeskakując między strefami czasowymi serwera i UTC podczas wybierania i aktualizacji, ponieważ jestem taki hardkorowy.
aplikacja powinna również być w stanie odpowiednio ustawić/wybrać czas letni dla każdego użytkownika.
Nie musisz i nie powinieneś zrób to w epoce nowożytnej.
Nowoczesne wersje PHP mają DateTimeZone klasa, która zawiera możliwość listy nazwanych stref czasowych . Nazwane strefy czasowe pozwalają użytkownikowi wybrać swoją rzeczywistą lokalizację, a system automatycznie określić swoje zasady czasu letniego na podstawie tej lokalizacji.
Możesz połączyć DateTimeZone z DateTime dla prostej, ale potężnej funkcjonalności. Możesz po prostu przechowywać i używać wszystko Twoich znaczników czasu w UTC domyślnie i przekonwertuj je na wyświetlaną strefę czasową użytkownika.
// 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
Dzięki tej technice system automatycznie wybierz prawidłowe ustawienia czasu letniego dla użytkownika, nie pytając użytkownika, czy aktualnie pracuje w czasie letnim.
Możesz użyć podobnej metody do renderowania menu wyboru. Możesz stale ponownie przypisywać strefę czasową dla pojedynczego obiektu DateTime. Na przykład ten kod wyświetli listę stref i ich aktualne godziny, w tej chwili:
$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";
}
Możesz znacznie uprościć proces selekcji, używając magii po stronie klienta. JavaScript ma niekonsekwentny, ale funkcjonalny Klasa daty ze standardową metodą uzyskania przesunięcia UTC w minutach . Możesz to wykorzystać, aby zawęzić listę prawdopodobnych stref czasowych, przy ślepym założeniu, że zegar użytkownika jest prawidłowy.
Porównajmy tę metodę z robieniem tego samemu. Trzeba by właściwie wykonać obliczenia na datach za każdym razem manipulujesz datą i godziną, a także narzucasz użytkownikowi wybór, o który tak naprawdę nie będzie dbał. To nie jest tylko nieoptymalne, to szaleństwo nietoperza. Zmuszanie użytkowników do zaznaczenia, kiedy chcą wsparcia czasu letniego, jest proszeniem o kłopoty i zamieszanie.
Co więcej, jeśli chcesz użyć do tego nowoczesnego frameworka PHP DateTime i DateTimeZone, musisz użyj przestarzałego Etc/GMT...
ciągi stref czasowych
zamiast nazwanych stref czasowych. Te nazwy stref mogą zostać usunięte z przyszłych wersji PHP, więc nie byłoby to rozsądne. Mówię to wszystko z doświadczenia.
tl;dr :Korzystaj z nowoczesnego zestawu narzędzi, oszczędź sobie koszmarów matematyki na randce. Zaprezentuj użytkownikowi listę nazwanych strefy czasowe. Przechowuj swoje daty w UTC , na który w żaden sposób nie ma wpływu czas letni. Konwertuj daty i godziny na wybraną przez użytkownika nazwaną strefę czasową na wyświetlaczu , nie wcześniej.
Zgodnie z żądaniem, oto pętla dostępnych stref czasowych, wyświetlająca ich przesunięcie GMT w minutach. Wybrałem tutaj minuty, aby zademonstrować niefortunny fakt:nie wszystkie przesunięcia są w pełnych godzinach! Niektórzy faktycznie przełączają się pół godziny do przodu podczas czasu letniego zamiast całej godziny. Wynikowe przesunięcie w minutach powinno pasuje do 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";
}