繁体   English   中英

mktime() 用于非本地时区

[英]mktime() for non-local timezone

在 C 中,function mktime() 根据本地时区返回纪元时间(输入结构是本地格式化的)。

function timegm() 根据 UTC 时间返回纪元时间(输入结构基于 UTC 时间格式化)。

function localtime_r () 接受纪元时间并返回本地时区格式的结构。

function gmtime_r () 接受纪元时间并返回 UTC 格式的结构。

我需要查明非本地时区当前是否为夏令时,如果它是本地时区,这将与 localtime_r() function 一起使用,但如果不是本地时区怎么办?

gmtime_r() function 总是将 tm_isdst 字段设置为零,这在这里不起作用。

也许还有一些我不知道的其他 function。 不确定。

如果您 (a) 不想弄乱全局环境变量并且 (b) 有“受 NetBSD 启发”的时间函数可供您使用,还有一个额外的可能性: mktime_z()localtime_rz() ,它们可以让您明确指定要使用的区域。 因此,您不限于默认本地区域或 UTC。

这是一个例子:

int main(int argc, char **argv)
{
    timezone_t tzp = tzalloc(argv[1]);
    if(tzp == NULL) return 1;
    time_t now = time(NULL);
    struct tm tm;
    struct tm *tmp = localtime_rz(tzp, &now, &tm);
    char tmpbuf[20];
    strftime(tmpbuf, sizeof(tmpbuf), "%H:%M:%S", tmp);
    printf("right now in zone %s is %s\n", argv[1], tmpbuf);

    tm.tm_year = 1976 - 1900;
    tm.tm_mon = 7 - 1;
    tm.tm_mday = 4;
    tm.tm_hour = 12;
    tm.tm_min = tm.tm_sec = 0;
    tm.tm_isdst = -1;

    time_t t = mktime_z(tzp, &tm);

    printf("in zone %s, %d-%02d-%02d %d:%02d was %ld\n", argv[1],
        1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, t);
}

当我调用tzt America/New_York时,我看到了

right now in zone America/New_York is 11:58:23
in zone America/New_York, 1976-07-04 12:00 was 205344000

当我调用tzt America/Los_Angeles时,我看到了

right now in zone America/Los_Angeles is 08:58:49
in zone America/Los_Angeles, 1976-07-04 12:00 was 205354800

现在,话虽如此,还有两条评论与我的开头“如果”相关:

一种。 如果您不想弄乱全局环境变量,我一点也不会责怪您。 我非常讨厌使用全局变量(更不用说环境变量)来影响像mktimelocaltime这样的 function 的行为。 然而,不幸的是,在 C 中,在这种情况下,这是推荐的方式 — 有关详细信息,请参阅此问题的其他答案12

b. 不幸的是,您很有可能没有,事实上,“您可以使用受 NetBSD 启发的时间函数”。 它们是非标准的,甚至不是很受欢迎。 我能够编译上面的测试程序只是因为我手边有IANA tz 数据库及其代码的副本,如果您还定义NETBSD_INSPIRED ,其中包括那些函数。 (这就是为什么我违反了规则,没有展示一个完整的例子,包括所有#include行,因为我的例子很奇怪而且很特别。)

我需要查明非本地时区当前是否为夏令时

  1. time获取当前时间作为time_t
  2. 使用putenv将 env var TZ设置为目标时区。
  3. 调用tzset (我不知道这是否是必需的,但调用它肯定没有坏处。)
  4. 根据(修改后的)本地时区,使用localtimetime_t转换为struct tm
  5. 检查该struct tmtm_isdst字段。

这是我十年前写的一些代码,它们执行ikegami在他们的回答中建议的内容:

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

static void time_convert(time_t t0, char const *tz_value)
{
    char old_tz[64] = "-none-";
    char *tz = getenv("TZ");
    if (tz != 0)
        strcpy(old_tz, tz);
    setenv("TZ", tz_value, 1);
    tzset();
    char new_tz[64];
    strcpy(new_tz, getenv("TZ"));
    char buffer[64];
    struct tm *lt = localtime(&t0);
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", lt);
    if (strcmp(old_tz, "-none-") == 0)
        unsetenv("TZ");
    else
        setenv("TZ", old_tz, 1);
    tzset();
    printf("%lld = %s (TZ=%s, DST = %d)\n",
           (long long)t0, buffer, new_tz, lt->tm_isdst);
}

int main(void)
{
    time_t t0 = time(0);
    char *tz = getenv("TZ");
    if (tz != 0)
        time_convert(t0, tz);
    time_convert(t0, "UTC0");
    time_convert(t0, "IST-5:30");
    time_convert(t0, "EST5");
    time_convert(t0, "EST5EDT");
    time_convert(t0, "PST8");
    time_convert(t0, "PST8PDT");
}

我得到的 output 是:

1650647033 = 2022-04-22 17:03:53 (TZ=UTC0, DST = 0)
1650647033 = 2022-04-22 22:33:53 (TZ=IST-5:30, DST = 0)
1650647033 = 2022-04-22 12:03:53 (TZ=EST5, DST = 0)
1650647033 = 2022-04-22 13:03:53 (TZ=EST5EDT, DST = 1)
1650647033 = 2022-04-22 09:03:53 (TZ=PST8, DST = 0)
1650647033 = 2022-04-22 10:03:53 (TZ=PST8PDT, DST = 1)

请注意,某些时区没有指定夏令时,并且代码报告这些时区的DST = 0

警惕在多线程应用程序中像这样更改时区。 并且在重置环境时要小心,以防你fork()exec()其他程序具有意外的 TZ 环境变量值。

注意:我已将代码修改为:

  • 使用long long而不是long来打印time_t值。 令人讨厌的事情之一是没有标准格式字符串用于将time_t类型打印为 integer(实际上,C 标准甚至不能保证该类型是 integer,但它实际上在大多数系统上都是如此,并且 POSIX 要求它是 integer 类型——参见<sys/types.h> )。 此更改应允许 32 位系统在 2038 年工作。假设time_t是 64 位值,即使它是 32 位系统; 如果系统仍然使用 32 位time_t ,当time_t回绕到 -2 31时,它最终会被破坏——但这不应该仍然是我的问题。
  • 打印lt->tm_isdst ,这是问题中需要的信息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM