staging.inyokaproject.org

Zeitwerte (C/C++)

Status: Gelöst | Ubuntu-Version: Ubuntu MATE 20.04 (Focal Fossa)
Antworten |

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Folgendes Szenario: Ich bekomme von einem Server den Zeitstempel einer Datei als Text. Dieser Wert soll dekodiert werden und in einer Variablen vom Typ time_t gespeichert werden. Mein Problem ist, dass ich nicht weiß, ob das jetzt ein 32-bit oder ein 64-bit Typ ist.

Ich habe da ja noch 2 32-bit Epia Boards am laufen. Daher möchte ich einen Quelltext haben, der in beiden Welten funktioniert.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    ...
    time_t      rcv_mtime;      // received from response header
    ...
        if( strncmp( rcv_buf, "Content-MTime: ", 15 ) == 0 ) { // not standard!
            cp += 15;
            rcv_mtime = atoi( cp );
            rcv_mtime_valid = true;
            continue;
        }
...

Wie man sieht, wird der Wert hier nur als 32-Bit Wert behandelt. Es geht um das Jahr-2038-Problem. Das müsste mich zwar eigentlich nicht mehr interessieren, aber ich möchte es trotzdem "richtig" machen. Zumal neuere Programme diese Umstellung schon eingeführt haben.

In einem anderen Zusammenhang wurde empfohlen, in solchen Fällen die Werte immer als "long long" zu behandeln und dann anschließend auf den benötigten Typ zu casten. Könnte das auch hier eine Lösung sein ?

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17630

Wenn Du den Zeitwert als Text bekommst, wie sieht denn so ein exemplarischer Text aus?

Er wird ja - als Text - nicht 32 oder 64 Bit groß sein, sondern eher mehr als eine Hand voll Bytes, selbst wenn es Seconds-of-epoch ist, oder Nanoseconds.

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Hier mal ein Beispieltext:

> Get any thumb for file:
  A. Vivaldi - Summer Presto guitar cover.mp4

HTTP/1.1 200 OK
Date: Sat, 12 Apr 2025 09:18:05 GMT
Server: thumbsv 0.5
Last-Modified: Sun, 24 Nov 2024 18:51:24 GMT
Content-MTime: 1732474284
Accept-Ranges: none
Content-Length: 19286
Content-Type: image/png

Aber es geht nicht um den Text als solchen, sondern darum, dass ich den Zahlenwert anschließend mit dem Unix Zeitstempel von lokal vorhandenen Dateien vergleichen will. Diese sind dann vom Typ "time_t" bzw. "struct timespec". Und time_t kann, je nach Plattform, 32 oder 64 Bit haben.

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4735

@Duakan Momentan ist es ja eigentlich noch egal wenn Du da keine Dateizeitstempel aus der Zukunft erwartest. Auf der vorerst sicheren Seite wäre man mit 64-Bit. Bonus wenn man sich für das parsen eine Funktion schreibt, die das als 64-Bit parst, nach time_t casted und dann prüft ob beim zurück casten der gleiche Wert heraus kommt und falls nicht — naja da musst Du dann entscheiden wie problematisch das an der Stelle ist.

Oder man schreibt sich eine Funktion die tatsächlich die Zeichenkette gleich in einen time_t-Wert umwandelt.

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Ich habe das jetzt mal so gemacht:

rcv_mtime = (time_t)atoll( cp );

Aber ob das auch auf einem 32 Bit System funktioniert, kann ich frühestens Morgen ausprobieren. Aber ich fürchte, da kommt mindestens eine Warnung.

Ich habe da noch das gefunden, bin aber nicht sicher, ob ich das alles verstanden habe. Jedenfalls ergibt

printf( "timesize: %d\n", __TIMESIZE );

den Wert 64. Das Makro dürfte es auf 32 Bit Systemen aber nicht geben. Vielleicht könnte man das trotzdem für einen Compiler Switch ausnutzen.

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4735

