gpt4 book ai didi

c - MinGW localtime_r 在一个时区工作,在另一个时区失败

转载 作者:太空宇宙 更新时间:2023-11-03 23:37:00 25 4
gpt4 key购买 nike

我有以下文件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 -D__MINGW_USE_VC2005_COMPAT=1 -o test.exe test.c

请注意,i686-w64-mingw32-gcc --version 报告 8.3-win32 20190406这是在 Ubuntu 19.04 的 Docker 镜像中运行的,使用的是 MinGW 版本Ubuntu 19.04 附带的(它说版本 6.0.0-3)。

我有一个 Windows 10 虚拟机(版本 1809 操作系统内部版本 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

这是预期的结果。 (在 1970 年 1 月 1 日的 UTC 午夜,UTC-8 仍是 1969 年。)

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

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__MINGW_USE_VC2005_COMPAT=1 启用的问题64 位时间_t。如果我忽略它,这意味着 32 位 time_t,那么代码在两个时区工作。 (但是,32 位 time_t 不是一个好主意,因为它在 2038 年中断,而距现在不到二十年。)

最佳答案

我找出了问题的原因。 Sander De Dycker 关于 mktime 返回 32 位值的建议是正确的。

问题基本上是这样的:MSVCRT 定义了三个 mktime 函数:_mktime32 用于 32 位 time_t,_mktime64 用于 64 位 time_t和 _mktime,它是 _mktime32 的旧别名。

_mingw.h在 32 位代码中执行 #define _USE_32BIT_TIME_T,除非您 #define __MINGW_USE_VC2005_COMPAT 禁用它。一旦你有了 #define __MINGW_USE_VC2005_COMPAT,localtime_s 就被定义为调用 _localtime64_s 的内联函数。 #define _POSIX_THREAD_SAFE_FUNCTIONSlocaltime_r 定义为调用 localtime_s 的内联函数。然而,mktime 仍然是 32 位的。要获得 64 位 mktime,您还需要 #define __MSVCRT_VERSION__ 0x1400(或更高版本)。执行此操作后,mktime 将成为调用 _mktime64 的内联函数。在此之前,mktime 是链接到遗留 32 位 mktime 的普通函数声明。

所以 #define __MINGW_USE_VC2005_COMPAT 1 没有 #define __MSVCRT_VERSION__ 0x1400(或 -D 等效)给你一个 localtime_r 使用 64 位 time_t,但是 mktime 使用 32 位 time_t,这显然行不通。更糟糕的是,mktime 符号的实际实现是返回一个 32 位的 time_t,但函数声明是针对 64 位的 time_t,这就是导致高 32 位出现垃圾的原因。

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

关于c - MinGW localtime_r 在一个时区工作,在另一个时区失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57885666/

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