c mktime 在 Windows 和 GNU/Linux 上是不是不同?

Posted

技术标签:

【中文标题】c mktime 在 Windows 和 GNU/Linux 上是不是不同?【英文标题】:is c mktime different on Windows and GNU/Linux?c mktime 在 Windows 和 GNU/Linux 上是否不同? 【发布时间】:2011-09-22 00:13:32 【问题描述】:

以下代码:

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

    static const char * wday_abb_names[] =
    
        "Mon",
        "Tue",
        "Wed",
        "Thu",
        "Fri",
        "Sat",
        "Sun",
    ;

    static void mb_setenv(const char *name, const char *value)
    
    #if !(defined _WIN32) || defined HAVE_SETENV
        setenv(name, value, 1);
    #else
        int len = strlen(name)+1+strlen(value)+1;
        char *str = malloc(len);
        sprintf(str, "%s=%s", name, value);
        putenv(str);
    #endif
    

    static void mb_unsetenv(const char *name)
    
    #if !(defined _WIN32) || defined HAVE_SETENV
        unsetenv(name);
    #else
        int len = strlen(name)+2;
        char *str = malloc(len);
        sprintf(str, "%s=", name);
        putenv(str);
                    free(str);
    #endif
    

    time_t mb_timegm(struct tm *tm)
    
        time_t ret;
        char *tz;

        tz = getenv("TZ");
        mb_setenv("TZ", "");
        tzset();
        ret = mktime(tm);
        if (tz)
        
            mb_setenv("TZ", tz);
        
        else
        
            mb_unsetenv("TZ");
        
        tzset();
        return ret;
    

    time_t get_test_time()
    
        struct tm msg_time;
        msg_time.tm_isdst = 0;
        msg_time.tm_wday = 4;
        msg_time.tm_mon = 5;
        msg_time.tm_mday = 16;
        msg_time.tm_hour = 4;
        msg_time.tm_min = 53;
        msg_time.tm_sec = 0;
        msg_time.tm_year = 111; //2011 - 1900
        time_t retval = mb_timegm(&msg_time);
        printf("final msg_time = %ld\n", retval);
        return retval;
    

    void print_time(const char *msg, struct tm *t)
      
        printf("%s %s, %02d.%02d.%2d %2d:%02d\n", msg,
               wday_abb_names[t->tm_wday],  t->tm_mday, t->tm_mon, t->tm_year,
               t->tm_hour, t->tm_min);
      

    int main()
    
        printf( "=== ENVIRON ===\n");
        printf("TZ = %s\n", getenv("TZ"));
        time_t now;
        struct tm l, g;
        time(&now);
        l = *localtime(&now);
        g = *gmtime(&now);

        print_time("Local time :", &l);
        print_time("utc        :", &g);
        printf("=== END ENVIRON ===\n\n");

        time_t tt = get_test_time();
        printf("fix test (16.6.2011 04:53) --> %s\n", ctime(&tt));

        printf("done.\n");
        return 0;
    

在它产生的 GNU/Linux 上运行:

=== ENVIRON ===
TZ = (null)
Local time : Sat, 24.05.111 14:20
utc        : Sat, 24.05.111 12:20
=== END ENVIRON ===

final msg_time = 1308199980
fix test (16.6.2011 04:53) --> Thu Jun 16 06:53:00 2011

done.

在 Win7 上运行它会产生:

=== ENVIRON ===
TZ = (null)
Local time : Sat, 24.05.111 14:25
utc        : Sat, 24.05.111 12:25
=== END ENVIRON ===

final msg_time = 1308196380
fix test (16.6.2011 04:53) --> Thu Jun 16 05:53:00 2011

done.

两个系统的时区均为 UTC+1,包括 DST(使 UTC+2 生效),并且两个系统都没有任何时间问题 - 除了显示的差异。

如您所见,“最终 msg_time”正好缺少 3600 秒,因此在 ctime 中不是问题。

谁能向我解释为什么 mktime 在 GNU/Linux 和 Windows 上表现不同 - 或者如何纠正?

编辑: 两个系统(在调用tzset() 之后)都报告 tzname[0] = CET, tzname[1] = CEST, daytime=1, timezone = -3600

【问题讨论】:

ctime 在调用变量 tzname 后设置。之后您可以检查它们在两个系统上是否相等。 flolo:我添加了 tzname 的值。但是 - time_t msg_time 不同,所以我怀疑问题出在 ctime 上。 【参考方案1】:

我假设根据您提供的夏令时生效的信息,您需要将get_test_time() 中的msg_time.tm_isdst 设置为1 的值,而不是0。这可能是缺少时间的问题。要么这样,要么您可以将其设置为 -1 并允许系统尝试确定您是否处于夏令时,对于给定的输入值。

【讨论】:

Jason,不 - 修改 tm_isdst 会修改结果,但不会使 gnu/linux 的结果与 windows 相同。事实上 get_test_time 应该以 UTC 格式返回 time_t - 这就是为什么我使用 mb_timegm(timegm 的替代品)而不是 timelocal【参考方案2】:

我的 mb_timegm 是基于 man 3 timegm 中所述的代码,它声明 "set the TZ environment variable to UTC" 会调用 setenv("TZ", "");

但是 - 这在 Windows 上不起作用。

使用setenv("TZ", "UTC");(或者,在上述情况下为mb_setenv)来解决问题。

【讨论】:

以上是关于c mktime 在 Windows 和 GNU/Linux 上是不是不同?的主要内容,如果未能解决你的问题,请参考以下文章

在 Linux 下使用 gcc 在 C 中的夏令时和 mktime

《linux 内核全然剖析》 mktime.c

c_cpp 这在Windows,Linux,* BSD和Mac OS X上提供了endian.h的endian转换函数。你仍然需要使用-std = gnu99代替

奇怪的 mktime() 行为

Windows环境下CodeBlocks导入GNU Regex Library 正则表达式

转换不存在的 struct tm 时的 mktime 问题(因为日光变化时间)