系统调用mktime忽略tm_isdst的标志
另外一个问题,关于mktime和DST系统调用mktime忽略tm_isdst的标志
的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动物有答案: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
我想我现在可以看到如何找到这个令人困惑。想的mktime()
为具有签名
time_t mktime_actual(struct tm *dst, const struct tm *src);
其中time_t
结果基于(归一化)*src
计算,并且归一化的领域和夏令时间是否适用在那个时候,被保存到*dst
。
这只是C语言开发人员历史上选择只使用一个指针,结合src
和dst
。但是,上述逻辑仍然存在。
见`man mktime手册页,特别是这部分:
的mktime()函数将一个破旧的时间结构, 表示为本地时间,日历时间表示。 函数将忽略tm_wday和 tm_yday字段中由调用方提供的值。在tm_isdst字段中指定的值通知 mktime()夏时制时间(DST)是否生效 在tm结构中提供的时间:正值表示DST为 生效;零意味着DST不起作用;而负值 意味着mktime()应该(使用时区信息和系统 数据库)尝试确定DST是否在 指定的时间生效。
()函数修改tm结构作为 领域的mktime如下:tm_wday和tm_yday被设置为从所述其他字段的内容 确定的值;如果结构成员超出其有效时间间隔,它们将被标准化(例如,10月份的月份更改为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也是,但没有检查过)。
成功完成后,该结构的
tm_wday
和tm_yday
分量的值被适当地设定,并且被设置其他组件来代表指定的日历 时间... C11dr§7.27.2.32
致电mktime(&tm)
时,tm
的原始值不受范围限制。
因为第一mktime(&tm)
电话,肯定tm.tm_isdst
和tm.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)行为似乎很奇怪。在调用'mktime(&tm);'为'mktime()')后,可以调整'tm'字段来重新设置所有**字段printf(“%i \ n”,(int)secs);'或'printf(“%lld \ n”,(long long)secs);'以避免未定义的行为。 – chux
它可能看起来很奇怪,但它确实不奇怪。的确,['mktime()'](http://man7.org/linux/man-pages/man3/mktime.3.html)修改了它的论点 - 特别是'tm.tm_hour'和'tm。 tm_isdst'字段。 –
@Nominal Animal什么是奇怪的是'mktime()'不会改变'tm',因为'tm'在前2个主要范围内。 _ahh_可能不在第一种情况。嗯 - 在夏季的0 dst可能会更改为1. – chux