# Jahr-2038-Problem

Das Jahr-2038-Problem von EDV-Systemen (Numeronym: Y2K38) könnte zu Ausfällen von Software im Jahr 2038 führen. Dieses Problem ist auf EDV-Systeme beschränkt, die die Unixzeit benutzen und diese als vorzeichenbehaftete 32-Bit-Ganzzahl speichern.

## Hintergrund

Die Unixzeit zählt die seit dem 1. Januar 1970 abgelaufene Zeit in Sekunden. Der dafür auf einem 32-Bit-Unix der 1970er Jahre genutzte Datentyp time_t ist als vorzeichenbehaftete 32-Bit-Ganzzahl definiert und weist einen Maximalwert von 2.147.483.647 bzw. 2$^{31}$−1 auf. Auf allen dazu kompatiblen Unix- und Unix-artigen Systemen der darauffolgenden Jahrzehnte wird am Dienstag, dem 19. Januar 2038 um 03:14:07 Uhr UTC die Anzahl der vergangenen Sekunden diese Kapazität überschreiten. Das signifikanteste Bit (MSB, wie hier im Beispiel meist die linkeste Stelle) wird laut Konvention dazu verwendet, positive und negative Zahlen zu unterscheiden (Vorzeichen im Zweierkomplement), sodass die Zählung bei einer Überschreitung des Wertes 2.147.483.647 (binär 01111111 11111111 11111111 11111111) in den negativen Bereich springt (z. B. −2.147.483.648 binär 10000000 00000000 00000000 00000000). Das führt bei einer ungenügend implementierten Konvertierung von Unixzeit zu Datum und Uhrzeit ungewollt zur Interpretation des Wertes als 20:45:52 Uhr am Freitag, 13. Dezember 1901 UTC. Dieses Problem wird in der Software-Entwicklung als Zählerüberlauf bezeichnet.
Ohne Gegenmaßnahmen könnten die wirtschaftlichen Auswirkungen unter Umständen schädlich sein, zumal teilweise weiterhin 32-Bit-Unix-Systeme im Einsatz sind, die zu UNIX ABI-kompatibel blieben (vgl. POSIX). Anders als bei Anwendungsbereichen wie Unix-Servern und PCs, auf denen der Übergang zu einer 64-Bit-Architektur als abgeschlossen angenommen werden kann, verbleiben viele eingebettete Systeme mit Unix-artigen Betriebssystemen oft weiterhin eine 32-Bit-Architektur, deren Einsatzzeit aber oft ein Vielfaches von Desktop- und Serversystemen beträgt (z. B. Router, elektronische Messgeräte, Automobil-Systeme, IoT, Fernsehgeräte, Geräte in der Anlagensteuerung und Gebäudeüberwachung/-steuerung). Zudem wurde bei der Portierung bestehender Software von 32 auf 64 Bits u. U. nicht vollständig überprüft, ob die 64-Bit-Zeitstempel auch korrekt und unbeschnitten weiterverarbeitet werden. Eine zumindest verzögerte, auf Dauer aber notwendige Anpassung bzw. Modernisierung entsprechender Rechnersysteme in Unternehmen und Institutionen in heutiger Zeit könnte die Ausfallwahrscheinlichkeit senken.

## Beispiel

Ein Beispiel für Jahr-2038-Fehler ist die Gültigkeitsprüfung per Zeitstempel: Hierzu wird beim Beginn eines Vorganges die aktuelle Zeit gespeichert. Mit dieser kann sichergestellt werden, dass bis zum Abschluss des Vorgangs nicht mehr Zeit verstreicht als vorgegeben (beispielsweise die automatische Abmeldung beim Onlinebanking nach wenigen Minuten zur Missbrauchsvermeidung). Springt innerhalb einer solchen Frist der vom System angegebene Zeitstempel auf das Jahr 1901, so ist die Differenz fortan immer negativ. Erwartet das Programm nun aber eine positive Zahl mit einer Mindestgröße (z. B. 5 Minuten nach Beginn des Vorganges), wartet es vergeblich darauf, dass die Zahl positiv wird – die Zahl bleibt somit immer kleiner als der Zielwert von beispielsweise 5 Minuten. Das kann zu unerwünscht lange offen bleibenden Sicherheitszugängen führen oder auch zu Endlosschleifen, was sich für den Endbenutzer wie ein Nichtreagieren des Programms äußern kann.

## Abhilfe

