Unixzeit

Die Unixzeit ist eine Zeitdefinition, die für das Betriebssystem Unix entwickelt und als POSIX-Standard festgelegt wurde. Die Unixzeit zählt die vergangenen Sekunden seit Donnerstag, dem 1. Januar 1970, 00:00 Uhr UTC. Das Startdatum wird auch als The Epoch bezeichnet. Die Umschaltung von einer Sekunde zur nächsten ist synchron zur UTC. Schaltsekunden werden ignoriert,[1] eine Schaltsekunde hat den gleichen Zeitstempel wie die Sekunde davor. Vor Unix Version 6 (1975) zählte die Unix-Uhr in Hundertstelsekunden, daher musste die Epoche jedes Jahr neu festgelegt werden.

Eigenschaften

Umrechnung

Die Umrechnung in eine menschenlesbare Form, einschließlich der Anwendung von Zeitzonen, Sommerzeit und Schaltjahren werden dann von zusätzlichen Funktionen der Standardbibliothek übernommen. Die Darstellung des Datums als Sekunden seit der Unix-Epoche wird häufig verwendet, weil sie für Computerprogramme viel leichter zu verarbeiten ist als das „menschliche“ Datumsformat. Es lassen sich mit diesen Werten leicht Zeiträume als Differenzen von Sekunden berechnen. Sommer- oder Winterzeit, Zeitzonen und Schaltsekunden spielen dann keine Rolle mehr. Aus diesem Grund wird dieser Wert auch gerne als Zeitstempel verwendet. In praktisch allen Server-Anwendungen spielt der Zeitstempel eine tragende Rolle, so etwa im Umfeld von PHP- oder MySQL-Applikationen bei der Unterscheidung und Generierung zeitbezogener Datenbank-Einträge. Die vordefinierten PHP-Funktionen date() und mktime() ermöglichen hier beispielsweise durch das Einsetzen passender Funktionsargumente die einfache Konvertierung eines Zeitstempels in ein menschenlesbares Datumsformat und umgekehrt.

Jahr-2038-Problem

Exemplarische Darstellung des Jahr-2038-Problems

Der POSIX-Standard verlangt für die Zeitangabe in Sekunden seit 1. Januar 1970 mindestens eine vorzeichenbehaftete 32-Bit-Zahl (Integer). Somit umfasst die Unixzeit garantiert die Werte −2.147.483.648 bis +2.147.483.647. Umgerechnet in Jahre entspricht dies etwas mehr als −68 bis +68.

Am 19. Januar 2038 um 3:14:08 Uhr UTC wird es daher bei Computersystemen, welche die Unixzeit in einer vorzeichenbehafteten 32-Bit-Variable speichern, zu einem Überlauf, und mithin zu einem Rücksprung kommen.

Unixzeiten vor dem 13. Dezember 1901 20:45:52 UTC sind mit einer vorzeichenbehafteten 32-Bit-Zahl auch nicht darstellbar, da die Zeitstempel kleiner als −2.147.483.648 wären.

Moderne Unix-Systeme verwenden zumindest in ihrer 64-Bit-Variante eine vorzeichenbehaftete 64-Bit-Zahl, bei der das Risiko eines Überlaufs ohne praktische Relevanz ist. Hier ließen sich Zeitspannen von bis zu 292 Milliarden Jahren korrekt darstellen.

Eingebettete Systeme auf Unix-Basis sind zurzeit noch nahezu ausschließlich 32-Bit-Systeme. Die fortlaufende Entwicklung, technische Geräte (z. B. Router, Radiowecker bis hin zu Automobilen und Flugzeugen) mit eingebetteten Systemen auf Unix-Basis auszustatten, bedingt, dass diese Geräte ab dem 19. Januar 2038 nicht mehr korrekt funktionieren.

Verwendet ein Entwickler den vorzeichenlosen (unsigned-) 32-bit-Typ, könnte er die Unix-Zeit noch bis über das Jahr 2100 hinaus verwenden. Das gilt allerdings nicht, wenn er die Standardbibliothek nutzt. Diese ist auf den minimalen POSIX-Standard festgelegt.

Schaltsekunden

Schaltsekunden werden in der Unixzeit nicht mitgezählt, da sie nicht regelmäßig auftreten, sondern je nach schwankender Erdrotation nur sechs Monate vor ihrer Einfügung angekündigt werden. Alle Tage sind in Unixzeit genau 24 × 3600 Sekunden lang, Zeitdifferenzen in Unixzeit, die über eine Schaltsekunde hinweggehen, sind daher um diese Sekunde zu kurz.[2]