@Dakuan: Welche Warnung sollte da kommen? Du castest doch und das ist doch eigentlich immer ein „halt die Klappe Compiler, ich weiss was ich tue“ (selbst in Fällen wo man das nicht weiss 😀).

Warum sollte die Existenz eines Makros davon abhängen ob man sich auf einem 32 oder 64 Bit-System befindet? Gerade bei diesem Makro braucht man das doch um zu unterscheiden was man da jetzt vor sich hat. Die Frage ist an der Stelle eher ob man sich auf den GCC Compiler beschränken will, denn __TIMESIZE und was da dran hängt ist ja kein Standard, sondern GCC-spezifisches Zeug.

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

@Marc_BlackJack_Rintsch

Welche Warnung sollte da kommen?

Ich meine mich zu erinnern, dass mir bei C++ eine Warnung vor die Füße geworfen wurde, dass möglicherweise signifikante Bits verloren gehen.

Warum sollte die Existenz eines Makros davon abhängen ob man sich auf einem 32 oder 64 Bit-System befindet?

Ich gehe mal davon aus, dass dieses Makro erst benötigt wird, wenn man das Jahr-2038 Problem ernsthaft in Angriff nimmt.

Ich will einen Quelltext haben, den ich ohne Probleme zwischen beiden Systemen hin und her schieben kann. Zumindes solange mein Notebook noch funktioniert.

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4735

@Duakan: Aber doch nicht wenn man castet. Bei solchen Warnungen ist doch gerade der cast die Lösung die Warnung weg zu bekommen, weil ein cast ja wie gesagt dem Compiler immer sagt „ich weiss was ich tue, nerv nicht”. Deshalb sollte man ja auch mit casts vorsichtig sein, und die wirklich nur anwenden, wenn man weiss was man da macht.

Und ich verstehe auch immer noch nicht den was das System für einen Unterschied machen soll. Es ist doch völlig uninteressant ob das System 32 oder 64 Bit ist, was man wissen will ist ob die GNU C Laufzeitbibliothek für das normale time_t 32 oder 64 Bit verwendet, wenn man __TIMESIZE verwendet. Weil man dann weiss ob man einfach time_t oder __time_t64 verwenden muss/will. Allerdings auf Kosten der Portierbarkeit, weil das keine standardisierten Typen und Funktionen sind. Der Test auf __TIMESIZE ist ja schon kein Standard.

Solange wir das Jahr 2038 noch nicht erreicht haben, ist das doch aber alles egal, und dann sollte die Compiler doch hoffentlich einen entsprechenden Datentyp für time_t verwenden. Falls nicht wird's im Quelltext hässlich unportabel. Ich würde da wie gesagt lieber einen Test in Standard C einbauen der testet ob tatsächlich Datenverlust auftritt und dann entsprechend drauf reagieren.

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2136

@Dakuan: Mal grundsätzlich gefragt, ist dir bewusst, dass bis zum besagten Datum im Jahre 2038 das gleiche Ergebnis herauskommt, egal ob der String als 32- oder 64-Bit Integer interpretiert wird?

Du kannst insofern die Zeichenkette immer als long long behandeln und das Ergebnis in ein time_t casten, so wie du es bereits gezeigt hast. Bevor der Wertebereich von 32 Bit nicht überschritten wird, macht das ja effektiv keinen Unterschied.

Deine Funktion könnte zur Absicherung einen Fehlercode liefern, wenn das "time_t Ergebnis" anders aussieht als das "long long Ergebnis", was aber wie gesagt erst zum Tragen kommen wird, sobald das besagte Datum in 2038 überschritten worden ist.

Eine sinnvolle Behandlung des Fehlers dürfte nach aktuellem Stand schwierig sein, da AFAIK bisher nicht festgelegt wurde, was ein System, welches in 2038 noch mit 32 Bit arbeitet, für zukünftige Daten ausgeben würde bzw. ob es da eine erkennbare Änderung gäbe.

