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

发布于 2024-10-18 17:27:30 字数 1905 浏览 5 评论 0原文

我正在用 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

我需要更改 < code>TZ 环境变量(也许调用 tzset)?似乎应该有一种方法来操作数据结构并获得正确的结果,但上述方法肯定行不通(无论如何,在 MacOS X 10.6 上 - 在 Linux 上效果很好)。

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

不过,理想情况下,我希望对我的 struct tm 进行修改,或者我可以使用其他一些函数调用(例如 strftime,但带有额外的参数或其他参数,或者可能是替代函数)相反,本地时间的形式),这将使事情做正确的事情。

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

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

I'm writing an application in C that parses data files recorded by an external program (over which I have no control). It's storing binary data, one field of which is a time in standard UNIX "epoch" format (seconds since January 1st, 1970, UTC).

Another field is the timezone, stored as an offset in seconds from UTC.

Cool, I've got everything I need to make a date/time string representing that information in the timezone it was recorded in, right? Well... it doesn't seem so, and/or I'm not sure how to do it.

I've boiled things down to a fairly simple test case:

#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;
}

When I run this on MacOS X 10.6, I get:

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

What I would expect (and in fact get, on a Linux box) would be:

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

Do I need to change the TZ environment variable (and maybe call tzset)? It seems like there ought to be a way to manipulate the data structures and get the right thing, but the above certainly isn't working (on MacOS X 10.6, anyway -- works great on Linux).

As a workaround, I suppose I can drop the %z from my format string and create that part myself.

Ideally, though, I'd like to have either a modification of my struct tm, or some other function call that I can use (like strftime, but with an extra parameter or something, or perhaps an alternate form of localtime, instead), that would make things do the right thing.

Since Linux seems to behave (though even there, the above solution isn't quite ideal, because I'm fudging my time_t value; I'd prefer to have a parameter that changes how the struct tm is calculated), is this something that I should report as a bug against MacOS?

Alternately, is there a different set of library routines I could call, even if that ends up requiring a third-party (something from the GNU folks, I'm imagining) library? I'd prefer to keep to C, though I'd consider ObjC or C++ options.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

月下客 2024-10-25 17:27:30

我更喜欢使用 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;
}

I prefer to use mktime() instead of modifying the time_t value. However, this applies the TZ offset (just like the localtime() solution), so a mkgmtime() implementation is required.

#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;
}
和影子一齐双人舞 2024-10-25 17:27:30

我认为最好的方法是

  1. 将您的值读取到time_t变量中;

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

  3. 调用localtime()

尽管我无法在 MacOS 平台上测试它,但应该保证它可以工作。

I think that the best approach would be

  1. read your value into a time_t variable;

  2. set the TZ environment variable following these guidelines. See also this page for info on how you should specify TZ;

  3. invoke localtime().

This should be guaranteed to work, althought I can't test it on a MacOS platform.

幼儿园老大 2024-10-25 17:27:30

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

I personally like to embed small python scripts in my bash code. Python has many powerful out-of-the-box libraries for functionalities like you require.

You can embed python code in a bash script as follows (Assuming python is installed)

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

You can change the timezone setting as you wish to display in different timezones. Have a look at http://pytz.sourceforge.net/ for more details.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文