Wie konvertieren von UTC in die lokale Zeit in C?
Es ist eine einfache Frage, aber die Lösung scheint ganz einfach. Ich würde gerne wissen, wie konvertieren von UTC in die lokale Zeit. Ich bin auf der Suche nach einer Lösung in C, die standard und mehr oder weniger garantiert, um auf jedem computer an jedem Standort.
Ich gelesen haben die folgenden links sorgfältig, aber ich kann nicht finden, eine Lösung, die es gibt:
Umwandlung von string in lokaler Zeit in UTC im C
Konvertierung Zwischen Ortszeit (Lokale Zeit und GMT/UTC im C/C++
Ich habe versucht, eine Reihe von Variationen, wie (datetime ist ein string mit Datum und Uhrzeit in UTC):
strptime(datetime, "%A %B %d %Y %H %M %S", tp);
strftime(printtime, strlen(datetime), "%A %B %d %Y %H %M %S", tp);
Oder
strptime(datetime, "%A %B %d %Y %H %M %S", tp);
lt=mktime(tp);
printtime=ctime(<);
Egal was ich versuche printtime endet als das gleiche wie UTC.
Bearbeiten 11-29-2013: auf der Grundlage der sehr hilfreichen Antwort von "R" unten bekam ich endlich um ein Beispiel. Ich fand die Arbeit richtig, die zwei Zeitzonen der ich es getestet habe, MEZ und PST:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
long long diff_tm(struct tm *a, struct tm *b)
{
return a->tm_sec - b->tm_sec
+60LL*(a->tm_min - b->tm_min)
+3600LL*(a->tm_hour - b->tm_hour)
+86400LL*(a->tm_yday - b->tm_yday)
+(a->tm_year-70)*31536000LL
-(a->tm_year-69)/4*86400LL
+(a->tm_year-1)/100*86400LL
-(a->tm_year+299)/400*86400LL
-(b->tm_year-70)*31536000LL
+(b->tm_year-69)/4*86400LL
-(b->tm_year-1)/100*86400LL
+(b->tm_year+299)/400*86400LL;
}
int main()
{
time_t utc, local;
char buf[100];
const char datetime[]="2013 11 30 23 30 26 UTC"; /* hard coded date and time in UTC */
struct tm *tp=malloc(sizeof(struct tm));
if(tp==NULL)
exit(-1);
struct tm *localt=malloc(sizeof(struct tm));
if(localt==NULL)
exit(-1);
memset(tp, 0, sizeof(struct tm));
memset(localt, 0, sizeof(struct tm));
printf("UTC date and time to be converted in local time: %s\n", datetime);
/* put values of datetime into time structure *tp */
strptime(datetime, "%Y %m %d %H %M %S %z", tp);
/* get seconds since EPOCH for this time */
utc=mktime(tp);
printf("UTC date and time in seconds since EPOCH: %d\n", utc);
/* lets convert this UTC date and time to local date and time */
struct tm e0={ .tm_year = 70, .tm_mday = 1 }, e1, new;
/* get time_t EPOCH value for e0 (Jan. 1, 1970) */
time_t pseudo=mktime(&e0);
/* get gmtime for this value */
e1=*gmtime(&pseudo);
/* calculate local time in seconds since EPOCH */
e0.tm_sec += utc - diff_tm(&e1, &e0);
/* assign to local, this can all can be coded shorter but I attempted to increase clarity */
local=e0.tm_sec;
printf("local date and time in seconds since EPOCH: %d\n", local);
/* convert seconds since EPOCH for local time into localt time structure */
localt=localtime(&local);
/* get nicely formatted human readable time */
strftime(buf, sizeof buf, "%Y-%m-%d %H:%M:%S %Z", localt);
printf("local date and time: %s\n", buf);
}
Sollte es kompilieren ohne Probleme auf den meisten Systemen. Ich hart codiert ein Datum und eine Uhrzeit in UTC, die dann konvertiert werden sollen, die Ortszeit und das Datum.
- Können wir davon ausgehen, POSIX (da Sie Verwendung
strptime
) oder einfach nur C? - Sorry übersehen, ja Sie können davon ausgehen, POSIX.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn man davon ausgehen kann POSIX (und damit die POSIX-Spezifikation von
time_t
als Sekunden seit Beginn der Epoche), würde ich den ersten verwenden die POSIX-Formel zum konvertieren in Sekunden seit Beginn der Epoche:Nächstes verwenden
localtime((time_t []){0})
zu bekommenstruct tm
repräsentiert die Epoche, in der lokalen Zeit. Fügen Sie die Sekunden seit der Epoche dertm_sec
Feld diesesstruct tm
sind, dann rufen Siemktime
zu kanonisieren es.Edit: Eigentlich nur POSIX-Abhängigkeit ist, dass eine bekannte Epoche, die
(time_t)0
entspricht. Vielleicht können Sie einen Weg finden, um Sie, wenn Sie wirklich brauchen, um... beispielsweise über Aufrufe beidegmtime
undlocaltime
beitime_t
0..Edit 2: Eine Skizze, wie dies zu tun:
Bitte nicht Sinn der hässliche Ausgabe-code. Dieses Programm nimmt ein argument in der form "Sekunden relativ zu den POSIX epoch" und gibt den resultierenden Zeit in lokale Zeit. Sie können konvertieren UTC-Zeit in Sekunden seit der Epoche, mit der Formel, die ich oben zitiert. Beachten Sie, dass dieser code nicht in irgendeiner Weise davon abhängen, POSIX, aber es nimmt der offset zurückgegeben
diff_tm
Kombination mit dem Sekunden-seit-die-Epoche-Wert nicht überlaufenint
. Eine Lösung für dieses Problem wäre die Verwendung einerlong long
offset und eine Schleife, die hält das hinzufügen von Schritten, die nicht größer alsINT_MAX/2
(oder kleiner alsINT_MIN/2)
und ruftmktime
zu renormalize, bis der offset-Wert 0 erreicht.struct tm
im localtime-format übergebenmktime
für die Normalisierung.mktime
Funktion möchte vermeiden, das ganze Thema. Zumindest der obige Ansatz kann verwendet werden, um eine solche Funktion...gmtime()
ich nehme an, Sie erwarten, dass Sie die locale und benutzenmktime()
. Aber praktisch kann man nicht ändern+wiederherstellen globalen Zustand in den zuständigen code für die Bibliothek, vor allem in multi-threaded-Anwendungen, so dass es einen ernsthaften Mangel gibt.setenv
oderputenv
, aber wie du gesagt hast, dass ist nicht thread-safe. Sie kann mit meinem Ansatz und drop in POSIXlocaltime_r
undgmtime_r
um eine thread-sichere Lösung, obwohl.localtime
undgmtime
Funktionen) ich finde es schwer zu glauben ist, dass "außerhalb des Bereichs C".tm_yday
ist einstellen, indemmain
zu 0, das ist der vorgesehene Wert.Ahm ... ich könnte nur ein Anfänger in C, aber ich habe dieses Beispiel:
Erzeugt diese Ausgabe:
Jetzt haben Sie den Unterschied zwischen UTC-Zeit und lokaler Zeit in Sekunden. Das sollte genug sein, um Sie zu konvertieren.
Einen Hinweis zu deinem code, aseq: Sie sind mit malloc ohne hier müssen (Sie können memset Werte auf dem stack als auch, und malloc kann teuer werden während der stack-Allokation ist oft viel schneller), und Sie nicht befreien. Das ist sehr, sehr schlecht üben.
Andere Sache:
Wäre besser getan, wenn Sie möchten, übergeben sizeof(*tp) (oder, wenn Sie tp auf den stack, sizeof(tp)) zu memset. Damit ist sichergestellt, dass, auch wenn der Typ des Objekts ändert, wird es immer noch voll memset.
tm_isdst
auf EintragSum-up: die Umwandlung von kaputten Datum (struct tm) in UTC zu einem (lokalen) Kalender-Zeit (time_t) erreicht, die mit timegm() - das Gegenteil von mktime() - ABER timegm() ist keine standard-Funktion (wie die Logik ist, dass).
Der C-standard lässt uns nur mit time(), gmtime(), mktime() und difftime().
Einen workaround gefunden, in anderen docs berät zu emulieren timegm (), indem Sie zuerst die Umgebungsvariable TZ auf einen null-string, dann der Aufruf von mktime (), die sich in UTC-Kalender-Zeit, zurücksetzen von TZ auf seinen ursprünglichen Wert, aber noch einmal, dies ist nicht standard.
Grundsätzlich, so wie ich es verstehe, ist der Unterschied zwischen der lokalen Zeit und der UTC-Zeit ist nur ein offset, so dass, wenn wir beurteilen können, der ausgeglichen ist, können wir passen Sie das Ergebnis von mktime(), also hier ist mein Vorschlag:
Einem quick-test:
Fand ich, dass die Lösung bei der OP gab funktionierte nicht in den Fällen, wenn die Sommerzeit gilt. Zum Beispiel, in meinem Fall, zum aktuellen Zeitpunkt die Sommerzeit wurde nicht in Kraft ist, aber wenn ich das erste Datum, das konvertiert lokale Zeit mit DST, dann würde es nicht funktionieren, D. H. das heutige Datum ist 3/1/2018 und die Sommerzeit ist nicht in Kraft, aber wenn ich den Zeitpunkt für die Konvertierung in, sagen wir, 8/1/2018 0:00:00 wenn DST ist in Kraft, dann ist die Lösung gegeben würde, zu konvertieren, um die lokale Zeit, aber nicht die Sommerzeit berücksichtigt. Ich fand, dass die Initialisierung
e0
auf das Datum und die Stunde, in der die ursprünglichen Datums - /Zeit-string und dentm_isdst
zu-1
das problem gelöst. Dann habe ich das folgende Programm mit komplementären Funktionen, die Sie können in Ihrem code. Die anfängliche format des Datums und der Uhrzeit ist die gleiche, die MySQL benutzt, weil ich brauchte es für solche Zwecke.Ich denke, es ist leichter als das; mal.h definiert drei Variablen:
geladen werden auf der Basis der TZ-env-variable beim Aufruf
wenn Sie ein utc-Zeit in
konvertieren Sie es in ein time_t-mit mktime
dann konvertieren Sie es in lokale Zeit
Zeitzone ist die Anzahl der Sekunden von utc für die aktuelle Zeitzone und Sommerzeit ist 1 anzugeben, die Zeitumstellung ist in spielen und null für nicht.
Einer kleinen Vorsicht: Wenn ich codiert diese für einen mikrocontroller und cross-kompiliert, es wird Zeit.h definiert die Variablen, die mit der Erstausbildung Unterstrichen.
Siehe die man-Seite für Sie Zeit.h
Folgte ich der Antwort von @Dachschaden und ich habe ein Beispiel, das zeigt auch lesbare Ausgabe und ich entfernen Sie die DST-option für die Differenz in Sekunden zwischen UTC-Zeit und lokaler Zeit. Hier ist es:
Ausgabe auf meinem Rechner ist:
Beachten Sie, dass die Sommerzeit ist in diesem Fall (es gibt eine 1-Stunden-time zone offset-Differenz zwischen UTC-Zeit und LOKALER Ebene).
versuchen diese test-Ausgabe:
utcEpochTime: 1487652688, localEpochTime: 1487699488, diff: 46800
diff 13 Stunden, was gut ist, wie meine Zeitzone ist UTC+8.
Einen einfachen und effektiven Weg: Addieren (bzw. subtrahieren) Sie die Anzahl der Sekunden, die zwischen Ihrer Zeitzone und UTC (unter Berücksichtigung der Sommerzeit).
Als ein Beispiel, das funktioniert nur vor einer minute, am Dezember 30, 2017, US Mountain Standard Time (DST), die 7 Stunden hinter UTC:
Genießen.