2015-09-06 10 views
7

Ich fand keinen trivialen Weg, um den Zeitversatz in Minuten zwischen der Ortszeit und der UTC-Zeit zu erhalten.C-Code, um lokale Zeitverschiebung in Minuten relativ zur UTC zu erhalten?

Zuerst wollte ich tzset() verwenden, aber es bietet nicht die Sommerzeit. Gemäß der man-Seite ist es einfach eine ganzzahlige Zahl ungleich Null, wenn die Tageslichtsicherung wirksam ist. Während es normalerweise eine Stunde dauert, kann es in einem Land eine halbe Stunde dauern.

Ich würde es vorziehen zu vermeiden, den Zeitunterschied zwischen aktuellen UTC von gmtime() und localtime() zurückgegebenen berechnen.

Eine allgemeinere Lösung würde mir diese Informationen für eine bestimmte Position und einen positiven time_t-Wert oder zumindest lokal geben.

Edit 1: Der Anwendungsfall ist, den richtigen lokalen Zeitversatz für https://github.com/chmike/timez zu erhalten. BTW, Wenn Sie dachten, libc Funktionen zur Manipulation der Zeit waren OK, lesen Sie diese https://rachelbythebay.com/w/2013/03/17/time/.

Edit 2: die beste und einfachste Lösung, die ich bisher habe, die Zeit zu UTC in Minuten Offset zu berechnen ist

// Bogus: assumes DST is always one hour 
tzset(); 
int offset = (int)(-timezone/60 + (daylight ? 60 : 0)); 

Das Problem ist, die reale Tageslicht Sparzeit zu bestimmen.

Bearbeiten 3: Inspiriert von der Antwort von @trenki, kam ich mit der folgenden Lösung. Dies ist ein Hack darin, dass es mktime() tricks, die Ausgabe von gmtime() als die lokale Zeit zu betrachten. Das Ergebnis ist ungenau, wenn die Änderung der Sommerzeit in der Zeitspanne zwischen UTC-Zeit und Ortszeit liegt.

#include <stdio.h> 
#include <time.h> 

int main() 
{ 
    time_t rawtime = time(NULL); 
    struct tm *ptm = gmtime(&rawtime); 
    // Request that mktime() looksup dst in timezone database 
    ptm->tm_isdst = -1;     
    time_t gmt = mktime(ptm); 
    double offset = difftime(rawtime, gmt)/60; 
    printf("%f\n", offset); 
    return 0; 
} 
+1

Warum brauchen Sie das? Erklären Sie viel mehr über den Kontext; Warum können Sie 'gmtime' und' localtime' nicht einfach verwenden und einen Unterschied zwischen ihrer 'struct tm' machen? Warum sollte sich Ihr Benutzer um diesen lokalen Zeitversatz kümmern? Und es gibt viele seltsame Fälle (zB Silvester, ...) –

+1

