无法在 C 中获得正确的本地时间

Cant get right localtime in C

我试图在 C 中获取当前本地时间,比方说意大利,我尝试了下面的代码,但它 return 比实际时间早三个小时。 例如,如果在 17:34 处执行,它将 return 14:34,我做错了什么?

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

int main()
{
    
    setenv("TZ", "UTC+1", 1); // Italy is UTC+1
    tzset();
    
    time_t sec = 1634224877; // Just for test
    
    struct tm * end = localtime(&sec);

    // Check for daylight save
    if(end->tm_isdst == 1)
    {
        end->tm_hour += 1;
    }

    printf("Daylight save time?: %d\n", end->tm_isdst);
    
    printf("Time is says: %.2d/%.2d/%d - %.2d:%.2d:%.2d\n", end->tm_mday, end->tm_mon + 1, end->tm_year + 1900,
                                     end->tm_hour, end->tm_min, end->tm_sec);
}

谢谢

tl;dr 如果您想要某个位置的时间,您必须将 TZ 设置为某个位置。


1634224877 是 2021-10-14 15:21:17 UTC。 setenv takes POSIX time zones which don't work like you think。 UTC+1 表示 UTC 的一个小时头。即14:21:17。如果你想要“UTC+1”,你实际上要求 UTC-1,比 UTC 晚一小时。

但不要要求 UTC-1


setenv("TZ", "UTC+1", 1); // Italy is UTC+1

该评论不正确。意大利不是 UTC+1。意大利有时是UTC+1,有时是UTC+2。为了了解夏令时(和其他古怪的时区问题),TZ 需要知道您的位置

将 TZ 设置为离您最近的城市,如 Europe/Rome。现在localtime可以判断是不是夏令时了,不需要更正了。

int main()
{
    setenv("TZ", "Europe/Rome", 1);
    tzset();

    time_t sec = 1634224877; // Just for test

    struct tm * end = localtime(&sec);

    printf("Daylight savings time?: %d\n", end->tm_isdst);

    printf("Time is says: %.2d/%.2d/%d - %.2d:%.2d:%.2d\n", end->tm_mday, end->tm_mon + 1, end->tm_year + 1900,
                                     end->tm_hour, end->tm_min, end->tm_sec);
}

Daylight savings time?: 1
Time is says: 14/10/2021 - 17:21:17

管理时区的系统叫做tzdata。它是一个包含位置、时区信息、夏令时开关和许多其他古怪时区信息的数据库。它让您的计算机知道罗马通常是 UTC+1,但有时应该是 UTC+2。

A list of all tzdata locations can be had on Wikipedia,但这些不一定与您机器上安装的 tzdata 匹配。

TZ 环境变量值 FOO+1(我将其从 UTC+1 更改为避免混淆)被解释为指定为“FOO”的标准时区,没有备用(夏令时) )区。 +1+ 是可选的)表示需要将当地时间添加 1 小时以将其转换为协调世界时 (UTC)。要指定备用(夏令时)时区,请将其添加到标准时间偏移量之后,例如FOO+1BAR0。备用区域后的偏移量可以省略,在这种情况下它默认为比标准偏移量小一,因此 FOO+1BAR0 可以缩短为 FOO1BAR。这意味着当标准时间生效时,当地时间将比 UTC 晚 1 小时,而当备用(夏令时)时间生效时,本地时间将是 UTC。可选地,偏移量可以包括分钟或分钟和秒,例如FOO+01:00BAR+00:00:00.

意大利在标准时间有效时(例如冬季)使用中欧时间(CET,对应UTC+1),而在交替(夏令时)时使用中欧夏令时(CEST,对应UTC+1)节省)时间有效(例如在夏季)。这可以通过 TZ 环境变量值 CET-1CEST-2CET-1CEST 来表示。请注意,TZ 环境变量中使用的偏移量与通常的约定相反。

TZ 具有前面提到的值之一和备用时间(例如 CET-1CEST)时,由系统库来使用一些任意的(并且对于大多数情况通常是不正确的)世界)规则来确定标准时间和备用时间之间转换的日期和时间。每年恰好两次转换的日期和时间的简单规则可以在 TZ 变量中的备用区域名称和偏移量之后进行编码,以逗号分隔。转换日期可以指定为 Mm.n.d,意思是 dn 周(从 1 开始计算)的第 天(0 = 星期日,1 = 星期一,...,6 = 星期六)第 43=]m 个月(1 = 一月,...,12 = 十二月)。 n = 5 被解释为一个月中的最后 dm。过渡日期也可以指定为 Jn,其中 n 是一年中的第几天,不包括 2 月 29 日(因此 3 月 1 日始终是一天60). (可选)过渡时间可以指定为 /time,其中 time 指定过渡日期的当前本地时间,在该时间过渡到其他时间,默认为 02:00:00。 time值可以像时区偏移量一样缩写,所以/2和/02:00:00一样,但不允许有前导加号或减号(由当前标准)。

意大利目前按照欧盟时区转换规则运行(该规则的废除目前已推迟),其中转换发生在 01:00 UTC,当地时间在 3 月的最后一个星期日提前 1 小时(M3.5.0) 并在 10 月的最后一个星期日 (M10.5.0) 撤退 1 小时。对于意大利,当地的过渡时间是三月02:00:00(前进到03:00:00)和十月03:00:00(退到02:00:00),所以规则是M3.5.0/2M10.5.3。 (M3.5.0/2 可以缩短为 M3.5.0,因为它使用默认的过渡时间。)

以下经过修改的代码可以显示意大利的时间,至少在当前的欧盟时区规则被废除之前:

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

int main()
{
    setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1); // Italy rules!
    tzset();

    time_t sec;
#if 0
    sec = 1634224877; // Just for test
#else
    sec = time(NULL); // For current time
#endif

    struct tm * end = localtime(&sec);

    printf("Daylight save time?: %d\n", end->tm_isdst);

    printf("Time is says: %.2d/%.2d/%d - %.2d:%.2d:%.2d\n",
           end->tm_mday, end->tm_mon + 1, end->tm_year + 1900,
           end->tm_hour, end->tm_min, end->tm_sec);
}