mktime macht verbotene Dinge...



  • Hallo Forum,

    zwei Fragen habe ich zu meinem Programm.

    Zuerst die einfache: Warum meldet mir das Terminal einen Speicherzugriffsfehler, wenn ich timeptr nicht mit " =localtime(..) " zunächst vollständig befülle?

    Dann die Frage, die mich wirklich bewegt:
    Warum gbt das Programm nicht 3, sondern -3597, also genau eine Stunde weniger aus als erwartet? Nach der Dokumentation hier:
    http://www.cplusplus.com/reference/clibrary/ctime/tm/
    glaube ich, alles richtig befüllt zu haben, insbesondere die Stunden.

    Habt ihr eine Idee?

    #include <time.h>
    #include <iostream>
    
    int main(){
    
    	time_t rawtime;
        struct tm* timeptr;
    
    	time(&rawtime);
           timeptr=localtime(&rawtime);
    
     timeptr->tm_year=1970-1900;
        timeptr->tm_mon=0; 
        timeptr->tm_mday=1;
        timeptr->tm_hour=00;
        timeptr->tm_min=00;
        timeptr->tm_sec=03;
        timeptr->tm_isdst=0;
    
      time_t i= mktime(timeptr);
       std::cout<<i<<"<-seconds since 1970"<<std::endl;
    
       return 0;
    }
    


  • Zunächst definiert

    struct tm *timeptr;
    

    kein Objekt des Typs struct tm, sondern lediglich einen Zeiger. Dieser zeigt zunächst ins Nirwana, weswegen Zugriffe durch ihn undefiniertes Verhalten erzeugen. localtime gibt einen Zeiger auf ein struct tm-Objekt mit statischer Speicherdauer zurück, in dem du dann rumschreiben kannst - auch wenn es ziemlich schlechter Stil ist, das zu tun, und ich nicht sicher bin, ob localtime darauf gereizt reagieren darf. Jedenfalls dürften deine Änderungen spätestens nach dem nächsten Aufruf von localtime verloren gehen. Besser wäre es,

    struct tm zeit;
    

    zu schreiben und jeweils dessen Adresse (&zeit) zu nehmen.

    Die Abweichung von 3600 Sekunden ergibt sich aus der Zeitzone - mitteleuropäische Winterzeit ist UTC+1. Siehe auch Folgendes:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    int main(void) {
      struct tm t = { 0 };
    
      t.tm_year = 70;
      t.tm_mday = 1;
    
      printf("%d\n", mktime(&t));
    
      setenv("TZ", "UTC", 1);
      tzset();
    
      printf("%d\n", mktime(&t));
    
      return 0;
    }
    


  • Das hab ich jetzt verstanden, das mit dem Zeiger.

    Und natürlich ist die Lösung, direkt eine Instanz zu deklarieren, viel eleganter.

    Mitteleuropäische Sommerzeit ist dann UTC+2?

    Ich habe mal versucht, dein Programm zu verstehen:

    Mktime gibt mir also immer die Sekunden seit dem 1.1.1970 in London, interpretiert aber die Eingabe nicht als UTC, sondern als Lokalzeit, weil die Umgebungsvariablen auf UTC+1 gesetzt sind. (?) Und als es bei uns schon Januar war, war es das in London eben noch nicht.

    Mit setenv setzt du dann die Umgebungsvariable TimeZone auf UTC.
    Die Eingabe wird daraufhin von mktime als Weltzeit interpretiert (das, was ich will).

    tzset(); ist nicht notwendig, da es offenbar von mktime sowieso aufgerufen wird. (Siehe http://www.codecogs.com/reference/computing/c/time.h/tzset.php)

    Was hat es denn mit der Variable tzname auf sich, die von tzset() gesetzt wird? Warum greift mktime nicht direkt auf TZ zu?

    Zuletzt zum Stil:
    Ich werde in meinem Programm wohl in UTC rechnen, die Benutzerangaben kommen aber natürlich in Lokalzeit mit allem drum und dran.
    Ich vermute, dass ich korrekte Ergebnisse bekomme, wenn ich während des Einlesens der Nutzereingaben mktime() mit UTC+1 verwende, da es mir ja die Sekunden nach Weltzeit liefert. Richtig? 🤡

    Anschließend, für die interne Rechnerei, kann ich dann TZ=UTC setzen, und für die Ausgabe stelle ich es wieder zurück. Und jedes mal tzset() aufrufen, wenn ich etwas ändere.

    Nur: Ist es nicht schlechter Stil, so an den Umgebungsvariablen herumzudoktorn? Ich vermute zwar, das diese nur lokal gültig sind, dass sich also meine Ubuntu-Uhr rechts oben nicht umstellt 😃 , aber es fühlt sich doch sehr wie ein Workaround an.

    (Im Fall meines aktuellen Programms kann ich darauf verzichten, da ich nur Zeitdifferenzen abschätzen muss und somit einfach die time_t Integer voneinander subtrahieren werde. Dennoch wird sich die Frage irgendwann stellen)

    Gruß, Handelsreisender 🙂



  • Das Programm, an dem ich schreibe, arbeitet mit Zeitdifferenzen zwischen Ereignissen, die an verschiedenen Stellen auf der Welt geschehen. Wenn ich also in New York und in Deutschland zwei Zeiteingaben habe, dann lasse ich mktime die Zeitangaben jeweils nach lokaler Zeit interpretieren, mktime liefert mir aber UTC- Sekunden, sodass die Zeitdifferenz mktime(&ereinisInNewYork)- mktime(&ereignisInBerlin) immer korrekt ist.

    Für diesen Zweck wäre es sehr hilfreich, wenn es ein Attribut im struct tm -Objekt gäbe, das die Zeitzone anzeigt und das von mktime (Statt der globalen Variable TZ) abgefragt wird. Ich vermute, das gibt es?



  • Im C-Standard gibt es etwas derartiges nicht; die Behandlung von Zeitzonen ist implementation-defined (vgl. 7.23.1 (1)). Im POSIX-Standard ist allerdings einiges dazu definiert, speziell das hier. Wie sich das unter Windows verhält, kann ich aus dem Stand nicht sagen, aber auf POSIX-konformen Systemen könntest du einfach

    mktime(&t) - timezone
    

    benutzen.

    Einen Zeitzonenmember in struct tm gibt es auch in POSIX nicht; BSD definiert etwas derartiges, aber mktime setzt diesen auf die eingestellte Systemzeitzone und benutzt ihn nicht als Eingabe - das würde sich wohl auch bit ISO-C und POSIX beißen.

    Die Rückgabe von mktime ist selbstverständlich auf UTC normiert; es handelt sich um die Anzahl Sekunden seit Epoch, d.h. 1.1.1970 0:00:00 UTC. Dieser Zeitpunkt ist nicht von Zeitzonen abhängig.



  • Ok, danke, das alles hilft mir schon sehr.


Anmelden zum Antworten