4

我正在用 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环境变量(可能还有 call tzset)?似乎应该有一种方法来操纵数据结构并获得正确的东西,但上面肯定行不通(无论如何,在 MacOS X 10.6 上——在 Linux 上工作得很好)。

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

不过,理想情况下,我希望修改 mystruct tm或其他一些我可以使用的函数调用(如 strftime,但带有额外的参数或其他东西,或者可能是 localtime 的替代形式),这将让事情做正确的事。

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

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

4

3 回答 3

3

我更喜欢使用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;
}
于 2011-02-25T14:03:19.407 回答
0

我认为最好的方法是

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

  2. 按照这些指南设置 TZ 环境变量。另请参阅页面以获取有关如何指定 TZ 的信息;

  3. 调用localtime().

尽管我无法在 MacOS 平台上对其进行测试,但这应该可以保证工作。

于 2011-02-23T09:57:58.513 回答
-1

我个人喜欢在我的 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/了解更多详情。

于 2012-10-02T16:19:49.397 回答