2016-04-19 31 views
1

Wie kann ich feststellen, ob die Sommerzeit für eine bestimmte Zeitzone nur bei time_t gilt? Ich habe read, dass "In POSIX-Systemen kann ein Benutzer die Zeitzone mit Hilfe der TZ-Umgebungsvariable angeben." Ich dachte, dass ich den aktuellen TZ-Wert speichern könnte (falls eingestellt), ändere es in den TZ, an dem ich interessiert bin, rufe localtime() an und überprüfe tm_isdst und ändere TZ zurück auf seinen ursprünglichen Wert. Ich bin mir nicht sicher, wie tragbar das wäre.Ermitteln Sie, ob DST für eine bestimmte Zeitzone mit einer bestimmten Zeitzone aktiv ist.

Gibt es eine tragbare Möglichkeit zur Bestimmung der Sommerzeit für eine Zeitzone mit time_t in C?

+0

"portable Weg" -> Nein. Leicht zu transportieren (innerhalb POSIX) -> vielleicht. Da Post nicht [POSIX] markiert ist, ist unklar, ob Sie eine POSIX-Antwort oder eine portable (Standard C) Antwort suchen. – chux

+0

Kann ich fragen * warum * Sie wollen wissen, ob DST in Kraft ist? Planen Sie gerade etwas wie "Hey Benutzer, wussten Sie, DST ist gerade in Kraft?" oder ist das ein Teil in etwas Komplexer, das du versuchst? Normalerweise, wenn ich das frage, ist die Antwort die letztere, und die Lösung besteht darin, eine Bibliothek wie [CCTZ] (https://github.com/google/cctz) oder HHinnets [Datum] (http: // howardhinnant .github.io/date_v2.html) und [tz] (http://howardhinnant.github.io/tz.html) Bibliotheken. –

+0

@chux Ich suche portabel, wenn möglich. Ab sofort läuft der Code in POSIX-Umgebungen, aber wer weiß, was die Zukunft bringt. – JB0x2D1

Antwort

1

Dies ist so tragbar, wie ich es machen könnte. Ich wäre an einer besseren Lösung interessiert. Was ich getan habe, ist die Zeit von Epoche bis Beginn und Ende der Sommerzeit in der Zeitzone Amerika/New_York für ein bestimmtes Jahr zu berechnen und zu testen, ob die angegebene time_t dazwischen liegt. Dies ist spezifisch für die Zeitzone Amerika/New_York, aber ich könnte mir vorstellen, dass es leicht mit einer gewissen Anstrengung für eine andere Zeitzone angepasst oder für alle/alle Zeitzonen angepasst werden könnte.

Wenn die GNU C Library verwenden, können timegm anstelle von getenv, verwendet werden mktime, setenv, aber nach GNU.org:

mktime ist im Wesentlichen universell verfügbar. timegm ist eher selten. Für die meisten tragbare Umwandlung von einer UTC kaputten Zeit auf eine einfache Zeit, stellen Sie den TZ Umgebungsvariable UTC, mktime nennt, dann setzen TZ zurück.

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include <sys/time.h> 

/***********************************************\ 
* In America/New_York: 
* DST begins: second Sunday in March 02:00 local, after which EDT == UTC-04:00 
* DST ends: first Sunday in November 02:00 local, after which EST == UTC-05:00 
\***********************************************/ 

//Return 1 if the year at UTC is greater than the year in America/New_York at 
//the given time t. In other words, at time t, is it between 00:00:00 UTC 
//(midnight) Jan 1 and 05:00:00 UTC Jan 1. Return 0 if the year at UTC is the 
//same as America/New_York at time t. 
int UTCyearIsGreater(time_t when) { 
    time_t begin, end; 
    struct tm* tm; 
    tm = gmtime(&when); 
    if (tm->tm_mon == 11 && tm->tm_mday == 31 && 
      (tm->tm_hour >= 19 && tm->tm_hour < 5)) { 
     return 1; 
    } 
    return 0; 
} 

//Return number of seconds from epoch until DST begins/began in America/New_York, the second Sunday in March (ssim). 
//for the given year. 
time_t ssim(int year) { 
    time_t t, t2; 
    int sim = 0; 
    struct tm tm = {0}; 
    tm.tm_year = year; 
    tm.tm_mon = 2; 
    tm.tm_mday = 1; 
    tm.tm_hour = 7; 
    char* env; 
    env = getenv("TZ"); 
    setenv("TZ", "UTC", 1); 
    t = mktime(&tm); 
    tm = *gmtime(&t); 
    while (sim < 2) { 
     if (tm.tm_wday == 0) { 
      sim += 1; 
      if (sim == 2) { break; } 
     } 
     tm.tm_mday += 1; 
     tm.tm_wday = 0; 
     t = mktime(&tm); 
     tm = *gmtime(&t); 
    } 
    t = mktime(&tm); 
    if (env == NULL) { 
     unsetenv("TZ"); 
    } else { 
     setenv("TZ", env, 1); 
    } 
    return t; 
} 

//Return number of seconds from epoch until DST ends/ended in America/New_York, the first Sunday in November (fsin). 
//for the given year. 
time_t fsin(int year) { 
    time_t t; 
    struct tm tm = {0}; 
    tm.tm_year = year; 
    tm.tm_mon = 10; 
    tm.tm_mday = 1; 
    tm.tm_hour = 6; 
    char* env; 
    env = getenv("TZ"); 
    setenv("TZ", "UTC", 1); 
    t = mktime(&tm); 
    tm = *gmtime(&t); 
    while (1) { 
     if (tm.tm_wday == 0) { break; } 
     tm.tm_mday += 1; 
     tm.tm_wday = 0; 
     t = mktime(&tm); 
     tm = *gmtime(&t); 
    } 
    t = mktime(&tm); 
    if (env == NULL) { 
     unsetenv("TZ"); 
    } else { 
     setenv("TZ", env, 1); 
    } 
    return t; 
} 

//Return 1 if DST is in effect in America/New_York at time t, return 0 otherwise 
int DSTinNYC(time_t t) { 
    time_t beginDST, endDST; 
    struct tm* tm_ptr; 
    tm_ptr = gmtime(&t); 
    if (UTCyearIsGreater(t)) { 
     tm_ptr->tm_year -= 1; 
    } 
    beginDST = ssim(tm_ptr->tm_year); 
    endDST = fsin(tm_ptr->tm_year); 
    return (t >= beginDST && t < endDST); 
} 

int main() { 
    //test it 
    if (DSTinNYC(1461179392)) { 
     printf("CORRECT 20 Apr 2016 15:09:52 EDT\n"); 
    } else { 
     printf("FAILED 20 Apr 2016 15:09:52 EDT\n"); 
    } 
    if (DSTinNYC(1455993975)) { 
     printf("FAILED 20 Feb 2016 13:46:15 EST\n"); 
    } else { 
     printf("CORRECT 20 Feb 2016 13:46:15 EST\n"); 
    } 
    if (DSTinNYC(1571179392)) { 
     printf("CORRECT 15 Oct 2019 18:43:12 EDT\n"); 
    } else { 
     printf("FAILED 15 Oct 2019 18:43:12 EDT\n"); 
    } 
    //results checked with http://www.epochconverter.com/ 
    return 0; 
} 
+0

Dies ist auch auf den * aktuellen * Satz von DST-Übergängen für New York beschränkt.Die USA haben 2007 ihre Regeln zuletzt geändert, aber andere Länder ändern sich ständig. Aus diesem Grund sind POSIX 'TZ'-Variablen nicht gut genug - sie können nur zwei Übergänge unterstützen und können keine Änderungen im Jahresvergleich durchführen. Die [TZ-Datenbank] (https://en.wikipedia.org/wiki/Tz_database) ist wichtig, um dies richtig zu machen. –

+0

BTW - Ihr Code erstellt im Wesentlichen die Effekte der POSIX TZ var von EST5EDT, M3.2.0/2, M11.1.0/2' –

+0

Richtig, aber in Ermangelung einer lebensfähigen C-Bibliothek, was könnte sonst noch zu tun? – JB0x2D1