PHP i mySQL: Rok 2038 Błąd: Co to jest? Jak to rozwiązać?

116

Myślałem o użyciu TIMESTAMP do przechowywania daty + czasu, ale przeczytałem, że jest na nim ograniczenie do roku 2038. Zamiast zadawać moje pytanie zbiorczo, wolałem podzielić je na małe części, tak aby początkujący użytkownicy mogli je łatwo zrozumieć. Więc moje pytanie (a):

  1. Na czym dokładnie polega problem roku 2038?
  2. Dlaczego tak się dzieje i co się dzieje, gdy się pojawia?
  3. Jak to rozwiązujemy?
  4. Czy są jakieś alternatywne rozwiązania, które nie stwarzają podobnego problemu?
  5. Co możemy zrobić z istniejącymi aplikacjami, które używają TIMESTAMP, aby uniknąć tak zwanego problemu, kiedy on naprawdę wystąpi?

Z góry dziękuję.

Devner
źródło
1
To jeszcze 28 lat. Czy nadal korzystasz z technologii komputerowej z 1982 roku? Mało prawdopodobne. Więc nie martw się, ponieważ do 2038 roku prawdopodobnie nie będzie to już problemem.
Gordon,
24
Gordon, tworzę aplikację do prognozowania. Oszczędzam, ile mam pieniędzy każdego miesiąca i mogę oszacować, kiedy zostanę milionerem. W moim przypadku 28 lat to niewiele i jestem pewien, że nie tylko ja mam ten problem w tej chwili (rozwiązałem go, używając 64-bitowych liczb do reprezentowania znacznika czasu).
Emil Vikström
2
@Emil Używając teraz 64-bitowych liczb całkowitych , znalazłeś łatwe rozwiązanie konkretnego problemu. Nie dotyczy (lub potrzebne) każdemu, ale działa w Twoim przypadku. Chodzi o to, że jeśli PO nie ma konkretnego problemu, takiego jak prognozowanie, może to być interesujący temat, ale nie ma się czym martwić, ponieważ rozwiązanie tego na poziomie ogólnym nie jest kwestią PHP (uwaga na tag). Tylko mój 2c.
Gordon,
1
Do 2038 r. Analiza ciągu „RRRR-MM-DD GG: MM: SS: mmmm…” będzie najtańszą operacją, o jakiej można marzyć. Do 2038 roku wersja 32-bitowa będzie przestarzała. Wątpię, czy uniksowy znacznik czasu, jaki znamy, będzie istniał wtedy, a jeśli tak, nasze 256-bitowe systemy będą obsługiwać daty, które sięgają daleko poza wiek, w którym systemy 4096-bitowe są rozdawane w szczęśliwych posiłkach.
Super Cat
8
Gordona, już 9 lat później. TIMESTAMPS są nadal używane. Nadal korzystamy z technologii sprzed 28 lat. Nazywa się World Wide Web.
sfscs

Odpowiedzi:

149

Oznaczyłem to jako wiki społeczności, więc możesz edytować w wolnym czasie.

Na czym dokładnie polega problem roku 2038?

„Problem z roku 2038 (znany również jako Unix Millennium Bug, Y2K38 przez analogię do problemu Y2K) może spowodować awarię niektórych programów komputerowych przed rokiem 2038 lub w tym roku. Problem dotyczy wszystkich programów i systemów przechowujących czas systemowy jako podpisany 32 -bitowa liczba całkowita i interpretuje tę liczbę jako liczbę sekund od godziny 00:00:00 czasu UTC 1 stycznia 1970 r. "


Dlaczego tak się dzieje i co się dzieje, gdy się pojawia?

Czasy poza godziną 03:14:07 czasu UTC we wtorek, 19 stycznia 2038 r. Będą „zawijać się” i przechowywane wewnętrznie jako liczba ujemna, którą systemy te będą interpretować jako czas z 13 grudnia 1901 r., A nie z 2038 r. Wynika to z fakt, że liczba sekund od epoki UNIX (1 stycznia 1970 00:00:00 GMT) przekroczy maksymalną wartość komputera dla 32-bitowej liczby całkowitej ze znakiem.