In den letzten Jahren gab es mehrere Versuche, im Rahmen der POSIX-Standardisierung eine Darstellung der Schaltsekunde zur POSIX-Unix-Zeitdefinition hinzuzufügen. Die Diskussionen zu diesem Thema führten jedoch bislang nicht zu einem allgemein akzeptierten Ergebnis.

Vergleich

Für die menschliche Anwendung sind auf Abschnitte herunter gebrochenen Zeiten, etwa Anzahl Jahre, Monate, Tage, Stunde mit zusätzlichen Angaben für Zeitzone und Sommerzeit übersichtlicher. Soweit eine alltägliche Zeit ohne Angabe von Sommerzeit und Zeitzone erfolgt, bleibt sie für eine computertechnische Verarbeitung allerdings noch unvollständig. Die gebrochene Darstellung macht Berechnungen in jedem Fall aufwändiger.

Unix bietet eine Konvertierung der Unixzeit in eine gebrochene Darstellung mittels localtime()an. Sommerzeit und Zeitzone werden von dieser Funktion, für den Anwender meist unsichtbar, aus administrativen Einstellungen des Systems bezogen – deshalb local-time.

In den DOS-Systemen war die gebrochene Zeit, aber ohne Zeitzonenangabe das Standardformat und wird auf FAT-Dateisystemen für Zeitstempel der Dateien in einer kompakten Darstellung gespeichert.

Eine Zeitangabe kann auch als Gleitkommazahl gespeichert werden. Dabei können auch sehr weit entfernte Zeitangaben gespeichert werden, teils Millionen Jahre entfernt, dann aber mit verminderter Zeitgenauigkeit. Eine Auflösung von unter einer Sekunde ist z. B. mit einem Double-Wert für die nächsten 140 Millionen Jahre möglich.

Besondere Werte

Das Unix-Millennium wurde am 9. Sep. 2001 in Kopenhagen auf einer Party der dänischen Unix User Group um 03:46:40 Ortszeit gefeiert

Unix-Enthusiasten haben es sich zum Brauch gemacht, zu bestimmten Werten der Unixzeit sogenannte „time_t-Partys“ – ähnliche den Neujahrsfeiern zum Jahreswechsel – zu veranstalten. Üblicherweise werden runde Dezimal-Werte, wie 1.000.000.000 oder 2.000.000.000 gefeiert. Unter manchen Benutzern werden allerdings auch runde Binär-Werte gefeiert, beispielsweise +230 (1.073.741.824), welcher auf den 10. Jan. 2004 13:37:04 UTC fiel. Am 13. Feb. 2009 um 23:31:30 UTC (14. Feb. 2009 um 00:31:30 CET) erreichte die Unixzeit den Wert 1234567890. Heise Online erwähnte dieses Ereignis in seinem Newsticker.[3]

Diese Zeitpunkte werden üblicherweise als „n Sekunden seit der Unix-Epoche“ gefeiert. Durch die Einführung von Schaltsekunden ist diese Bezeichnung allerdings nicht ganz korrekt.

WertZeitpunkt (UTC)
arithm.dezimalhexadez.
−231−2 147 483 64880 00 00 0013. Dez. 1901   20:45:52
−230−1 073 741 824B0 00 00 0023. Dez. 1935   10:22:56
−1 000 000 000C4 65 36 0024. April 1938   22:13:20
−229−536 870 912E0 00 00 0027. Dez. 1952   05:11:28
−228−268 435 456F0 00 00 0030. Juni 1961   02:35:44
000 00 00 001. Jan. 1970   00:00:00
21665 53600 01 00 001. Jan. 1970   18:12:16
22416 777 21601 00 00 0014. Juli 1970   04:20:16
100 000 00005 F5 E1 003. März 1973   09:46:40
2280268 435 45610 00 00 004. Juli 1978   21:24:16
500 000 0001D CD 65 005. Nov. 1985   00:53:20
2290536 870 91220 00 00 005. Jan. 1987   18:48:32
805 306 36830 00 00 009. Juli 1995   16:12:48
1 000 000 0003B 9A CA 009. Sep. 2001   01:46:40
2301 073 741 82440 00 00 0010. Jan. 2004   13:37:04
1 111 111 11142 3A 35 C718. März 2005   01:58:31
1 234 567 89049 96 02 D213. Feb. 2009   23:31:30
1 300 000 0004D 7C 6D 0013. März 2011   07:06:40
1 342 177 28050 00 00 0013. Juli 2012   11:01:20
1 400 000 00053 72 4E 0013. Mai 2014   16:53:20
1 500 000 00059 68 2F 0014. Juli 2017   02:40:00
1 600 000 0005F 5E 10 0013. Sep. 2020   12:26:40
1,5 · 2301 610 612 73660 00 00 0014. Jan. 2021   08:25:36
1 700 000 00065 53 F1 0014. Nov. 2023   22:13:20
1 800 000 0006B 49 D2 0015. Jan. 2027   08:00:00
1 879 048 19270 00 00 0018. Juli 2029   05:49:52
1 900 000 00071 3F B3 0017. März 2030   17:46:40
2 000 000 00077 35 94 0018. Mai 2033   03:33:20
2 100 000 0007D 2B 75 0018. Juli 2036   13:20:00
231−12 147 483 6477F FF FF FF19. Jan. 2038   03:14:07
Bei Verwendung des vorzeichenlosen
(unsigned-) 32-bit-Typ
232−14 294 967 295FF FF FF FF7. Feb. 2106   06:28:15

