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

Posted

技术标签:

【中文标题】转换不存在的 struct tm 时的 mktime 问题(因为日光变化时间)【英文标题】:mktime issue when converting a struct tm that doesn't exist (because of the daylight changing time) 【发布时间】:2012-08-28 09:12:15 【问题描述】:

我有一个 mktime 问题,如果由于 DST 更改而导致小时不存在,那么使用 MSVS 2010 的 windows 上的 mktime 将返回过去的 time_t,在我的情况下为 23:00,它应该返回 1:00AM第二天(在 Linux 上它应该返回凌晨 1:00)。我的问题发生在巴西时区(格林威治标准时间 -3)上,恰好是自动调整夏令时发生的时间。在他们的情况下,这发生在 2012 年 10 月 21 日凌晨 0:00(这将变为凌晨 1:00)。

这是部分代码:

/* test_date1.cpp : Defines the entry point for the console application.
*
*/
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int _tmain(int argc, _TCHAR* argv[])


time_t mytime=1350784881;
struct tm *timeinfo;
char *tz;

/*time ( &mytime ); */
timeinfo = localtime ( &mytime );

printf("%.2d/%.2d/%.4d, %.2d:%.2d isdst?=%d\n", 
timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year,  timeinfo->tm_hour, timeinfo-> tm_min, timeinfo -> tm_isdst);

timeinfo->tm_mday=21;
timeinfo->tm_mon=9;
timeinfo->tm_year=112;
timeinfo->tm_hour=0;
timeinfo->tm_min=0;
timeinfo->tm_isdst=-1;

printf("The shit: %.2d/%.2d/%.4d, %.2d:%.2d isdst?=%d\n", 
    timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year,  timeinfo->tm_hour, timeinfo-> tm_min, timeinfo -> tm_isdst);

mytime= mktime(timeinfo);
printf("mytime is=%d\n", mytime);

timeinfo = localtime ( &mytime );

printf("%.2d/%.2d/%.4d, %.2d:%.2d isdst?=%d\n", 
    timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year,  timeinfo->tm_hour, timeinfo-> tm_min, timeinfo -> tm_isdst);

return 0;

Windows 上的结果是:

20/09/0112, 23:01 isdst?=0
21/09/0112, 00:00 isdst?=-1
mytime is=1350784800
20/09/0112, 23:00 isdst?=0

在 Linux 上是:

20/09/0112, 23:01 isdst?=0
21/09/0112, 00:00 isdst?=-1
mytime is=1350788400
21/09/0112, 01:00 isdst?=1

如您所见,在 time_t 从 Unix 返回 C 和从 Microsoft Visual Studio 2010 返回 C 之间,mytime 的差异是 3600 秒。

这个程序应该在不同的平台(UNIX/WINDOWS/etc)和任何时区上运行,所以我不应该对时区进行硬编码。

正如您所见,问题出在 Windows 上,错误地返回了时间。在这一刻,我不知道如何解决这个问题。有人遇到过这个特殊问题吗?你是怎么解决的?我特别需要当地日的开始。

非常感谢,

玩笑

【问题讨论】:

当您提出此类问题时,请务必记录操作系统版本。看看 TZ 环境变量给你带来了什么。 【参考方案1】:

我不知道 mktime 的行为是否在您传递无效时间时被定义,所以这实际上可能不是错误。

无论如何,除了自己重新实现 mktime 之外,您还需要解决这个问题。我提出以下算法:

为当天上午 9 点构建一个 struct tm。 转换为 time_t。 从 time_t 中减去 24 小时。

如果没有夏令时变化,那么此时您将看到前一天上午 9 点。如果夏令时开始了,你会看到早上 8 点;如果它完成了,你正在看上午 10 点。

转换回 struct tm。 将 struct tm 中的日期与原始日期进行比较。 如果还不是正确的日期,请将 time_t 增加一个小时,然后重试。

由于您一次只添加一个小时,一旦您获得所需的日期,您就可以确定您获得了该日期的最早时间。

我做了一些我认为目前安全的假设:夏令时永远不会在一个小时内开始或结束,它永远不会在上午 8 点到 10 点之间开始或结束,并且它的变化永远不会超过一个小时。

我故意假设夏令时永远不会在晚上开始或结束(例如,晚上 11 点变成午夜,反之亦然)或清晨(例如,凌晨 2 点变成凌晨 3 点,反之亦然) - 反之亦然,这正是这里发生的事情)。

【讨论】:

以上是关于转换不存在的 struct tm 时的 mktime 问题(因为日光变化时间)的主要内容,如果未能解决你的问题,请参考以下文章

三种时间格式的转换

为啥 struct tm 的 tm_gmtoff 字段未在手册页中记录?

由“闰年”判断想到的日期与字符串间的转换

timegm, mktime 改变 struct tm

如何从 ctime 巧妙地初始化 struct tm

时间函数的使用