Im ANSI-Standard findet man in der Informationsdatei time.h alle gewünschten Funktionen und Typen deklariert.
Im Standard werden drei grundlegende Datentypen zur Zeitbearbeitung eingeführt.
clock_t nimmt kleine Zeitdifferenzen auf
time_t stellt die momentane Zeit dar
struct tm schlüsselt die Zeitangaben
auf
Mit diesen drei Datentypen kann man die Zeitfunktionen des Standards abdecken. Neben den Datentypen enthält time.h noch zwei Makros: NULL und CLOCKS_PER_SEC. Die ungültige Adresse NULL ist schon aus anderen Informationsdateien bekannt. Der zweite Makroname CLOCKS_PER_SEC gibt die Genauigkeit der Systemtakte an. Beachten Sie bei der Anwendung, daß es sich dabei auch um eine Fließkommazahl handeln kann.
Die in time.h deklarierten Funktionen zeigt die folgende Übersicht.
time_t time (time_t *zeitvar)
double difftime (time_t zeit1, time_t zeit0);
clock_t clock(void);
struct tm * localtime (const time_t *zeitvar);
struct tm * gmtime (const time_t * zeitvar);
time_t mk_time (struct tm * zeitzeiger);
char * asctime (const struct tm * zeitzeiger);
char * ctime (const time_t * zeitvar);
size_t strftime(char *s,size_t max,char *f,struct
tm *z);
Beginnen wir mit der momentanen Zeit. Die Funktion zum Lesen der momentanen Kalenderzeit heißt sinnvollerweise time(). Sie liefert eine systemabhängige Zeitangabe zurück. Unter TurboC++ und UNIX werden hier die Sekunden geliefert, die seit dem 1.1. 1970 00h00m00s GMT verstrichen sind.
Bild 7-1: Lesen der Systemzeit
Dieses Datum ist sozusagen das Geburtsdatum der C und UNIX-Welt. Der Datentyp time_t wird zumeist mit long dargestellt. Dieser Umstand wurde im Beispiel (Bild 1) ausgenutzt, um die momentane Zeit anzeigen zu können. Vielleicht ist es auch ganz interessant zu sehen, daß die interne Zeit unter C als GMT-(Greenwich Mean Time) geführt wird. Schließlich sind UNIX-Rechner internationale Systeme, die mit den entsprechenden Programmen weltweit kommunizieren können und daher die Zeit in GMT rechnen (oder, wie es heute heißt, UTC - Coordinated Universal Time).
Die Zeitcodierung wurde im Standard nicht festgelegt, aber man kann sich im Normalfall auf die oben angegebene Codierung verlassen.
time() liefert ein Ergebnis vom Datentyp time_t als Funktionsergebnis zurück. Das gleiche Ergebnis kann auch in einer Variablen zurückgegeben werden, wenn time() einen gültigen Zeiger auf eine Zeitvariable erhält. Braucht man nur den normalen Rückgabewert, dann kann der Zeiger NULL sein.
Bild 7-2: Ermitteln einer Zeitdifferenz
Im zweiten Beispiel (Bild 2) wird eine Zeitdifferenz gebildet. Dazu gibt es eine spezielle Funktion difftime(). (Diese Funktion ist die Operatorfunktion für die Subtraktion für den Datentyp time_t.) Im Beispiel wird wieder printf() benutzt, um das Ergebnis anzuzeigen. Die Anzeige mit printf() ist nur ein momentaner Notbehelf, bis die Wandlungsfunktionen besprochen sind, die eine Zeitangabe als Text darstellen können.
Neben der ANSI-Funktion difftime() wurde in der Zeile 15 sleep() verwendet. Diese Funktion, die es unter UNIX und DOS gibt, legt eine Pause im Programm für die angegebene Sekundenzahl ein. Unter DOS gibt es oft noch eine feinere Zeitauflösung mit delay(), die eine Anzahl von Millisekunden erwartet. delay() und sleep() entsprechen nicht dem ANSI-Standard.
In der printf()-Anweisung wurde die Rückgabe ohne Nachkommastellen angezeigt. Die Differenz von ganzen Sekunden kann sicher keine Bruchteile erzeugen. Auch wenn die Rückgabe von difftime() ein double-Wert ist, so wird er doch ganze Zahlen enthalten.
Die nächste Zeitangabe, die in ANSI möglich ist, ist die verbrauchte Rechenzeit. Die Maßeinheit sind interne Takte. Das Makro CLOCKS_PER_SEC gibt dabei an, wieviele Takte in einer Sekunde gezählt werden werden können. Die Takte sind die feinste Zeitmessung, die ANSI-C kennt.
Bild 7-3: Ermitteln der Programmlaufzeit in Systemtakten
Unter DOS wird der Zeitinterrupt zur Messung benutzt, sodaß die Makroexpansion von CLOCKS_PER_SEC "18.2" ergibt. Die printf()-Anweisung verwendet zur Ausgabe der Zeit in Sekunden daher auch eine Fließkommazahl. Eine älterer Name für CLOCKS_PER_SEC ist übrigens CLK_TCK. Unter TurboC++ haben beide Makros den gleichen Ersetzungswert.
Bild 7-4: Aufbau der Zeitstruktur
Die Namen der einzelnen Datenelemente sind im Standard festgelegt, die Reihenfolge nicht. Zur Anzeige der Informationen in der Struktur tm schreiben wir eine kleine Anzeigefunktion, die dann bei Bedarf in den einzelnen Beispielen aufgerufen werden kann.
Bild 7-5: Anzeige der Zeitinformation aus "tm"
Bild 7-6: Aufschlüsseln der lokalen Zeit
Die Ausgabe der lokalen Zeit mit der selbstgeschriebenen Ausgabefunktion könnte dann so aussehen.
Bild 7-7: Ausgabe der Zeitinformation
Wie man sieht, sind Autoren Nachtarbeiter. Aber zurück zur Zeitausgabe. Die Funktion localtime() liefert einen Zeiger auf einen internen Speicherbereich zurück, der mit jedem neuen Aufruf überschrieben wird. Dieser Speicherbereich verhält sich so, als würde er für jedes Programm eigens angelegt.
Mit printf() und einer mit localtime() gesetzten Strukturvariablen kann man sich nun beliebige Zeitangaben zusammenstellen.
Bei der Strukturdefinition im Bild 4 wurden innerhalb der Kommentare die möglichen Wertebereiche in eckigen Klammern mit angegeben. An zwei Stellen sind Korrekturen für Schaltjahre möglich. Ein Tag ist berücksichtigt und zwei Schaltsekunden.
Die Angabe der Sommerzeit kann "1" für Ja, "0" für Nein und negativ für Nicht-bekannt sein.
Innerhalb des Standards wird nicht festgelegt, wie man von einer GMT-Angabe, die immer vorhanden sein sollte, zur lokalen Zeit kommt. In den üblichen Implementierung setzt der Systemverwalter eine Umgebungsvariable, die darüber Auskunft gibt. Die Variable heißt in den meisten Fällen TZ oder TIMEZONE.
Es ist fast schon selbstverständlich, daß viele Implementierungen der Compiler sich nur extrem dürftig um einen allgemeinen und damit internationalen Denkansatz bemühen.
Bild 7-8: Versuch, die Zeitzone zu setzen
Das TurboC++ Beispiel im Bild 8 kann zwar mit setenv() die Umgebungsvariable TZ setzen, obwohl diese Variable besser beim Hochfahren des Systems in einer Anlaufdatei gesetzt würde (autoexec.bat,rc,/etc/profile). Die Variable enthält hier die Abkürzung für die Zeitzone, einen Versatz relativ zur GMT und die Abkürzung für die Sommerzeit.
Der Versatz zur GMT wird für Orte westlich von Greenwich positiv, für Orte östlich negativ angegeben.
Die Angaben setzen aber interne Tabellen voraus, die nur für die USA implementiert sind. Die weiteren, absolut notwendigen Angaben wären die Datumsangaben der Umschaltung zwischen Winter- und Sommerzeit, die Umschaltzeit sowie der Zeitversatz zwischen Sommer- und Winterzeit. Sie fehlen hier und machen das gesamte Konzept der Zeitangaben zunichte.
Wenn man weiß, daß hier nur ein paar Zeilen angepaßt werden müßten, ist diese Unterlasssung unverständlich.
Unter UNIX (Coherent 3.2) sieht die Variable anders aus. Hier können wieder die Kürzel für die Zeitzone und die Sommerzeit zusammen mit der Ablage von GMT angegeben werden. Zusätzlich ist es jetzt möglich zu sagen, an welchen Tag im Monat die Umschaltung zur Sommerzeit oder zur Winterzeit erfolgt. Im Beispiel wurde eingestellt, daß die Umstellung am letzten (-1) Sonntag (1) im März (3) erfolgt und die Rückstellung am letzten Sonntag im September geschieht. Der Zeitpunkt ist 2 Uhr (2) und der Versatz ist 60 Minuten (60).
TIMEZONE=mez:-60:msz:-1.1.3:-1.1.9:2:60
Sollte eine vollständige Angabe nicht möglich und damit nicht vorgesehen sein, bleibt Nicht-USA-Programmieren nur die eigenhändige Konvertierung.
Ganz nebenbei wurde im Bild 8 eine neue Anzeigefunktion benutzt. asctime() wandelt die Angaben aus einer tm-Struktur in eine ASCII-Text. Der Textpuffer ist in asctime() definiert und wird bei jedem Aufruf überschrieben.
Eine Funktion wurde aus Kompatibilität zu älteren Implementierungen beibehalten: ctime(). ctime() funktioniert wie ein Aufruf von asctime() mit einem durch localtime() ermittelten Parameter. Der entsprechende Aufruf steht im Bild 9.
Bild 7-9: Textliche Zeitangabe mit ctime()
Bei Computersystemen ergibt sich aus den verschiedenen Zeiten ein ähnliches Problem. Die Programme für die Kommunikation oder Backups, Jobtransfer oder Remote Login müssen eine gemeinsame Zeitbasis haben und das ist die Weltzeit, die am besten unter ihrem alten Namen GMT bekannt ist.
In ANSI-C gibt es eine eigene Funktion, die analog der localtime()-Funktion benutzt wird, zur Ermittlung der Weltzeit: gmtime().
Sie liefert einen Zeiger auf eine statische, interne Struktur vom Typ struct tm zurück, die die Datums- Tages- und Zeitangaben enthält.
Bild 7-10: Ermitteln der Weltzeit
Aus einer teilweise gesetzten Zeitstruktur werden mit der Funktion mktime() die fehlenden Einträge ermittelt und ein Wert des Datentyps "time_t" gebildet.
Die notwendigen Einträge sind der Tag, der Monat und das Jahr sowie bei Bedarf die Uhrzeit mit Stunden, Minuten und Sekunden. Ermittelt werden der Tag im Jahr und der Wochentag.
Der ermittelte Wert könnete mit einer DOS/UNIX-Funktion stime() auch zum Stellen der momentanen Systemzeit verwendet werden. Dabei erwartet stime() einen Zeiger auf eine Zeitvariable vom Typ time_t.
Das Beispiel wurde dem Standard entlehnt. Es soll ermittelt werden, welcher Wochentag der amerikanische Nationalfeiertag im Jahre 2001 ist. Dazu setzt man eine Strukturvariable mit Hilfe der angegebenen Werte und ruft mktime(). Ist der Rückgabewert nicht (-1), dann war die Wandlung erfolgreich und der Wochentag ermittelt.
Bei eienm Ergebnis von "3" hat man dann herausgefunden, daß dies ein Mittwoch sein wird.
Bild 7-11: Ermitteln der Kalenderzeit
Um dem Problem eines unsigned long-Zählers aus dem Weg zu gehen, hat man den eigenen Zeitdatentyp time_t definiert und wurde damit unabhängig von der eigentlichen Realisierung.
Benutzer von ANSI-C können der Jahrtausendwende gelassen entgegensehen, da die internen Zeitangaben nicht zu einem Fehler führen sollten.
Die Anzeige beruht auf dem Begriff eines Schauplatzes (locale), der entweder mit Umgebungsvariablen oder mit der Funktion setlocale() eingestellt werden kann.
Bild 7-12: International formatierte Zeitanzeige
Die Funktion strftime() (string formated time / Text mit formatierter Zeit) erhält als Parameter einen Puffer mit seiner Länge, einen Text mit Formatangaben und einen Zeiger auf eine Zeitstruktur.
Der Formattext wird analog dem Formattext von printf() aufgebaut. Jedes Formatelement wird mit einem "%"-Zeichen eingeleitet, normaler Text wird ohne Änderung ausgegeben und ein doppeltes "%%" wird als einzelnes "%"-Zeichen dargestellt.
Die einzelnen Formatelemente berücksichtigen nun die ortsübliche Darstellung von Zeitelementen. So können vollständige Datums- und Zeitangaben sowie jedes einzelne Element in ortsüblicher Darstellung ausgewählt werden, wenn ... ja, wenn nur die setlocale()-Funktion funktioniert.
Bisher gibt es dies nur bei XPG3 ( X/OPEN Portability Guide Version 3) konformen Compilern. Leider sind die jedoch nur wenig verbreitet. UNIX-Systeme sind hier ihren DOS-Kollegen weit voraus.
Näheres zur Internationalisierung können Sie dem Kapitel über Ortsanpassungen entnehmen.
In der folgenden Übersicht sind die Formatelemente für strftime()
aufgelistet.
%a | Tag, Abkz., ortsangepaßt |
%A | Tag, Name, ortsangepaßt |
%b | Monat, Abkz, ortsangepaßt |
%B | Monat, Name, ortsangepaßt |
%c | Datum und Zeit, ortsangepaßt |
%d | dezimaler Tag im Monat [1 -31] |
%H | Stunde, dezimal, [0 - 23] |
%I | Stunde, dezimal, [0 - 11] |
%j | Tag im Jahr, dezimal, [1 - 366] |
%m | Monat, dezimal [1 - 12] |
%M | Minute, dezimal, [0 - 59] |
%p | Halbtageskennung (AM/PM), ortsangepaßt |
%S | Sekunde, dezimal [0 - 61] |
%U | Wochennummer in Sonntagen vergangen [0 - 53] |
%w | Wochentag, [0 - 6], ab Sonntag |
%W | Wochennummer In Montagen vergangen |
%x | Datum, ortsangepaßt |
%X | Zeit, ortsangepaßt |
%y | Jahr ohne Jahrhundert, dezimal |
%Y | Jahr mit Jahrhundert |
%Z | Zeitzone (Abkz.) |