Unix-Befehle

Bei einigen Unix-ähnlichen Systemen lässt sich mittels nachstehendem Befehl eine Unixzeit in die äquivalente UTC-Zeit umrechnen (das Verhalten von date aus dem Beispiel ist nicht Bestandteil des POSIX-Standards).

 date -u -d @UNIXTIME

Beispiel:

 date -u -d @1234567890
 Fr 13. Feb 23:31:30 UTC 2009

Umgekehrt lässt sich die aktuelle Anzahl der vergangenen Sekunden seit dem 1. Januar 1970 auf einigen Unix-/Linux-Systemen mittels

 date +%s

anzeigen (das Verhalten von date ist auch hier nicht Bestandteil des POSIX-Standards).

Beispiel-Implementierung

Möchte man die Unixzeit zu einem gegebenen Zeitpunkt berechnen, lässt sich das über folgenden Rechenweg bewerkstelligen. Die Unixzeit kennt keine Zeitzonen. Sie nutzt als Eingabe eine von Zeitzonen bereinigte Zeit. Schaltsekunden werden mangels Vorhersagbarkeit weder für die Vergangenheit noch für die Zukunft berücksichtigt.

Achtung: Der folgende Quelltext ist in der Programmiersprache C verfasst und arbeitet mit maschinenabhängigen Datentypen. Das Jahr-2038-Problem tritt bei diesem Programm jedoch nicht auf, da der verwendete Datentyp „long long“ mindestens 64 Bits besitzt. Wie jeder Beispielquelltext dient er allein der Illustration und sollte ohne Überprüfung nicht in den Praxiseinsatz übernommen werden.

/** Konvertiert gegliederte UTC-Angaben in Unix-Zeit.
 * Parameter und ihre Werte-Bereiche:
 * - jahr [1970..2038]
 * - monat [1..12]
 * - tag [1..31]
 * - stunde [0..23]
 * - minute [0..59]
 * - sekunde [0..59]
 */
long long unixzeit(int jahr, int monat, int tag,
                   int stunde, int minute, int sekunde)
{
  const short tage_seit_jahresanfang[12] = /* Anzahl der Tage seit Jahresanfang ohne Tage des aktuellen Monats und ohne Schalttag */
    {0,31,59,90,120,151,181,212,243,273,304,334};

  int schaltjahre = ((jahr-1)-1968)/4 /* Anzahl der Schaltjahre seit 1970 (ohne das evtl. laufende Schaltjahr) */
                  - ((jahr-1)-1900)/100
                  + ((jahr-1)-1600)/400;

  long long tage_seit_1970 = (jahr-1970)*365 + schaltjahre
                           + tage_seit_jahresanfang[monat-1] + tag-1;

  if ( (monat>2) && (jahr%4==0 && (jahr%100!=0 || jahr%400==0)) )
    tage_seit_1970 += 1; /* +Schalttag, wenn jahr Schaltjahr ist */

  return sekunde + 60 * ( minute + 60 * (stunde + 24*tage_seit_1970) );
}

Eine Umrechnung von einer gegebenen Unixzeit in unsere gewöhnliche Datums- und Zeitdarstellung ist mit folgender Funktion möglich.

