År 2038-problemet (även kallat Y2K38-felet) hänvisar till ett problem som vissa datorsystem kan stöta på när de hanterar tider efter 2038-01-19 03:14:07.
Många datorsystem, som Unix och Unix-baserade system, beräknar inte tid med den gregorianska kalendern. De beräknar tiden som antalet sekunder sedan 1 januari 1970. Därför, i dessa system, representeras tiden som ett stort tal (dvs. antalet sekunder som passerat sedan 1970-01-01 00:00:00). Detta kallas vanligtvis epoktid, Unixtid, Unix epoktid eller POSIX-tid. När jag skriver detta är Unix-tiden 1560913841. Och när jag skriver nästa rad har Unix-tiden ökat till 1560913879.
2038-problemet orsakas av det faktum att många system lagrar detta nummer som ett signerat 32-bitars binärt heltal. Intervallet för ett signerat 32-bitars heltal är -2 147 483 648 till 2 147 483 647. Detta betyder att den senaste epoktiden som kan representeras är 2147483647. Detta kommer att ske kl. 03:14:07 tisdagen den 19 januari 2038.
Därefter kommer resultatet till stor del att bero på systemet. I många system kommer ett heltalsspill att inträffa, och senare tider kommer att lindas runt och lagras internt som ett negativt tal. Resultatet är att en sekund senare kommer tiden att tolkas som den 13 december 1901 snarare än den 19 januari 2038.
Men du kan också få varierande resultat, beroende på vilken applikation som används. Även om ditt operativsystem inte har några problem, kan din egen kod fortfarande ha ett problem. Till exempel, om du har skrivit anpassad kod för att returnera Unix-tid, och du lagrar den i ett signerat 4-byte heltal, kommer du att få problem. I sådana fall kan det vara allt du behöver göra att skriva om koden för att använda ett heltal på 8 byte.
Eftersom denna webbplats handlar om databaser, är här några databasexempel.
Exempel 1 – MySQL
I MySQL, TIMESTAMP
datatypen stöder datum/tider från '1970-01-01 00:00:01.000000' UTC till '2038-01-19 03:14:07.999999'. Därför kan man säga att vilken databas som helst som använder denna datatyp har en Y2K38-bugg.
MySQL har också en inbyggd funktion som heter UNIX_TIMESTAMP()
som, som du kan förvänta dig, returnerar Unix-tidsstämpeln.
UNIX_TIMESTAMP()
funktion accepterar ett valfritt argument som låter dig ange ett datum att använda för Unix-tiden (dvs. antalet sekunder från '1970-01-01 00:00:00' UTC till den tid du anger). Det giltiga intervallet av argumentvärden är detsamma som för TIMESTAMP
datatyp, som är '1970-01-01 00:00:01.000000' UTC till '2038-01-19 03:14:07.999999' UTC. Om du skickar ett datum utanför intervallet till den här funktionen returnerar det 0
.
Här är vad som händer om du försöker använda den här funktionen för att returnera Unix-tiden från ett datum efter '2038-01-19 03:14:07.999999':
SELECT UNIX_TIMESTAMP('2038-01-20') Result;
Resultat:
+--------+ | Result | +--------+ | 0 | +--------+
Vi får 0
eftersom datumargumentet ligger utanför det stödda intervallet.
En relaterad felrapport togs upp för MySQL-teamet 2005 (även om vissa detaljer verkar vara annorlunda), och när detta skrivs har den fortfarande inte behandlats.
Ett liknande problem togs också upp för att ta itu med begränsningarna med TIMESTAMP
datatyp, som också ännu inte har behandlats.
Exempel 2 – SQL Server
SQL Server har för närvarande inte en motsvarighet till MySQL:s UNIX_TIMESTAMP
fungera. Därför, om du behöver återställa Epoch time, måste du göra något så här:
SELECT DATEDIFF(SECOND,'1970-01-01', GETUTCDATE());
Detta är bra för datum före 2038-problemet. Efter det datumet har du problem eftersom DATEDIFF()
funktion returnerar resultatet som en int data typ. int datatypen har ett intervall på -2^31 (-2,147,483,648) till 2^31-1 (2,147,483,647).
Det här är vad som händer om jag försöker returnera Epoch time senare än '2038-01-19 03:14:07':
SELECT DATEDIFF(SECOND,'1970-01-01', '2038-01-19 03:14:08') AS 'Result';
Resultat:
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.
Lyckligtvis finns det också en DATEDIFF_BIG()
funktion, som gör exakt samma sak, förutom att den returnerar resultatet som en bigint datatyp.
Så vi kan skriva om det föregående exemplet till följande för att lösa problemet:
SELECT DATEDIFF_BIG(SECOND,'1970-01-01 00:00:00', '2038-01-19 03:14:08') AS 'Result';
Resultat:
+------------+ | Result | |------------| | 2147483648 | +------------+
Den stora datatypen använder 8 byte (till skillnad från 4 byte för en int ), så du måste bestämma om du vill byta till DATEDIFF_BIG()
nu eller senare. Om din ansökan handlar om framtida datum kan det vara klokt att göra det tidigare än senare.