MinGW localtime\r在一个时区工作,在另一个时区失败
我有以下文件test.c: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;
#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谢谢,我刚刚这么做了。