void UnixzeitNachDatumZeit(unsigned long int unixtime,
                           int *pJahr, int *pMonat, int *pTag,
                           int *pStunde, int *pMinute, int *pSekunde)
{
    const unsigned long int SEKUNDEN_PRO_TAG   =  86400ul; /*  24* 60 * 60 */
    const unsigned long int TAGE_IM_GEMEINJAHR =    365ul; /* kein Schaltjahr */
    const unsigned long int TAGE_IN_4_JAHREN   =   1461ul; /*   4*365 +   1 */
    const unsigned long int TAGE_IN_100_JAHREN =  36524ul; /* 100*365 +  25 - 1 */
    const unsigned long int TAGE_IN_400_JAHREN = 146097ul; /* 400*365 + 100 - 4 + 1 */
    const unsigned long int TAGN_AD_1970_01_01 = 719468ul; /* Tagnummer bezogen auf den 1. Maerz des Jahres "Null" */

    unsigned long int TagN = TAGN_AD_1970_01_01 + unixtime/SEKUNDEN_PRO_TAG;
    unsigned long int Sekunden_seit_Mitternacht = unixtime%SEKUNDEN_PRO_TAG;
    unsigned long int temp;

    /* Schaltjahrregel des Gregorianischen Kalenders:
       Jedes durch 100 teilbare Jahr ist kein Schaltjahr, es sei denn, es ist durch 400 teilbar. */
    temp = 4 * (TagN + TAGE_IN_100_JAHREN + 1) / TAGE_IN_400_JAHREN - 1;
    *pJahr = 100 * temp;
    TagN -= TAGE_IN_100_JAHREN * temp + temp / 4;

    /* Schaltjahrregel des Julianischen Kalenders:
       Jedes durch 4 teilbare Jahr ist ein Schaltjahr. */
    temp = 4 * (TagN + TAGE_IM_GEMEINJAHR + 1) / TAGE_IN_4_JAHREN - 1;
    *pJahr += temp;
    TagN -= TAGE_IM_GEMEINJAHR * temp + temp / 4;

    /* TagN enthaelt jetzt nur noch die Tage des errechneten Jahres bezogen auf den 1. Maerz. */
    *pMonat = (5 * TagN + 2) / 153;
    *pTag = TagN - (*pMonat * 153 + 2) / 5 + 1;
    /*  153 = 31+30+31+30+31 Tage fuer die 5 Monate von Maerz bis Juli
        153 = 31+30+31+30+31 Tage fuer die 5 Monate von August bis Dezember
              31+28          Tage fuer Januar und Februar (siehe unten)
        +2: Justierung der Rundung
        +1: Der erste Tag im Monat ist 1 (und nicht 0).
    */

    *pMonat += 3; /* vom Jahr, das am 1. Maerz beginnt auf unser normales Jahr umrechnen: */
    if (*pMonat > 12)
    {   /* Monate 13 und 14 entsprechen 1 (Januar) und 2 (Februar) des naechsten Jahres */
        *pMonat -= 12;
        ++*pJahr;
    }

    *pStunde  = Sekunden_seit_Mitternacht / 3600;
    *pMinute  = Sekunden_seit_Mitternacht % 3600 / 60;
    *pSekunde = Sekunden_seit_Mitternacht        % 60;
}

Diese Funktion erwartet als Eingangsparameter eine vorzeichenlose Ganzzahl („unsigned long int“). Nach dem C-Standard sind das mindestens 32 Bit. Die Funktion liefert somit korrekte Ergebnisse bis mindestens zum Januar 2106.

Weblinks

Einzelnachweise

  1. The Open Group Base Specifications Issue 7, Rationale, section 4.16 Seconds Since the Epoch. The OpenGroup, abgerufen am 22. Januar 2017.
  2. General Concepts. Abgerufen am 5. Mai 2018.
  3. Jürgen Schmidt: 1234567890, Heise online, 14. Feb. 2009, archiviert im Internet Archive

Auf dieser Seite verwendete Medien

Year 2038 problem.gif
Jahr 2038 Problem. Binär / Dezimal / falsche Zeit / echte Zeit
1000000000seconds.jpg
Autor/Urheber:
  • Chlor on da.Wikipédia
  • Antaya on fr.Wikipédia (update on Commons)
, Lizenz: CC BY-SA 3.0
Die 1.000.000.000 Sekunde nach der Unixzeit wurde am 9. September 2001 in Kopenhagen auf einer Party der dänischen UNIX User Group um 03:46:40 Ortszeit gefeiert