Jak to rozwiązujemy?

  • Używaj długich typów danych (wystarczy 64 bity)
  • W przypadku MySQL (lub MariaDB), jeśli nie potrzebujesz informacji o czasie, rozważ użycie DATEtypu kolumny. Jeśli potrzebujesz większej dokładności, użyj DATETIMEzamiast TIMESTAMP. Pamiętaj, że DATETIMEkolumny nie przechowują informacji o strefie czasowej, więc Twoja aplikacja będzie musiała wiedzieć, która strefa czasowa była używana.
  • Inne Możliwe rozwiązania opisane w Wikipedii
  • Poczekaj, aż deweloperzy MySQL naprawią ten błąd zgłoszony ponad dekadę temu.

Czy są jakieś alternatywne rozwiązania, które nie stwarzają podobnego problemu?

W miarę możliwości staraj się używać dużych typów do przechowywania dat w bazach danych: wystarczy 64-bity - długi typ w GNU C i POSIX / SuS lub sprintf('%u'...)w PHP lub rozszerzeniu BCmath.


Jakie są potencjalnie psujące przypadki użycia, mimo że nie jesteśmy jeszcze w 2038 roku?

Tak więc MySQL DATETIME ma zakres 1000-9999, ale TIMESTAMP ma tylko zakres 1970-2038. Jeśli Twój system przechowuje daty urodzenia, przyszłe daty przyszłe (np. 30-letnie kredyty hipoteczne) lub podobne, już napotkasz ten błąd. Ponownie, nie używaj TIMESTAMP, jeśli może to stanowić problem.


Co możemy zrobić z istniejącymi aplikacjami, które używają TIMESTAMP, aby uniknąć tak zwanego problemu, kiedy on naprawdę wystąpi?

Niewiele aplikacji PHP będzie nadal dostępnych w 2038 r., Choć trudno to przewidzieć, ponieważ internet nie jest jeszcze starszą platformą.

Oto proces zmieniania kolumny tabeli bazy danych do konwersji TIMESTAMPdo DATETIME. Zaczyna się od utworzenia tymczasowej kolumny:

# rename the old TIMESTAMP field
ALTER TABLE `myTable` CHANGE `myTimestamp` `temp_myTimestamp` int(11) NOT NULL;

# create a new DATETIME column of the same name as your old column
ALTER TABLE `myTable` ADD `myTimestamp` DATETIME NOT NULL;

# update all rows by populating your new DATETIME field
UPDATE `myTable` SET `myTimestamp` = FROM_UNIXTIME(temp_myTimestamp);

# remove the temporary column
ALTER TABLE `myTable` DROP `temp_myTimestamp`

Zasoby

Corey Ballou
źródło
2
Niesamowite. Dla mnie twoja odpowiedź jest najbardziej kompletna. Dzięki wielkie.
Devner,
7
W MySQL, jeśli przyszła wersja zmieni podstawowy typ danych pamięci TIMESTAMP na 64-bitowy, nie ma potrzeby zmiany kodu na DATETIME, prawda? Po prostu nie uważam, że zniechęcanie kogokolwiek do korzystania z TIMESTAMP jest właściwe, ponieważ ten typ danych ma swój cel.
pixelfreak
1
Wydaje się, że podpisane pole BIGINT MySQL będzie działać dobrze do przechowywania znaczników czasu i przeprowadziłem lokalne testy na MySQL 5.5, które potwierdzają, że działa. Oczywiście użycie podpisu byłoby lepsze niż niepodpisane, ponieważ możesz również reprezentować daty z przeszłości. Czy jest jakiś powód, aby nie używać BIGINT do sygnatur czasowych?
zuallauz
Należy zauważyć, że MySQL ma automatyczne ustawienie tylko bieżącej daty / godziny (CURRENT_TIMESTAMP) dla pól znaczników czasu. Miejmy nadzieję, że ta funkcja w końcu zostanie przeniesiona na wszystkie typy dat.
Ray
5
To absolutnie absurdalne, że MySQL (i MariaDB) nie używają 64-bitowych liczb całkowitych do przechowywania znaczników czasu w systemach 64-bitowych. Używanie DATETIME nie jest odpowiednim rozwiązaniem, ponieważ nie mamy pojęcia o strefie czasowej. Zostało to zgłoszone już w 2005 roku , ale nadal nie ma dostępnych poprawek.
Mike
14

