C:如何在特定时区(按偏移量)打印特定的“时间”值?

Posted

技术标签:

【中文标题】C:如何在特定时区(按偏移量)打印特定的“时间”值?【英文标题】:C: How to print a particular `time` value in a particular timezone (by offset)? 【发布时间】:2011-07-02 14:37:51 【问题描述】:

我正在用 C 语言编写一个应用程序,它解析由外部程序记录的数据文件(我无法控制)。它存储二进制数据,其中一个字段是标准 UNIX“纪元”格式的时间(自 1970 年 1 月 1 日以来的秒数,UTC)。

另一个字段是时区,存储为距 UTC 的秒数。

太棒了,我已经拥有了创建一个日期/时间字符串所需的一切,该字符串表示记录时区中的该信息,对吧?嗯...看起来不是这样,和/或我不知道该怎么做。

我将事情归结为一个相当简单的测试用例:

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

int main(void)

    time_t t;
    struct tm *tm;
    char buf[BUFSIZ];

    int offset = 4980; /* slightly bizarre, just to test this - an hour
                        * and 23 minutes ahead of UTC */

    t = time(NULL);
    tm = localtime(&t);

    strftime(buf, BUFSIZ, "%FT%T%z", tm);
    printf("before: %s\n", buf);

    /* since we're not telling localtime anything different,
     * compensate here (by subtracting applied offset, and adding
     * desired one): */
    t += offset - tm->tm_gmtoff;
    tm = localtime(&t);

    tm->tm_zone = "XYZ"; // not used -- but it was in an earlier version
    tm->tm_gmtoff = offset;

    // on macos, I used to also have %+, which referenced tm_zone
    strftime(buf, BUFSIZ, "%FT%T%z", tm);
    printf("after:  %s\n", buf);

    return 0;

当我在 MacOS X 10.6 上运行它时,我得到:

before: 2011-02-23T00:53:04-0800
after:  2011-02-23T10:16:04-0800

我所期望的(实际上是在 Linux 机器上得到的)是:

before: 2011-02-23T00:53:04-0800
after:  2011-02-23T10:16:04+0123

我是否需要更改TZ 环境变量(并且可能调用tzset)?似乎应该有一种方法来操纵数据结构并获得正确的东西,但上面肯定行不通(无论如何,在 MacOS X 10.6 上——在 Linux 上运行得很好)。

作为一种解决方法,我想我可以从格式字符串中删除 %z 并自己创建该部分。

不过,理想情况下,我希望修改我的struct tm,或者我可以使用的其他一些函数调用(比如 strftime,但带有额外的参数或其他东西,或者可能是本地时间的另一种形式,而是),这将使事情做正确的事。

由于 Linux 似乎表现良好(尽管即使在那里,上述解决方案也不是很理想,因为我在捏造我的 time_t 值;我希望有一个参数来改变 struct tm 的计算方式),这是我应该报告为针对 MacOS 的错误吗?

或者,我可以调用一组不同的库例程,即使最终需要第三方(我想是来自 GNU 人的东西)库?尽管我会考虑使用 ObjC 或 C++ 选项,但我更愿意继续使用 C。

【问题讨论】:

【参考方案1】:

我更喜欢使用mktime() 而不是修改time_t 值。但是,这应用了TZ 偏移量(就像localtime() 解决方案一样),因此需要mkgmtime() 实现。

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

static void set_timezone(char *tz)

  static char var[1024];

  snprintf(var, sizeof(var), "TZ=%s", tz);
  putenv(var);
  tzset();


static time_t mkgmtime(struct tm *tm)

  char var[1024];
  time_t t;

  snprintf(var, sizeof(var), "%s", getenv("TZ") ? getenv("TZ") : "");

  set_timezone("GMT0");

  t = mktime(tm);

  set_timezone(var);

  return t;


int main(void)

  time_t t;
  struct tm tm;
  char buf[BUFSIZ];
  int offset = 4980; /* slightly bizarre, just to test this - an hour
                      * and 23 minutes ahead of UTC */

  t = time(NULL);
  tm = *localtime(&t);
  strftime(buf, BUFSIZ, "%FT%T%z", &tm);
  printf("before: %s\n", buf);

  tm = *gmtime(&t);
  tm.tm_sec += offset;
  mkgmtime(&tm);

  strftime(buf, BUFSIZ, "%FT%T%z", &tm);
  printf("after:  %s\n", buf);

  return 0;

【讨论】:

当我(在 MacOS 上)运行它时,我得到:before: 2011-02-25T14:13:57-0800 after: 2011-02-25T23:36:57+0000(注:0000)。我期待:before: 2011-02-25T14:13:57-0800 after: 2011-02-25T23:36:57+0123(注:0123)。听起来也许改变环境中的 TZ 是答案?如果是这样,我想我可以这样做......我只是不明白为什么 strftime 不尊重 tm_gmtoff。 ?【参考方案2】:

我认为最好的方法是

    将您的值读入time_t 变量;

    按照these 指南设置 TZ 环境变量。有关如何指定 TZ 的信息,另请参阅 this 页面;

    调用localtime()

这应该可以保证工作,虽然我无法在 MacOS 平台上测试它。

【讨论】:

【参考方案3】:

我个人喜欢在我的 bash 代码中嵌入小型 python 脚本。 Python 具有许多功能强大的开箱即用库,可满足您所需的功能。

您可以将python代码嵌入bash脚本中,如下所示(假设安装了python)

python << END
from pytz import timezone    

south_africa = timezone('Africa/Johannesburg')
sa_time = datetime.now(south_africa)
print sa_time.strftime('%Y-%m-%d_%H-%M-%S')
END

您可以更改您希望在不同时区显示的时区设置。请查看http://pytz.sourceforge.net/ 了解更多详情。

【讨论】:

不过,我在这里使用的是 C,而不是 bash。我想,嵌入 Python(或 Python 库,或其他类似语言的类似语言)可以完成……但实际上,这似乎是 C 库应该有办法处理的事情。不过,谢谢。

以上是关于C:如何在特定时区(按偏移量)打印特定的“时间”值?的主要内容,如果未能解决你的问题,请参考以下文章

pytz 时区的 STD 偏移量

如何在 .NET 中的 DST 时间获取 UTC 偏移量

BigQuery 在解析时无法识别时区

使用 Joda-Time 获取给定日期和时区的 UTC 偏移量

Luxon:获取偏移量和直到的数组(就像我们可以在时刻时区中一样)

在 MySQL 中计算时区的偏移量