gpt4 book ai didi

c - 为什么 timelocal 和 mktime 无法正确处理夏令时?

转载 作者:行者123 更新时间:2023-11-30 19:52:35 34 4
gpt4 key购买 nike

我试图了解 UNIX timelocal 和 mktime 的工作原理。据说,当您在 struct tm tm_isdst 字段中传递正确的值时,它们会处理夏令时。

我正在测试一个非常特定的时刻。根据“America/New_York”的时区数据库,夏令时于 2005 年 10 月 30 日 01:00 发生变化。以下是 zdump -v America/New_York 的输出,您可以在自己的系统上确认。我仅显示 2005 年左右的数据子集(向右滚动以查看 gmtoff 值):

America/New_York  Sun Apr  3 06:59:59 2005 UT = Sun Apr  3 01:59:59 2005 EST isdst=0 gmtoff=-18000America/New_York  Sun Apr  3 07:00:00 2005 UT = Sun Apr  3 03:00:00 2005 EDT isdst=1 gmtoff=-14400America/New_York  Sun Oct 30 05:59:59 2005 UT = Sun Oct 30 01:59:59 2005 EDT isdst=1 gmtoff=-14400America/New_York  Sun Oct 30 06:00:00 2005 UT = Sun Oct 30 01:00:00 2005 EST isdst=0 gmtoff=-18000America/New_York  Sun Apr  2 06:59:59 2006 UT = Sun Apr  2 01:59:59 2006 EST isdst=0 gmtoff=-18000America/New_York  Sun Apr  2 07:00:00 2006 UT = Sun Apr  2 03:00:00 2006 EDT isdst=1 gmtoff=-14400

To test this transition I am setting up a struct tm to contain 01:30 on that specific day. If I pass 0 for tm_isdst it should give me a gmtoffset of -18000. If I pass 1 and enable daylight savings, then gmtoffset should be -14400.

Here's the code I'm using to test on both Darwin/OSX and FreeBSD:

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


void print_tm(struct tm* tm) {
printf("tm: sec [%d] min [%d] hour [%d] mday [%d] mon [%d] year [%d] wday [%d] yday [%d] isdst [%d] zone [%s] gmtoff [%ld]\n",
tm->tm_sec,
tm->tm_min,
tm->tm_hour,
tm->tm_mday,
tm->tm_mon + 1,
tm->tm_year,
tm->tm_wday,
tm->tm_yday + 1,
tm->tm_isdst,
tm->tm_zone,
tm->tm_gmtoff);
}

struct tm* set_tm(int sec, int min, int hour, int mday, int mon, int year, int wday, int yday, int isdst, int gmtoff, char* zone) {
struct tm* tm;

tm = malloc(sizeof(struct tm));
memset(tm, 0, sizeof(struct tm));

tm->tm_sec = sec;
tm->tm_min = min;
tm->tm_hour = hour;
tm->tm_mday = mday;
tm->tm_mon = mon - 1;
tm->tm_year = year;
tm->tm_wday = wday;
tm->tm_yday = yday - 1;
tm->tm_isdst = isdst;
tm->tm_zone = zone;
tm->tm_gmtoff = gmtoff;

return tm;
}

void test_timelocal(struct tm* tm, int isdst) {
time_t seconds = -1;

if(!setenv("TZ", "America/New_York", 1)) {
printf("isdst is [%d]\n", isdst);
tm->tm_isdst = isdst;

tzset();
seconds = timelocal(tm);

localtime_r(&seconds, tm);
print_tm(tm);
} else {
printf("setenv failed with [%s]\n", strerror(errno));
}

printf("\n");
}

void test_mktime(struct tm* tm, int isdst) {
time_t seconds = -1;

if(!setenv("TZ", "America/New_York", 1)) {
printf("isdst is [%d]\n", isdst);
tm->tm_isdst = isdst;

tzset();
seconds = mktime(tm);

localtime_r(&seconds, tm);
print_tm(tm);
} else {
printf("setenv failed with [%s]\n", strerror(errno));
}

printf("\n");
}

int main(void) {
struct tm* tm;

printf("Test with timelocal\n");
tm = set_tm(0, 30, 1, 30, 10, 2005, 0, 0, 0, 0, "");
test_timelocal(tm, 0);

tm = set_tm(0, 30, 1, 30, 10, 2005, 0, 0, 0, 0, "");
test_timelocal(tm, 1);

tm = set_tm(0, 30, 1, 30, 10, 2005, 0, 0, 0, 0, "");
test_timelocal(tm, -1);


printf("Test with mktime\n");
tm = set_tm(0, 30, 1, 30, 10, 2005, 0, 0, 0, 0, "");
test_mktime(tm, 0);

tm = set_tm(0, 30, 1, 30, 10, 2005, 0, 0, 0, 0, "");
test_mktime(tm, 1);

tm = set_tm(0, 30, 1, 30, 10, 2005, 0, 0, 0, 0, "");
test_mktime(tm, -1);

return 0;
}

在不同的操作系统上运行它会产生不同的结果。在 FreeBSD 上,此代码输出(向右滚动以查看 gmtoffset 值):

Test with timelocalisdst is [0]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [1] zone [EDT] gmtoff [-14400]isdst is [1]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [1] zone [EDT] gmtoff [-14400]isdst is [-1]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [1] zone [EDT] gmtoff [-14400]Test with mktimeisdst is [0]tm: sec [0] min [30] hour [2] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [1] zone [EDT] gmtoff [-14400]isdst is [1]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [1] zone [EDT] gmtoff [-14400]isdst is [-1]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [1] zone [EDT] gmtoff [-14400]

在 darwin/OSX 上,完全相同的代码会产生以下结果(向右滚动以查看 gmtoffset 值):

Test with timelocalisdst is [0]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [0] zone [EST] gmtoff [-18000]isdst is [1]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [0] zone [EST] gmtoff [-18000]isdst is [-1]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [0] zone [EST] gmtoff [-18000]Test with mktimeisdst is [0]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [0] zone [EST] gmtoff [-18000]isdst is [1]tm: sec [0] min [30] hour [0] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [0] zone [EST] gmtoff [-18000]isdst is [-1]tm: sec [0] min [30] hour [1] mday [30] mon [10] year [2005] wday [1] yday [303] isdst [0] zone [EST] gmtoff [-18000]

在我看来,他们俩都错了。 tm_isdst 字段似乎对 tm_gmtoff 字段没有影响。使用 mktime 时,tm_hour 输出发生变化,但偏移量仍然错误。

如果您将tm_mday更改为提前几天或几天后,gmtoffset根本不会改变,这让我感到困惑。

我做错了什么或者我误解了这些函数的工作原理吗?

最佳答案

UNIX 时间非常复杂。事实证明,我的错误与 struct tm 中的 tm_year 字段有关。它应该表示自 1900 年以来的年数,因此该字段中的值应该是 105 而不是 2005(例如 2005 - 1900 = 105)。现在就得出了正确的答案。

该结构的定义可以在 this very useful page. 上找到。

关于c - 为什么 timelocal 和 mktime 无法正确处理夏令时?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42308930/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com