Używając znaczników czasu UNIX do przechowywania dat, w rzeczywistości używasz 32-bitowych liczb całkowitych, które przechowują liczbę sekund od 1970-01-01; zobacz Unix Time

Ta 32-bitowa liczba przepełni się w 2038 roku. Na tym polega problem w 2038 roku.


Aby rozwiązać ten problem, nie możesz używać 32-bitowego znacznika czasu UNIX do przechowywania dat - co oznacza, że ​​podczas korzystania z MySQL nie powinieneś używać TIMESTAMP, ale DATETIME(patrz 10.3.1. Typy DATETIME, DATE i TIMESTAMP ):

DATETIMETyp jest używany, gdy trzeba wartości, które zawierają zarówno datę i informacje o czasie. Obsługiwany zakres jest '1000-01-01 00:00:00'do '9999-12-31 23:59:59'.

Typ TIMESTAMPdanych ma zakres od '1970-01-01 00:00:01'UTC do '2038-01-19 03:14:07'UTC.


(Prawdopodobnie) Najlepszą rzeczą jaką możesz zrobić dla aplikacji, aby uniknąć / fix, że problemem jest nie używać TIMESTAMP, ale DATETIMEdla kolumn, które muszą zawierać datę, które nie są między 1970 a 2038 r.

Jednak jedna mała uwaga: istnieje bardzo duże prawdopodobieństwo (statystycznie rzecz biorąc), że Twoja aplikacja zostanie przepisana kilka razy przed 2038 rokiem ^^ Więc może, jeśli nie będziesz musiał zajmować się datami w przyszłości , nie będziesz musiał zajmować się tym problemem z aktualną wersją Twojej aplikacji ...

Pascal MARTIN
źródło
1
+1 dla informacji. Podjęto próbę dostosowania najlepszych praktyk od samego początku, aby nie trzeba było później martwić się o problem. Więc chociaż na razie 2038 może nie brzmieć jak problem, chcę po prostu postępować zgodnie z najlepszymi praktykami i postępować zgodnie z nimi dla każdej aplikacji, którą tworzę, od samego początku. Mam nadzieję, że to ma sens.
Devner,
Tak, to ma sens, rozumiem twój punkt widzenia. Ale „najlepsza praktyka” może również oznaczać „jaka jest odpowiedź na potrzebę” - jeśli wiesz, że znacznik czasu wystarczy, nie ma potrzeby używania daty i godziny (na przykład wymagają one więcej pamięci do przechowywania; i może to mieć znaczenie, jeśli masz miliony rekordów) ;; Mam kilka aplikacji, w których używam znacznika czasu dla niektórych kolumn (na przykład kolumny zawierające aktualną datę) i datetime dla niektórych (na przykład kolumny zawierające przeszłe / przyszłe daty)
Pascal MARTIN
Widzę, że twoja procedura również ma sens i działa dobrze, zwłaszcza jeśli chodzi o przestrzeń magazynową. Jeśli wszystko w porządku, czy mogę zapytać, jakiej struktury i długości używasz ogólnie dla kolumny TIMESTAMP w swoich aplikacjach? Dzięki.
Devner
Cóż, kiedy chcę użyć TIMESTAMP, to jest to dla / ponieważ moje dane "data / czas" mieszczą się w latach 1970-2038 - więc używam typu danych MySQL TIMESTAMP.
Pascal MARTIN
Dzięki za informację. Z Twojej odpowiedzi wynika, że ​​wystarczy zadeklarować kolumnę jako TIMESTAMP i nie musimy podawać jej żadnej długości (w przeciwieństwie do i int lub var, gdzie podajemy długość). Czy mam rację?
Devner,
7

