Problem roku 2038 (nazywany również błędem Y2K38) odnosi się do problemu, który niektóre systemy komputerowe mogą napotkać, gdy mają do czynienia z czasami po 2038-01-19 03:14:07.
Wiele systemów komputerowych, takich jak systemy Unix i Unix, nie oblicza czasu za pomocą kalendarza gregoriańskiego. Obliczają czas jako liczbę sekund od 1 stycznia 1970 roku. Dlatego w tych systemach czas jest przedstawiany jako duża liczba (tj. liczba sekund, które upłynęły od 1970-01-01 00:00:00). Jest to zwykle określane jako czas epoki, czas uniksowy, czas epoki uniksowej lub czas POSIX. Kiedy to piszę, czas uniksowy to 1560913841. I kiedy piszę następną linię, czas uniksowy wzrósł do 1560913879.
Problem 2038 jest spowodowany faktem, że wiele systemów przechowuje tę liczbę jako 32-bitową binarną liczbę całkowitą ze znakiem. Zakres 32-bitowej liczby całkowitej ze znakiem wynosi od -2 147 483 648 do 2 147 483 647. Oznacza to, że ostatni czas Epoki, który można przedstawić, to 2147483647. Nastąpi to o 03:14:07 we wtorek 19 stycznia 2038 r.
Następnie wynik będzie w dużej mierze zależał od systemu. W wielu systemach wystąpi przepełnienie liczby całkowitej, a późniejsze czasy zostaną zawinięte i będą przechowywane wewnętrznie jako liczba ujemna. Skutek jest taki, że sekundę później, czas zostanie zinterpretowany jako 13 grudnia 1901, a nie 19 stycznia 2038.
Jednak możesz również uzyskać różne wyniki, w zależności od używanej aplikacji. Nawet jeśli twój system operacyjny nie ma problemu, twój własny kod może nadal mieć problem. Na przykład, jeśli napisałeś niestandardowy kod zwracający czas uniksowy i przechowujesz go w podpisanej 4-bajtowej liczbie całkowitej, będziesz mieć problemy. W takich przypadkach przepisanie kodu na 8-bajtową liczbę całkowitą może być wszystkim, co musisz zrobić.
Biorąc pod uwagę, że ta strona internetowa dotyczy baz danych, oto kilka przykładów baz danych.
Przykład 1 – MySQL
W MySQL TIMESTAMP
typ danych obsługuje daty/czasy od ‘1970-01-01 00:00:01.000000’ UTC do ‘2038-01-19 03:14:07.999999’. Dlatego można powiedzieć, że każda baza danych korzystająca z tego typu danych ma błąd Y2K38.
MySQL ma również wbudowaną funkcję o nazwie UNIX_TIMESTAMP()
który, jak można się spodziewać, zwraca uniksowy znacznik czasu.
UNIX_TIMESTAMP()
funkcja przyjmuje opcjonalny argument, który pozwala określić datę, która ma być używana dla czasu uniksowego (tj. liczba sekund od ‘1970-01-01 00:00:00’ UTC do podanego czasu). Prawidłowy zakres wartości argumentów jest taki sam jak dla TIMESTAMP
typ danych, od ‘1970-01-01 00:00:01.0000’ UTC do ‘2038-01-19 03:14:07.999999’ UTC. Jeśli przekażesz do tej funkcji datę spoza zakresu, zwróci ona 0
.
Oto, co się stanie, jeśli spróbujesz użyć tej funkcji do zwrócenia czasu uniksowego z daty po „2038-01-19 03:14:07.999999”:
SELECT UNIX_TIMESTAMP('2038-01-20') Result;
Wynik:
+--------+ | Result | +--------+ | 0 | +--------+
Otrzymujemy 0
ponieważ argument daty jest poza obsługiwanym zakresem.
Powiązany raport o błędzie został zgłoszony zespołowi MySQL w 2005 roku (chociaż niektóre szczegóły wydają się być inne), a w chwili pisania tego tekstu nadal nie został rozwiązany.
Podobny problem został również podniesiony w celu rozwiązania ograniczeń związanych z TIMESTAMP
typ danych, który również nie został jeszcze rozwiązany.
Przykład 2 – Serwer SQL
SQL Server nie ma obecnie odpowiednika UNIX_TIMESTAMP
MySQLa funkcjonować. Dlatego, jeśli chcesz zwrócić czas Epoki, musisz zrobić coś takiego:
SELECT DATEDIFF(SECOND,'1970-01-01', GETUTCDATE());
Jest to dobre dla dat sprzed 2038 roku. Po tej dacie będziesz mieć problemy, ponieważ DATEDIFF()
funkcja zwraca wynik jako int typ danych. int typ danych ma zakres od -2^31 (-2147483648) do 2^31-1 (2147483647).
Oto co się stanie, jeśli spróbuję zwrócić czas Epoki później niż „2038-01-19 03:14:07”:
SELECT DATEDIFF(SECOND,'1970-01-01', '2038-01-19 03:14:08') AS 'Result';
Wynik:
The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.
Na szczęście istnieje również funkcja DATEDIFF_BIG()
funkcja, która robi dokładnie to samo, z wyjątkiem tego, że zwraca wynik jako bigint typ danych.
Aby rozwiązać ten problem, możemy przepisać poprzedni przykład na następujący:
SELECT DATEDIFF_BIG(SECOND,'1970-01-01 00:00:00', '2038-01-19 03:14:08') AS 'Result';
Wynik:
+------------+ | Result | |------------| | 2147483648 | +------------+
Wielki typ danych używa 8 bajtów (w przeciwieństwie do 4 bajtów dla int ), więc musisz zdecydować, czy przełączyć się na DATEDIFF_BIG()
teraz albo później. Jeśli Twoja aplikacja dotyczy przyszłych dat, rozsądne może być zrobienie tego wcześniej niż później.