Im Unix-Umfeld hat sich beim Übergang von 32-Bit- zu 64-Bit-Architekturen durchgesetzt, dass der Basistyp „long“ der Programmiersprache C von 32 Bit auf 64 Bit aufgeweitet wird (technisch: Umstellung von ILP32 auf LP64-Modell, siehe Datentypen in C). Dieser Datentyp entspricht der traditionellen Definition von time_t als größtem verfügbaren Basistyp, bevor mit C99 und UNIX98 der „long long“ Basistyp standardgemäß eingeführt wurde. Solche 64-Bit-Systeme sind damit auf einen POSIX-Zeitstempel mit 64-Bit-Sekunden seit 1. Januar 1970 umgestellt, womit sie für 292 Milliarden Jahre zuverlässig arbeiten.
Dennoch reicht eine Umstellung auf neue 64-Bit-Prozessorarchitekturen (x64, Itanium/IA-64, IBM Power 5, UltraSPARC, PA-RISC, MIPS, ARMv8) hier alleine nicht aus: Sie vereinfacht zwar die systemseitige Anpassung, das macht jedoch die Durchforstung und Neuübersetzung aller Programme mit starrer 32-Bit-Formatierung nicht überflüssig. Die meisten Programme sind inzwischen für 64-Bit-Architekturen angepasst worden, allerdings ist es dennoch leicht möglich, dass an verschiedenen Stellen im Programm der vom System gelieferte 64-Bit-Zeitstempel fälschlicherweise als 32-Bit-Wert weiterverarbeitet wird und somit nur die niederwertigen 32 Bits abgefragt werden, welche dann losgelöst wiederum am 19. Januar 2038 den Wert −2$^{31}$ = 13. Dezember 1901 annehmen. Auch bleiben weiterhin 32-Bit-Programme im Einsatz, z. B. auf 64-Bit-Multilib-Systemen, die eventuell wegen bestehender ABI-Kompatibilität nicht angepasst werden können. In beiden Fällen, also für 32-Bit-Programme (oder zwar inzwischen angepassten 64-Bit-fähigen Programmen, die aber für 32-Bit-Systeme übersetzt wurden) sowie für nicht vollständig überarbeitete und überprüfte 64-Programme, sind u. U. weiterhin 32-Bit-Zeitstempel in Verwendung.
Um 32-Bit-Systeme bzw. -Programme, die weiterhin in Verwendung sind, auch über das Jahr 2038 hinaus benutzbar zu machen, haben einige Betriebssysteme auch für 32-Bit-Architekturen die Definition von time_t auf 64-Bit umgestellt. Dies ist etwa bei NetBSD ab Version 6.0 von 2012, bei OpenBSD ab Version 5.5 von 2014 und beim Linux-Kernel ab Version 5.6 von 2020 der Fall. Zwar hatte man auf der Applikationsebene schon vorher die Verwendung von 64-Bit Alternativen vorgeschlagen, allerdings verblieb meist die alte Binärschnittstelle (ABI) erhalten, da sonst eine komplette Neuübersetzung aller 32-Bit-Programme notwendig gewesen wäre. Für einzelne Programme hatte die GNU-C-Bibliothek, eine der verbreiteten C-Standard-Bibliotheken, für die Umstellung entsprechend bereits eine time64_t-Definition eingeführt. Eine ähnliche Definition __time64_t wird im Windows-Umfeld verwendet, und mit Visual C++ 2005 wurde der Default auf time64 geändert. Weil die verbreiteten x86-Distributionen bereits um das Jahr 2020 für 32-Bit „IA-32“ (teils auch mit „i386“ bezeichnet) nicht fortgeführt und vollständig von deren 64-Bit-Varianten (x64, mit den Alternativbezeichnungen „amd64“ bzw. „x86-64“) beerbt wurden, haben die meisten Linux-Distributionen das Jahr-2038-Problem bis zuletzt nie vollständig gelöst, verblieben dadurch jedoch ABI-kompatibel. Die noch bestehenden 32-Bit-Distributionen haben um 2020 begonnen, alle 32-Bit-Zeitstempel in bestehender Software durchgehend zu 64-Bit-Datentypen zu überführen. So verkündete etwa Debian GNU/Linux erst 2024, alle bestehenden 32-Bit-Ports vollständig umzustellen (z. B. ARMv7), allerdings mit Ausnahme der 32-Bit-x86-Architektur (i386 und i386-hurd). Gerade auf der x86-Architektur gibt es seit der Umstellung auf 64 Bit größere Veränderungen, wie beispielsweise doppelt so viele Register, die auch 32-Bit-Anwendungen zugutekommen können – allerdings nur im 64-Bit-Betriebsmodus von amd64 bzw. x64. Weil dieser aber nicht vollständig mit i386 bzw. IA-32 kompatibel ist, wurde unter Linux mit „x32“ eine neue Binärschnittstelle (ABI) geschaffen, die – als Neuentwicklung – nicht zum bestehenden 32-Bit-x86-ABI (i386) kompatibel sein musste. Weil x32 im nativen 64-Bit-Modus läuft, verbleiben auch einige Datentypen 64-bittig, darunter time_t, sodass 32-Bit-Programme auf 64-Bit-„x32“-Systemen genau gleich wie unter x64 betroffen oder, bei korrekter Anpassung bestehender Software, nicht betroffen sind.
Eine andere Abhilfe ist die Umstellung der Programme vom Unixzeit-Zähler auf eine neue Zeitbasis; schon verbreitet ist etwa die Zählung von Millisekunden oder Mikrosekunden mit 64-Bit-Zählern (die nicht notwendigerweise eine 64-Bit-Architektur erfordern), insbesondere in eingebetteten Systemen mit Echtzeitanforderungen in dieser Größenordnung. Neuere Zeit-APIs verwenden immer eine größere Genauigkeit und Spannweite als die Unixzeit, beispielsweise Java System.currentTimeMillis (64-Bit-Millisekunden seit 1. Januar 1970; ausreichend für 292 Millionen Jahre) und .NET System.DateTime.Now.Ticks (64-Bit-10tel Mikrosekunden seit 1. Januar 0001; ausreichend für 29227 Jahre). Die datenbankgestützten Transaktionen verwenden oft TIMESTAMP-Werte, die im Datenbankstandard SQL92 mit einer Genauigkeit in Mikrosekunden definiert sind (auch so zugreifbar in ODBC/JDBC) und deren Repräsentation in Datenbanken meist als Abstand zum Tageszähler (SQL DATE) erfolgt, wobei der Tageszähler auch in 32 Bit eine größere Spannweite besitzt (die zugrunde liegende Epoche des Tageszählers ist jedoch sehr verschieden). Sofern diese Datentypen für Zeitstempel im Programm durchgängig weiterverwendet werden, heben sich die Beschränkungen des Unixzeit-Zählers auf.
Eine weitere Abhilfemöglichkeit ist die Speicherung des Zeitstempels als Zeichenkette, so wie es gemäß ISO 8601 vorgesehen ist, als YYYYMMDDhhmmss-Zeitstempel, z. B. „20140823142216“. Diese bleiben mindestens bis zum 31. Dezember 9999 23:59:59 Uhr von Jahr-Überlauf-Problemen verschont, sofern nicht die internen Operationen (z. B. zur Berechnung der Differenz zwischen zwei Zeitstempeln) diese in ein problematisches Binärformat umwandeln.

