系统调用mktime忽略tm_isdst标志

时间:2016-08-22 14:14:01

标签: c linux unix posix mktime

关于mktime和DST的另一个问题

Linux,Ubuntu,时区设置为欧洲/柏林,即当前时间为CEST:

>date
Mon Aug 22 16:08:10 CEST 2016
>date --utc
Mon Aug 22 14:08:14 UTC 2016
到目前为止一切都还好。

现在我尝试运行以下代码:

#include <stdio.h>
#include <time.h>
int main()
{

    struct tm   tm = {0};
    int secs;

    tm.tm_sec = 0;
    tm.tm_min = 0;
    tm.tm_hour = 12;
    tm.tm_mon = 9 - 1;
    tm.tm_mday = 30;
    tm.tm_year = 2016 - 1900;

    tm.tm_isdst = 0;
    secs = mktime(&tm);
    printf("%i\n", secs);

    tm.tm_isdst = 1;
    secs = mktime(&tm);
    printf("%i\n", secs);

    tm.tm_isdst = -1;
    secs = mktime(&tm);
    printf("%i\n", secs);

    return 0;
}

并获取

1475233200
1475233200
1475233200

这三种情况都是错误的(1小时偏移):

>date -d @1475233200
Fri Sep 30 13:00:00 CEST 2016

所以我现在有点困惑,我的时区有点破碎吗?为什么完全忽略tm_isdst标志?

编辑:@Nominal Animal有答案:mktime修改tm_hour!我想知道它在哪里记录?!

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

void reset(struct tm* tm){
    (*tm) = (const struct tm){0};

    tm->tm_sec = 0;
    tm->tm_min = 0;
    tm->tm_hour = 12;
    tm->tm_mon = 9 - 1;
    tm->tm_mday = 30;
    tm->tm_year = 2016 - 1900;
}

int main()
{

    struct tm   tm;
    int secs;

    reset(&tm);
    tm.tm_isdst = 0;
    secs = mktime(&tm);
    printf("%i\n", secs);

    reset(&tm);
    tm.tm_isdst = 1;
    secs = mktime(&tm);
    printf("%i\n", secs);

    reset(&tm);    
    tm.tm_isdst = -1;
    secs = mktime(&tm);
    printf("%i\n", secs);

    return 0;
}

给出

1475233200
1475229600
1475229600

2 个答案:

答案 0 :(得分:1)

  

成功完成后,相应地设置结构的tm_wdaytm_yday组件的值,并设置其他组件以表示指定的日历   时间,......C11dr§7.27.2.32

调用mktime(&tm)时,tm的原始值不受范围限制。

由于第一个mktime(&tm)电话,当然tm.tm_isdsttm.tm_hour已调整为1和11.因此OP的代码为tm.tm_isdst = 1;tm.tm_isdst = -1;不影响时间戳。

最好设置所有要调查的字段。

struct tm   tm0 = {0};
struct tm   tm;
int secs;

tm0.tm_sec = 0;
tm0.tm_min = 0;
tm0.tm_hour = 12;
tm0.tm_mon = 9 - 1;
tm0.tm_mday = 30;
tm0.tm_year = 2016 - 1900;

tm = tm0;
tm.tm_isdst = 0;
secs = mktime(&tm);
printf("%i\n", (int) secs);

tm = tm0;
tm.tm_isdst = 1;
secs = mktime(&tm);
printf("%i\n", (int) secs);

tm = tm0;
tm.tm_isdst = -1;
secs = mktime(&tm);
printf("%i\n", (int) secs);

答案 1 :(得分:1)

我想我现在可以看到人们如何发现这种混乱。将mktime()视为签名

time_t mktime_actual(struct tm *dst, const struct tm *src);

根据(标准化)time_t计算*src结果,并且标准化字段以及当时是否应用夏令时,会保存到*dst

只是C语言开发人员历来选择只使用一个指针,将srcdst结合起来。不过,上述逻辑仍然存在。

请参阅`man mktime手册页,尤其是此部分:

  

mktime()函数转换故障时间结构,   以当地时间表示,以日历时间表示。该   函数忽略调用者在tm_wday和中提供的值   tm_yday字段。 tm_isdst字段中指定的值通知   mktime()是否夏令时(DST)生效   在tm结构中提供的时间:正值表示DST   有效;零表示DST无效;和负值   意味着mktime()应该(使用时区信息和系统   数据库,以试图确定DST是否有效   指定的时间。

     

mktime()函数将tm结构的字段修改为   如下:tm_wday和tm_yday设置为从中确定的值   其他领域的内容;如果结构成员在他们之外   有效间隔,它们将被标准化(例如,40   十月改为11月9日);设置tm_isdst(不管是什么   它的初始值为正值或分别为0   指示DST是否在指定时间生效。   调用mktime()还会将外部变量tzname设置为   有关当前时区的信息。

     

如果指定的细分时间无法表示为日历   时间(自纪元以来的秒数),mktime()返回(time_t)-1并且确实如此   不要改变破碎时间结构的成员。

换句话说,如果你改变测试程序,比如说

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

static const char *dst(const int flag)
{
    if (flag > 0)
        return "(>0: is DST)";
    else
    if (flag < 0)
        return "(<0: Unknown if DST)";
    else
        return "(=0: not DST)";
}

static struct tm newtm(const int year, const int month, const int day,
                       const int hour, const int min, const int sec,
                       const int isdst)
{
    struct tm t = { .tm_year  = year - 1900,
                    .tm_mon   = month - 1,
                    .tm_mday  = day,
                    .tm_hour  = hour,
                    .tm_min   = min,
                    .tm_sec   = sec,
                    .tm_isdst = isdst };
    return t;
}

int main(void)
{
    struct tm   tm = {0};
    time_t secs;

    tm = newtm(2016,9,30, 12,0,0, -1);
    secs = mktime(&tm);
    printf("-1: %04d-%02d-%02d %02d:%02d:%02d %s %lld\n",
           tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
           tm.tm_hour, tm.tm_min, tm.tm_sec, dst(tm.tm_isdst), (long long)secs);

    tm = newtm(2016,9,30, 12,0,0, 0);
    secs = mktime(&tm);
    printf(" 0: %04d-%02d-%02d %02d:%02d:%02d %s %lld\n",
           tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
           tm.tm_hour, tm.tm_min, tm.tm_sec, dst(tm.tm_isdst), (long long)secs);

    tm = newtm(2016,9,30, 12,0,0, 1);
    secs = mktime(&tm);
    printf("+1: %04d-%02d-%02d %02d:%02d:%02d %s %lld\n",
           tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
           tm.tm_hour, tm.tm_min, tm.tm_sec, dst(tm.tm_isdst), (long long)secs);

    return EXIT_SUCCESS;
}

然后运行它会产生输出

-1: 2016-09-30 12:00:00 (>0: is DST) 1475226000
 0: 2016-09-30 13:00:00 (>0: is DST) 1475229600
+1: 2016-09-30 12:00:00 (>0: is DST) 1475226000

换句话说,它的行为完全如上所述(在上面的引文中)。这种行为记录在C89,C99和POSIX.1中(我认为C11也是如此,但是没有检查过。)