Ansonsten kann man ja nur raten, ob ein Datum für Anfang des 20. Jahrhunderts als Rückgabe sinnvoll erscheint oder ob das wahrscheinlich ein Überlauf für ein Datum ab 2038 ist. Bei Logfiles und ähnlichen Daten würde man natürlich vom zweiten Fall ausgehen (result + 0xffffffff).

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4735

Das hier sollte portabel sein:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

time_t parse_timestamp(char *text)
{
    long long int tmp = atoll(text);
    time_t result = (time_t) tmp;
    if (((long long int) result) != tmp) {
        fprintf(stderr, "time_t can't hold the parsed time value %lld\n", tmp);
        exit(1);
    }
    return result;
}

int main(void)
{
    time_t timestamp;
    
    timestamp = parse_timestamp("1732474284");
    printf("%s", ctime(&timestamp));
    
    timestamp = parse_timestamp("2211663600");
    printf("%s", ctime(&timestamp));
    
    printf("%zu\n", sizeof(time_t));
    
    return 0;
}

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Mal grundsätzlich gefragt, ist dir bewusst, dass bis zum besagten Datum im Jahre 2038 das gleiche Ergebnis herauskommt, egal ob der String als 32- oder 64-Bit Integer interpretiert wird?

Das ist klar. Aber ich habe noch in Erinnerung, wie viele Probleme ich vor einiger Zeit hatte, als ich alte Quelltexte auf ein 64-Bit System portiert hatte. Ursache war fast immer das ich bei den Datentypen früher zu "großzügig" war. Momentan stelle ich von FLTK 1.3.x auf 1.4.x um, was auch wieder viel Arbeit bedeutet. Aus diesem Anlass sehe ich gerade alle Quelltexte nach Unklarheiten durch.

Ich habe auch gerade den "32 Bit Test" durchgeführt (direktes casting). Die Übersetzung läuft glatt durch und das Programm funktioniert.

@ Marc_BlackJack_Rintsch Danke für das Testprogramm. Muss ich morgen mal testen (hab das Notebook gerade weggeräumt).

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2136

Wobei ich das Aussteigen via exit(1) mitten in der Funktion nicht so schön finde. Wie schon geschrieben könnte es in Abhängigkeit vom Kontext ja durchaus eine sinnvolle Fehlerbehandlung für den Fall eines Überlaufs geben.

Mein Code sähe dann wohl eher so aus:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

time_t parse_timestamp(char *text, int *overflow)
{
    long long int timestamp = atoll(text);
    *overflow = ((time_t) timestamp) != timestamp;
    return (time_t) timestamp;
}

void print_timestamps(char **strings, size_t num_items)
{
    int overflow;

    for (size_t i = 0; i < num_items; ++i) {
        char *text = strings[i];
        time_t timestamp = parse_timestamp(text, &overflow);
        if (overflow) {
            fprintf(stderr, "time_t can't hold the parsed time value %s\n", text);
        } else {
            printf("%s", ctime(&timestamp));
        }
    }
}

int main(void)
{
    char *strings[2] = {"1732474284", "2211663600"};

    printf("Using %zu-bit time_t\n\n", sizeof(time_t) * 8);
    print_timestamps(strings, 2);
    
    return 0;
}

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6532

Für mein aktuelles Projekt ist die Frage ja gelöst. Ich gehe auch nicht davon aus, dass einer meiner betroffenen Rechner 2038 noch läuft. Aber ich hatte in der Vergangenheit öfters das Problem mit Kalenderdaten rechnen zu müssen, was ich dann oft manuell mit Hilfe der Tagesnummern in meinem Ringbuchkalender durchgeführt hatte. Mein aktueller Kalender (Weihnachtsgeschnk) hat aber nicht mal Wochennummern.

Von daher hatte ich schon einmal über einen einfachen Datumsrechner nachgedacht (der müsste nur tagesgenau sein). Für so ein Projekt währen eure Vorschläge eine gute Ausgangsbasis, sofern man noch 32-Bit Systeme hat und auf Zeitfunktionen des Systems zurückgreifen will.

Antworten |