在给定time_t的情况下确定DST是否对指定时区有效

时间:2016-04-19 14:06:01

标签: c timezone dst

如果仅指定time_t,我如何确定夏令时是否对指定的时区有效?我read表示“在POSIX系统中,用户可以通过TZ环境变量指定时区。”我想我可以保存当前的TZ值(如果设置),将其更改为我感兴趣的TZ,调用localtime()并检查tm_isdst,并将TZ更改回原始值。我只是不确定那将是多么便携。

在C中给定time_t时,是否有可行的方法来确定时区的DST?

1 个答案:

答案 0 :(得分:1)

这是我可以携带的便携式。我对任何更好的解决方案感兴趣。我所做的是计算给定年份在America / New_York时区的从纪元到DST开始和结束的时间,并测试给定的time_t是否介于两者之间。这是针对America / New_York时区的,但我认为它可以很容易地适应另一个时区,或者适应任何/所有时区,需要付出一些努力。

如果使用GNU C库,可以使用timegm代替getenvmktimesetenv,但根据{ {3}}:

  

mktime基本上是普遍可用的。 timegm相当罕见。   对于从UTC故障时间到a的最便携式转换   简单的时间,将TZ环境变量设置为UTC,然后调用mktime   重新设置TZ

#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;
}