Szybkie wyszukiwanie w Google załatwi sprawę: problem z rokiem 2038

  1. Problem z roku 2038 (znany również jako Unix Millennium Bug, Y2K38 przez analogię do problemu Y2K) może powodować awarie niektórych programów komputerowych przed rokiem 2038 lub w nim
  2. Problem dotyczy wszystkich programów i systemów, które przechowują czas systemowy jako 32-bitową liczbę całkowitą ze znakiem i interpretują tę liczbę jako liczbę sekund od godziny 00:00:00 czasu UTC 1 stycznia 1970 r. Ostatni czas, który można przedstawić w ten sposób to 03:14:07 UTC we wtorek, 19 stycznia 2038 r. Czasy poza tą chwilą będą „zawijane” i przechowywane wewnętrznie jako liczba ujemna, którą te systemy będą interpretować jako datę w 1901 r., a nie w 2038 r.
  3. Nie ma łatwego rozwiązania tego problemu dla istniejących kombinacji CPU / OS, istniejących systemów plików lub istniejących formatów danych binarnych
Rubens Farias
źródło
2

http://en.wikipedia.org/wiki/Year_2038_problem zawiera większość szczegółów

W podsumowaniu:

1) + 2) Problem polega na tym, że wiele systemów przechowuje informacje o dacie w postaci 32-bitowej liczby int ze znakiem równej liczbie sekund od 1 stycznia 1970 roku. Ostatnią datą, którą można przechowywać w ten sposób, jest 03:14:07 czasu UTC we wtorek 19 stycznia 2038 r. Kiedy to się stanie, int zostanie „zawinięty” i zostanie zapisany jako liczba ujemna, która zostanie zinterpretowana jako data w 1901 roku. Co dokładnie się wtedy stanie, różni się w zależności od systemu, ale wystarczy powiedzieć, że prawdopodobnie nie będzie to dobre dla żadnego z nich!

W przypadku systemów, które przechowują tylko daty z przeszłości, myślę, że nie musisz się martwić przez chwilę! Głównym problemem są systemy, które działają z datami w przyszłości. Jeśli Twój system musi działać z datami za 28 lat w przyszłości, powinieneś zacząć się martwić już teraz!

3) Użyj jednego z alternatywnych dostępnych formatów daty lub przejdź do systemu 64-bitowego i użyj 64-bitowych liczb całkowitych. Lub w przypadku baz danych użyj alternatywnego formatu znacznika czasu (np. Dla MySQL użyj DATETIME)

4) Zobacz 3!

5) Zobacz 4 !!! ;)

Addsy
źródło
Twoje punkty 4 i 5 przypominają mi „Call by Reference”. To wywołało uśmiech na mojej twarzy. Dzięki i +1 za to samo.
Devner,
1

Bracia, jeśli potrzebujesz używać PHP do wyświetlania znaczników czasu, jest to NAJLEPSZE rozwiązanie PHP bez zmiany formatu UNIX_TIMESTAMP.

Użyj funkcji custom_date (). Wewnątrz użyj DateTime. Oto rozwiązanie DateTime .

Tak długo, jak masz UNSIGNED BIGINT (8) jako swoje znaczniki czasu w bazie danych. O ile masz PHP 5.2.0 ++

Dexter
źródło
-1

Ponieważ nie chciałem niczego aktualizować, poprosiłem mój backend (MSSQL) o wykonanie tej pracy zamiast PHP!

$qry = "select DATEADD(month, 1, :date) next_date ";
$rs_tmp = $pdo->prepare($qry);
$rs_tmp->bindValue(":date", '2038/01/15');
$rs_tmp->execute();
$row_tmp = $rs_tmp->fetch(PDO::FETCH_ASSOC);

echo $row_tmp['next_date'];

Może nie być skutecznym sposobem, ale działa.

Venkatesh GS Rao
źródło