【问题标题】:C: How to print a particular `time` value in a particular timezone (by offset)?C:如何在特定时区(按偏移量)打印特定的“时间”值?
【发布时间】: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。

【问题讨论】:

    标签: c unix strftime localtime ctime


    【解决方案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】:

    我认为最好的方法是

    1. 将您的值读入time_t 变量;

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

    3. 调用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 库应该有办法处理的事情。不过,谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-20
      • 2020-08-05
      • 1970-01-01
      • 2018-02-06
      • 2015-01-03
      • 1970-01-01
      • 2011-03-12
      相关资源
      最近更新 更多