## Verwandtes Jahr-2036-Problem

Eng verwandt zum Jahr-2038-Problem ist das Jahr 2036 (Numeronym: Y2K36). Am Donnerstag, 7. Februar 2036 um 06:28:16 Uhr UTC läuft der Zähler des ursprünglich für UNIX entwickelten Zeitsynchronisations-Protokolls NTP (Network Time Protocol) über. In modernen Implementierungen ist dieses Problem zwar behoben (siehe RFC 5905), doch sehr viele Geräte – besonders eingebettete Systeme – arbeiten noch nach dem alten Standard RFC 868.
Auch hier ist der Hintergrund, dass die Zeitübermittlung als 32-Bit-Zahl in Sekunden stattfindet, jedoch mit der Startzeit 1. Januar 1900, 00:00:00 Uhr UTC und nicht vorzeichenbehaftet. Bei sehr ordentlicher Implementierung der Systeme kommt es sogar hier zu keinem (größeren) Problem während der Berechnung, da die Zeitsynchronisation nach einer Differenz-Methode arbeiten sollte.
Allerdings können ungültige Werte – implementierungsabhängig – auch dadurch auftreten, dass sowohl im NTP- als auch UNIX-Format gearbeitet wird: Dies betrifft insbesondere die wachsende Zahl eingebetteter Systeme im Rahmen des Internet of Things, wo zwar mangels batteriegestützter Echtzeituhr die Ermittlung der Systemzeit nach jedem Start zunächst im NTP-Format über Zeitserver erfolgen muss, diese in der Folge dann aber zur weiteren Repräsentation in die übliche Unixzeit konvertiert wird (durch Abzug der Zeitdifferenz). Wird hier die Zeit nach (nicht selten) fehlgeschlagenen Verbindungsversuchen mit 0 angegeben und dementsprechend mit 1900-01-01 00:00:00 UTC (in Unixzeit nur als negativer Wert darstellbar), würden nicht abgesicherte Konvertierungen zum UNIX-Format zu einem ungültigen und (wenn vorzeichenbehaftet) zudem negativen Wert führen, mit vergleichbaren Auswirkungen auf das System und sein Verhalten.