Auch Ihre Frage ist sehr wahrscheinlich Betriebssystem-spezifisch ... AFAIK, [locale (7)] (http://man7.org/linux/ man-pages/man7/locale.7.html) sind nicht im Standard C99, aber in POSIX –

+0

@BasileStarynkevitch Locales sind in C99, selbst C89 hat 'strftime', leider nur mit'% Z' aber nicht '% z'. – Jens

Antwort

3

@Serge Ballesta Antwort ist gut. Also würde ich es testen und ein paar Details aufräumen. Ich hätte es als Kommentar gepostet, aber offensichtlich zu groß dafür. Ich habe es nur für meine Zeitzone trainiert, aber auch wenn andere ihre Maschine und Zone anprobieren wollen.

Ich machte Community-Wiki als nicht zu sammeln rep. Imitation is the sincerest form of flattery

long tz_offset_second(time_t t) { 
    struct tm local = *localtime(&t); 
    struct tm utc = *gmtime(&t); 
    long diff = ((local.tm_hour - utc.tm_hour) * 60 + (local.tm_min - utc.tm_min)) 
      * 60L + (local.tm_sec - utc.tm_sec); 
    int delta_day = local.tm_mday - utc.tm_mday; 
    if ((delta_day == 1) || (delta_day < -1)) { 
    diff += 24L * 60 * 60; 
    } else if ((delta_day == -1) || (delta_day > 1)) { 
    diff -= 24L * 60 * 60; 
    } 
    return diff; 
} 

void testtz(void) { 
    long off = -1; 
    int delta = 600; 
    for (time_t t = 0; t < LONG_MAX-delta; t+=delta) { 
    long off2 = tz_offset_second(t); 

    // Print time whenever offset changes. 
    if (off != off2) { 
     struct tm utc = *gmtime(&t); 
     printf("%10jd %04d-%02d-%02dT%02d:%02d:%02dZ\n", (intmax_t) t, 
       utc.tm_year + 1900, utc.tm_mon + 1, utc.tm_mday, 
       utc.tm_hour, utc.tm_min, utc.tm_sec); 
     struct tm local = *localtime(&t); 
     off = off2; 
     printf("%10s %04d-%02d-%02d %02d:%02d:%02d %2d %6ld\n\n", "", 
       local.tm_year + 1900, local.tm_mon + 1, local.tm_mday, 
       local.tm_hour, local.tm_min, local.tm_sec, local.tm_isdst ,off); 
     fflush(stdout); 
    } 
    } 
    puts("Done"); 
} 

Ausgabe

  0 1970-01-01T00:00:00Z 
      1969-12-31 18:00:00 0 -21600 

    5731200 1970-03-08T08:00:00Z 
      1970-03-08 03:00:00 1 -18000 

    26290800 1970-11-01T07:00:00Z 
      1970-11-01 01:00:00 0 -21600 

... 

2109222000 2036-11-02T07:00:00Z 
      2036-11-02 01:00:00 0 -21600 

2120112000 2037-03-08T08:00:00Z 
      2037-03-08 03:00:00 1 -18000 

2140671600 2037-11-01T07:00:00Z 
      2037-11-01 01:00:00 0 -21600 

Done 
2

Hat Ihr System strftime() -Funktion unterstützt die %z und %Z Planer? Auf FreeBSD,

%Z is replaced by the time zone name. 

%z is replaced by the time zone offset from UTC; a leading plus sign 
     stands for east of UTC, a minus sign for west of UTC, hours and 
     minutes follow with two digits each and no delimiter between them 
     (common form for RFC 822 date headers). 

und ich kann dies dies drucken verwenden:

$ date +"%Z: %z" 
CEST: +0200 

ISO C99 hat dies in 7.23.3.5 Die strftime Funktion:

%z  is replaced by the offset from UTC in the ISO 8601 format 
     ‘‘−0430’’ (meaning 4 hours 30 minutes behind UTC, west of Greenwich), 
     or by no characters if no time zone is determinable. [tm_isdst] 
%Z  is replaced by the locale’s time zone name or abbreviation, or by no 
     characters if no time zone is determinable. [tm_isdst] 
+0

Das wäre ein Weg, um die Informationen zu bekommen. Ein wenig verschachtelt, aber es würde funktionieren. Aber Visual Studio zum Beispiel unterstützt C99 nicht. Nicht sicher über VC 2015, aber das ist ein bekanntes Problem. Ich würde es vorziehen, die Abhängigkeit von C99 zu vermeiden. – chmike

4

Dieser C-Code berechnet den lokalen Zeitversatz in Minuten relativ zur UTC. Es nimmt an, dass DST immer eine Stunde versetzt ist.

#include <stdio.h> 
#include <time.h> 

int main() 
{ 
    time_t rawtime = time(NULL); 
    struct tm *ptm = gmtime(&rawtime); 
    time_t gmt = mktime(ptm); 
    ptm = localtime(&rawtime); 
    time_t offset = rawtime - gmt + (ptm->tm_isdst ? 3600 : 0); 

    printf("%i\n", (int)offset); 
} 

Es verwendet jedoch gmtime und localtime. Warum willst du diese Funktionen nicht benutzen?

+0

In Lord Howe Island (Australasien), ist die DST 30 Minuten. Siehe http://www.timeanddate.com/time/zone/australia/lord-howe-island. Ich würde es vorziehen, 'gmtime()' und 'localtime()' zu vermeiden, weil 1) viel mehr Rechenleistung benötigt wird als benötigt wird 2) die Berechnung des Zeitversatzes von den Feldern ist knifflig und fehleranfällig. – chmike

3

IMHO die einzige idiotensichere und portable Möglichkeit ist, localtime und gmtime zu verwenden und das Delta in Minute manuell zu berechnen, da diese 2 Funktionen auf allen bekannten Systemen existieren.Zum Beispiel:

int deltam() { 
    time_t t = time(NULL); 
    struct tm *loc = localtime(&t); 
    /* save values because they could be erased by the call to gmtime */ 
    int loc_min = loc->tm_min; 
    int loc_hour = loc->tm_hour; 
    int loc_day = loc->tm_mday; 
    struct tm *utc = gmtime(&t); 
    int delta = loc_min - utc->tm_min; 
    int deltaj = loc_day - utc->tm_mday; 
    delta += (loc_hour - utc->tm_hour) * 60; 
    /* hack for the day because the difference actually is only 0, 1 or -1 */ 
    if ((deltaj == 1) || (deltaj < -1)) { 
     delta += 1440; 
    } 
    else if ((deltaj == -1) || (deltaj > 1)) { 
     delta -= 1440; 
    } 
    return delta; 
} 

Vorsicht, testete ich nicht alle möglichen Sonderfälle, aber es könnte ein Ausgangspunkt für Ihre Anforderung sein.

+0

Das Problem ist der Eckfall. Dies macht diesen Ansatz schwierig. – chmike

+0

Was passiert, wenn der Zeitversatz die Jahresgrenze überschreitet? – chmike

+0

@chmike: Das ist der Grund für das, was ich * hack for the day * nannte.Ich benutze keine 'tm_mon'- oder' tm_year'-Felder, da ich folgendes vermute: Wenn der Unterschied zwischen den 'tm_mday'-Feldern 0, 1 oder -1 ist, dann ist es der wirkliche Unterschied; Wenn es in der Nähe von +30 ist (> 1 in meinem Hack), gibt es eine Monatsänderung, und der tatsächliche Wert ist -1; und wenn es in der Nähe von -30 (<-1 in meinem Code) ist, gibt es eine Monatsänderung auf der gegenüberliegenden Seite und der tatsächliche Wert ist +1. –