År 2038-problemet (også omtalt som Y2K38-fejlen) refererer til et problem, som nogle computersystemer kan støde på, når de håndterer tider, der er over 2038-01-19 03:14:07.
Mange computersystemer, såsom Unix og Unix-baserede systemer, beregner ikke tid ved hjælp af den gregorianske kalender. De beregner tid som antallet af sekunder siden 1. januar 1970. Derfor er tiden i disse systemer repræsenteret som et stort tal (dvs. antallet af sekunder, der er gået siden 1970-01-01 00:00:00). Dette omtales typisk som epoketid, Unix-tid, Unix-epoketid eller POSIX-tid. Mens jeg skriver dette, er Unix-tiden 1560913841. Og mens jeg skriver denne næste linje, er Unix-tiden steget til 1560913879.
2038-problemet er forårsaget af det faktum, at mange systemer gemmer dette nummer som et signeret 32-bit binært heltal. Området for et fortegnet 32-bit heltal er -2.147.483.648 til 2.147.483.647. Det betyder, at den seneste epoketid, der kan repræsenteres, er 2147483647. Dette vil ske kl. 03:14:07 tirsdag den 19. januar 2038.
Herefter vil resultatet i høj grad afhænge af systemet. I mange systemer vil der opstå et heltalsoverløb, og eventuelle senere tidspunkter vil pakkes rundt og blive gemt internt som et negativt tal. Resultatet er, at et sekund senere vil tiden blive fortolket som værende den 13. december 1901 i stedet for den 19. januar 2038.
Du kan dog også ende med forskellige resultater, afhængigt af den applikation, der bruges. Selvom dit operativsystem ikke har noget problem, kan din egen kode stadig have et problem. For eksempel, hvis du har skrevet tilpasset kode for at returnere Unix-tid, og du gemmer den i et signeret 4 byte heltal, vil du have problemer. I sådanne tilfælde kan det være alt hvad du behøver at gøre at omskrive koden til at bruge et 8 byte heltal.
Da denne hjemmeside udelukkende handler om databaser, er her nogle databaseeksempler.
Eksempel 1 – MySQL
I MySQL er TIMESTAMP
datatypen understøtter datoer/klokkeslæt fra '1970-01-01 00:00:01.000000' UTC til '2038-01-19 03:14:07.999999'. Derfor kan du sige, at enhver database, der bruger denne datatype, har en Y2K38-fejl.
MySQL har også en indbygget funktion kaldet UNIX_TIMESTAMP()
som, som du kunne forvente, returnerer Unix-tidsstemplet.
UNIX_TIMESTAMP()
funktion accepterer et valgfrit argument, der giver dig mulighed for at angive en dato, der skal bruges til Unix-tiden (dvs. antallet af sekunder fra '1970-01-01 00:00:00' UTC til det tidspunkt, du angiver). Det gyldige interval af argumentværdier er det samme som for TIMESTAMP
datatype, som er '1970-01-01 00:00:01.000000' UTC til '2038-01-19 03:14:07.999999' UTC. Hvis du sender en dato uden for intervallet til denne funktion, returnerer den 0
.
Her er, hvad der sker, hvis du forsøger at bruge denne funktion til at returnere Unix-tiden fra en dato efter '2038-01-19 03:14:07.999999':
SELECT UNIX_TIMESTAMP('2038-01-20') Result;
Resultat:
+--------+ | Result | +--------+ | 0 | +--------+
Vi får 0
fordi datoargumentet er uden for det understøttede interval.
En relateret fejlrapport blev rejst for MySQL-teamet i 2005 (selvom nogle af detaljerne ser ud til at være anderledes), og i skrivende stund er den stadig ikke blevet behandlet.
Et lignende problem blev også rejst for at løse begrænsningerne med TIMESTAMP
datatype, som også endnu mangler at blive behandlet.
Eksempel 2 – SQL Server
SQL Server har i øjeblikket ikke en ækvivalent til MySQL's UNIX_TIMESTAMP
fungere. Derfor, hvis du har brug for at returnere Epoch time, bliver du nødt til at gøre noget som dette:
SELECT DATEDIFF(SECOND,'1970-01-01', GETUTCDATE());
Dette er fint for datoer før 2038-problemet. Efter denne dato vil du have problemer, fordi DATEDIFF()
funktion returnerer resultatet som en int datatype. int datatypen har et interval på -2^31 (-2.147.483.648) til 2^31-1 (2.147.483.647).
Her er, hvad der sker, hvis jeg forsøger at returnere Epoch time senere end '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.
Heldigvis er der også en DATEDIFF_BIG()
funktion, som gør nøjagtig det samme, bortset fra at den returnerer resultatet som en bigint datatype.
Så vi kan omskrive det forrige eksempel til følgende for at løse dette problem:
SELECT DATEDIFF_BIG(SECOND,'1970-01-01 00:00:00', '2038-01-19 03:14:08') AS 'Result';
Resultat:
+------------+ | Result | |------------| | 2147483648 | +------------+
Den store datatypen bruger 8 bytes (i modsætning til 4 bytes for en int ), så du skal beslutte, om du vil skifte til DATEDIFF_BIG()
nu eller senere. Hvis din ansøgning omhandler fremtidige datoer, kan det være klogt at gøre det tidligere end senere.