MinGW localtime\r在一个时区工作,在另一个时区失败

MinGW localtime\r在一个时区工作,在另一个时区失败,c,mingw,unix-timestamp,epoch,localtime,C,Mingw,Unix Timestamp,Epoch,Localtime,我有以下文件test.c: #define _POSIX_THREAD_SAFE_FUNCTIONS #include <time.h> #include <stdio.h> #include <string.h> #include <stdint.h> #include <inttypes.h> int main(int argc,char**argv) { struct tm t1, t2, t3;

我有以下文件test.c:

 #define _POSIX_THREAD_SAFE_FUNCTIONS
 #include <time.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdint.h>
 #include <inttypes.h>

 int main(int argc,char**argv) {
    struct tm t1, t2, t3;
    time_t w1, w2, w3;
    memset(&t1,0,sizeof(struct tm));
    memset(&t2,0,sizeof(struct tm));
    memset(&t3,0,sizeof(struct tm));
    w1 = 0;
    errno = 0;
    localtime_r(&w1,&t1);
    printf("localtime_r: errno=%d\n",errno);
    errno = 0;
    w2 = mktime(&t1);
    printf("mktime: errno=%d result=%" PRId64 "\n",errno,((int64_t)w2));
    errno = 0;
    localtime_r(&w2,&t2);
    printf("localtime_r: errno=%d\n",errno);
    errno = 0;
    w3 = mktime(&t2);
    printf("mktime: errno=%d result=%" PRId64 "\n",errno,((int64_t)w3));
    errno = 0;
    localtime_r(&w3,&t3);
    printf("localtime_r: errno=%d\n",errno);
    printf("sizeof(time_t)=%" PRId64 "\n", ((int64_t)sizeof(time_t)));
    printf("W1=%" PRId64 " W2=%" PRId64 " W3=%" PRId64 "\n",((int64_t)w1),((int64_t)w2),((int64_t)w3));
    printf("Y1=%d Y2=%d Y3=%d\n",t1.tm_year,t2.tm_year,t3.tm_year);
    return 0;
 }
注意,
i686-w64-mingw32-gcc--version
报告
8.3-win32 20190406
这是在Ubuntu19.04的Docker映像中运行的,使用的是MinGW版本 这是Ubuntu19.04(版本6.0.0-3)附带的

我有一个Windows10VM(版本1809OS构建17763.379)。 默认情况下,时区设置为美国太平洋时间(UTC-8)。 我将test.exe复制到此VM并在那里运行它

它打印:

localtime_r: errno=0
mktime: errno=0 result=0
localtime_r: errno=0
mktime: errno=0 result=0
localtime_r: errno=0
sizeof(time_t)=8
W1=0 W2=0 W3=0
Y1=69 Y2=69 Y3=69
localtime_r: errno=0
mktime: errno=0 result=47244640256
localtime_r: errno=22
mktime: errno=22 result=4294967295
localtime_r: errno=0
sizeof(time_t)=8
W1=0 W2=47244640256 W3=4294967295
Y1=70 Y2=-1 Y3=206
这是预期的结果。(1970年1月1日UTC午夜,UTC-8的时间仍然是1969年。)

我将Windows时区更改为UTC+10(堪培拉、墨尔本、悉尼)。 再运行一次。它打印:

localtime_r: errno=0
mktime: errno=0 result=0
localtime_r: errno=0
mktime: errno=0 result=0
localtime_r: errno=0
sizeof(time_t)=8
W1=0 W2=0 W3=0
Y1=69 Y2=69 Y3=69
localtime_r: errno=0
mktime: errno=0 result=47244640256
localtime_r: errno=22
mktime: errno=22 result=4294967295
localtime_r: errno=0
sizeof(time_t)=8
W1=0 W2=47244640256 W3=4294967295
Y1=70 Y2=-1 Y3=206
看起来mktime()调用在UTC+10时区返回的值无效,但在UTC-8时区返回的值正确为0

为什么这段代码在一个时区和另一个时区之间工作

注意,这只是
-D_u_umingw_USE_VC2005_COMPAT=1
启用时出现的问题 64位时间。如果我不考虑它,这意味着32位时间,那么代码
在两个时区工作。(但是,32位时间不是一个好主意,因为它在2038年中断,现在离这不到20年了。)

我找到了问题的原因。Sander De Dycker的建议是正确的,即
mktime
返回32位值

问题基本上是这样的:MSVCRT定义了三个
mktime
函数:
\mktime32
用于32位时间,
\mktime64
用于64位时间,以及
\mktime32
的传统别名
\mktime32

除非您使用VC2005兼容来禁用,否则
\define>是否在32位代码中使用32位时间
。一旦你有了
#define u MINGW u USE VC2005 COMPAT
,localtime就被定义为一个内联函数,它调用了localtime64。而
#define(定义)POSIX(线程)SAFE(函数)
localtime(r)
定义为一个内联函数,该函数调用
localtime
。但是,
mktime
仍然是32位。要获取64位
mktime
,您还需要定义MSVCRT版本0x1400
(或更高版本)。一旦这样做,
mktime
就变成了一个内联函数,它调用
\u mktime64
。在此之前,
mktime
是一个普通函数声明,它链接到传统的32位
mktime

因此
\define\u MINGW\u USE\u VC2005\u COMPAT 1
\define\u MSVCRT\u VERSION\uuuuuuuu1400
(或
-D
等价物)提供了一个带有64位
时间的
localtime\r
,但带有32位
时间的
mktime
,显然不起作用。更糟糕的是,
mktime
符号的实际实现返回32位的
time\t
,但函数声明是针对64位的
time\t
,这就是导致上32位出现垃圾的原因


至于不同时区的不同行为,我没有完整的解释,但我认为原因可能如下:当你有一个函数实际返回一个32位的值,但被错误地定义为返回一个64位的值,返回值的上32位将保存以前计算中留下的随机垃圾数据。因此,先前计算中的任何差异,或稍微不同的代码路径,都可能导致不同的随机垃圾。对于UTC-8时区,无论出于何种原因,随机垃圾都恰好为零,因此代码(尽管不正确)实际上可以工作。使用UTC+10时区时,随机垃圾结果为非零,这会导致其余代码停止工作。

如果使用:
printf(“W1=%lld W2=%lld W3=%lld\n”,((long long)W1),((long long)W2),((long long)W3))?@sanderedycker谢谢,你说得对<代码>%d
是错误的printf格式,因为它是64位值。要用调整后的代码更新我的问题,执行此操作后,问题似乎有所不同。注意:47244640256==0xB00000000,当然是错误值。我怀疑问题被简化为为什么
w2=mktime(&t1)表现得不稳定。在
mktime()
之前和之后打印
t1
的全部内容可能是说明性的。可能是先前的
localtime\r(&w1,&t1)将垃圾放入
t1
-或者是
mktime(&t1)
这是错误的?可能是
mktime
返回一个32位的值(即
0x00000000
),但调用者需要一个64位的值(添加
0xB
垃圾)?这可能是abi不匹配吗?或者iow:实现
mktime
的库是否也使用
-D\u MINGW\u USE\u VC2005\u COMPAT=1
或同等版本构建?@SanderDeDycker谢谢,我刚刚这么做了。