将 UTC 日期和时间转换为 UNIX 时间会给出错误的(时区不同)值。为啥?

Posted

技术标签:

【中文标题】将 UTC 日期和时间转换为 UNIX 时间会给出错误的(时区不同)值。为啥?【英文标题】:Conversion of UTC Date and Time to UNIX Time gives wrong (time zone different) value. Why?将 UTC 日期和时间转换为 UNIX 时间会给出错误的(时区不同)值。为什么? 【发布时间】:2019-06-15 18:55:36 【问题描述】:

我编写了一个小函数来将 UTC 中的日期和时间转换为 UNIX 时间(纪元时间)。然而,我得到的价值取决于我所在的时区。

这里是代码

#include<stdio.h>
#include<time.h>

time_t GenerateUnixTimeStampFromDateAndTime(char *DateAndTime);

void main()

    long int UnixTime=0;
    char *CurrentTime="01/22/2019 06:30:00";

    UnixTime = (long int)GenerateUnixTimeStampFromDateAndTime(CurrentTime);
    printf("Current Unix Time= %ld\r\n", UnixTime);


time_t GenerateUnixTimeStampFromDateAndTime(char *DateAndTime) 

    struct tm ti=0;
    if( sscanf(DateAndTime, "%d/%d/%d %d:%d:%d", &ti.tm_mon, &ti.tm_mday, &ti.tm_year, &ti.tm_hour, &ti.tm_min, &ti.tm_sec) != 6 )
        return -1;
    ti.tm_year = ti.tm_year - 1900;
    ti.tm_mon = ti.tm_mon - 1;

    return mktime(&ti);

我得到的答案是 1548118800,即 01/22/2019 01:00:00 即 -5:30,这是我所在的时区(印度)。如果我将我的 PC 时区更改为 UTC,那么它会给出正确的值 1548138600。

我需要进行哪些更改才能使其与时区无关?

【问题讨论】:

将 LC_TIME 区域设置为“UTC”。 @KamilCuk 这是操作系统级别的命令还是c代码级别的命令? 【参考方案1】:

如文档所述,mktime() 采用当地时间的细分时间组件。

首先,保存用户当前时区:

char  *old_timezone, *temp;

temp = getenv("TZ");
if (temp) 
    const size_t  len = temp;
    old_timezone = malloc(len + 1);
    if (!old_timezone) 
        fprintf(stderr, "Out of memory!\n");
        exit(EXIT_FAILURE);
    
    if (len > 0)
        memcpy(old_timezone, temp, len);
    old_timezone[len] = '\0';
 else
    old_timezone = NULL;

如果用户使用系统默认值,old_timezone将为NULL。

接下来,将当前时区(对于这个进程)设置为 UTC:

setenv("TZ", "UTC", 1);
tzset();

请注意,如果您愿意,可以使用任何时区说明符代替上面的"UTC";有关详细信息,请参阅tzset()tzset() 调用通常由您的 C 库在内部完成,但在这里明确地执行它可以帮助我们人类看到刚刚发生的特定时区的事情。

此时,mktime() 将以 UTC 运行,localtime()gmtime() 将返回相同的结果。

之后,通过

恢复时区
if (old_timezone) 
    setenv("TZ", old_timezone, 1);
    free(old_timezone);
    old_timezone = NULL;
 else
    unsetenv("TZ");
tzset();

请注意,这不会影响除当前进程(以及您可能通过popen()system()fork()exec() 创建的任何子进程)之外的任何内容。时区与区域设置一样,是每个进程的属性。


跑步

    unsetenv("TZ");
    tzset();

会将此进程的当前时区更改为系统默认时区。


如果您的程序在 UTC 中明确运行,您可以这样做

    /* This program works explicitly in the UTC timezone.
       User/system timezone configuration is completely ignored. */
    setenv("TZ", "UTC", 1");
    tzset();

main() 的开头附近。

【讨论】:

以上是关于将 UTC 日期和时间转换为 UNIX 时间会给出错误的(时区不同)值。为啥?的主要内容,如果未能解决你的问题,请参考以下文章

将 unix 时间转换为 UTC 日期时间

在 VBA 中,如何以简单的方式将 UTC UNIX 时间戳转换为本地时区日期?

Ruby:将unix时间戳转换为日期[重复]

将本地时间转换为 UTC 时间(日期时间对象)

oracle 将 unix 纪元时间转换为日期

Python pytz:将本地时间转换为 UTC。本地化似乎没有转换