Het jaar 2038-probleem (ook wel de Y2K38-bug genoemd) verwijst naar een probleem dat sommige computersystemen kunnen tegenkomen bij het omgaan met tijden na 2038-01-19 03:14:07.
Veel computersystemen, zoals Unix en Unix-gebaseerde systemen, berekenen geen tijd met behulp van de Gregoriaanse kalender. Ze berekenen de tijd als het aantal seconden sinds 1 januari 1970. Daarom wordt tijd in deze systemen weergegeven als een groot getal (d.w.z. het aantal seconden dat is verstreken sinds 1970-01-01 00:00:00). Dit wordt meestal Epoch-tijd, Unix-tijd, Unix Epoch-tijd of POSIX-tijd genoemd. Terwijl ik dit schrijf, is de Unix-tijd 1560913841. En terwijl ik deze volgende regel schrijf, is de Unix-tijd verhoogd naar 1560913879.
Het 2038-probleem wordt veroorzaakt door het feit dat veel systemen dit nummer opslaan als een 32-bits binair geheel getal met teken. Het bereik van een 32-bits geheel getal met teken is -2.147.483.648 tot 2.147.483.647. Dit betekent dat de laatste Epoch-tijd die kan worden weergegeven 2147483647 is. Dit zal gebeuren om 03:14:07 op dinsdag 19 januari 2038.
Daarna zal het resultaat grotendeels afhankelijk zijn van het systeem. In veel systemen zal een integer overflow optreden, en eventuele latere tijden zullen rondlopen en intern worden opgeslagen als een negatief getal. Het resultaat is dat een seconde later de tijd wordt geïnterpreteerd als 13 december 1901 in plaats van 19 januari 2038.
U kunt echter ook wisselende resultaten krijgen, afhankelijk van de gebruikte toepassing. Zelfs als uw besturingssysteem geen probleem heeft, kan uw eigen code nog steeds een probleem hebben. Als u bijvoorbeeld aangepaste code hebt geschreven om Unix-tijd terug te geven en u deze opslaat in een ondertekend geheel getal van 4 bytes, krijgt u problemen. In dergelijke gevallen is het misschien voldoende om de code te herschrijven om een geheel getal van 8 bytes te gebruiken.
Aangezien deze website helemaal over databases gaat, volgen hier enkele databasevoorbeelden.
Voorbeeld 1 – MySQL
In MySQL is de TIMESTAMP
gegevenstype ondersteunt datums/tijden van '1970-01-01 00:00:01.000000' UTC tot '2038-01-19 03:14:07.999999'. Daarom zou je kunnen zeggen dat elke database die dit gegevenstype gebruikt een Y2K38-bug heeft.
MySQL heeft ook een ingebouwde functie genaamd UNIX_TIMESTAMP()
die, zoals je zou verwachten, de Unix-tijdstempel retourneert.
De UNIX_TIMESTAMP()
functie accepteert een optioneel argument waarmee u een datum kunt specificeren die u wilt gebruiken voor de Unix-tijd (d.w.z. het aantal seconden van '1970-01-01 00:00:00' UTC tot de tijd die u opgeeft). Het geldige bereik van argumentwaarden is hetzelfde als voor de TIMESTAMP
gegevenstype, dat is '1970-01-01 00:00:01.000000' UTC tot '2038-01-19 03:14:07.999999' UTC. Als u een datum buiten het bereik doorgeeft aan deze functie, retourneert deze 0
.
Dit is wat er gebeurt als je deze functie probeert te gebruiken om de Unix-tijd te retourneren vanaf een datum na '2038-01-19 03:14:07.999999':
SELECT UNIX_TIMESTAMP('2038-01-20') Result;
Resultaat:
+--------+ | Result | +--------+ | 0 | +--------+
We krijgen 0
omdat het datumargument buiten het ondersteunde bereik valt.
Er is in 2005 een gerelateerd bugrapport ingediend voor het MySQL-team (hoewel sommige details anders lijken te zijn), en op het moment van schrijven is dit nog steeds niet verholpen.
Een soortgelijk probleem is ook aan de orde gesteld om de beperkingen met de TIMESTAMP
. aan te pakken gegevenstype, dat ook nog moet worden aangepakt.
Voorbeeld 2 – SQL Server
SQL Server heeft momenteel geen equivalent van MySQL's UNIX_TIMESTAMP
functie. Daarom, als je Epoch-tijd moet retourneren, moet je zoiets als dit doen:
SELECT DATEDIFF(SECOND,'1970-01-01', GETUTCDATE());
Dit is prima voor datums vóór het probleem van 2038. Na die datum heb je problemen, omdat de DATEDIFF()
functie retourneert het resultaat als een int data type. De int gegevenstype heeft een bereik van -2^31 (-2.147.483.648) tot 2^31-1 (2.147.483.647).
Dit is wat er gebeurt als ik Epoch-tijd later probeer terug te sturen dan '2038-01-19 03:14:07':
SELECT DATEDIFF(SECOND,'1970-01-01', '2038-01-19 03:14:08') AS 'Result';
Resultaat:
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.
Gelukkig is er ook een DATEDIFF_BIG()
functie, die precies hetzelfde doet, behalve dat het het resultaat retourneert als een bigint gegevenstype.
We kunnen het vorige voorbeeld dus herschrijven naar het volgende om dit probleem op te lossen:
SELECT DATEDIFF_BIG(SECOND,'1970-01-01 00:00:00', '2038-01-19 03:14:08') AS 'Result';
Resultaat:
+------------+ | Result | |------------| | 2147483648 | +------------+
De grote gegevenstype gebruikt 8 bytes (in tegenstelling tot 4 bytes voor een int ), dus u moet beslissen of u al dan niet wilt overschakelen naar DATEDIFF_BIG()
nu of later. Als uw aanvraag betrekking heeft op toekomstige data, kan het verstandig zijn om dit eerder dan later te doen.