【问题标题】:How to neatly initialize struct tm from ctime如何从 ctime 巧妙地初始化 struct tm
【发布时间】:2014-08-02 19:58:45
【问题描述】:

考虑以下两种方法从格式化为字符串的日期中获取纪元时间:

#include <iostream>

int main() {
    struct tm tm_init = {0};
    strptime("2012-10-26 16:00", "%Y-%m-%dT %H:%M", &tm_init);
    long epoch = mktime(&tm_init);

    struct tm tm_rand;
    strptime("2012-10-26 16:00", "%Y-%m-%dT %H:%M", &tm_rand);
    epoch = mktime(&tm_rand);

    return 0;
}

来源:http://ideone.com/3xMUm8。本质上的区别是 tm_init 用 0 初始化而 tm_rand 不是。第一个触发 g++(假设 -Wall 和 -W)说:

test.cpp:4:24: warning: missing initializer for member ‘tm::tm_hour’ [-Wmissing-field-initializers]

以及 tm 结构中其他字段的类似消息。但是,如果我省略初始化,就像在第二种情况下,valgrind 告诉我:

==9892== Conditional jump or move depends on uninitialised value(s)
==9892==    at 0x51E957C: __offtime (offtime.c:40)
==9892==    by 0x51EBBE7: __tz_convert (tzset.c:653)
==9892==    by 0x51E9EDB: __mktime_internal (mktime.c:310)
==9892==    by 0x400786: main (test.cpp:10)

当然,后者比前者差得多,但我也不喜欢警告。当然,我可以通过编写某种 tm 工厂手动初始化所​​有字段;但这需要我根据mt的逻辑编写代码,我需要两个写一个工厂(blegh)。

这是 ctime 的错误吗?不幸的是,我在 strptime 上找不到(半)官方文档,也就是说,上面没有 cplusplus.com 页面。我错过了什么吗?将字符串解析为 epoch long 的好方法是什么?

编辑:

根据一些答案的建议,我应该手动将 tm_isdst 设置为 -1、0 或 1。但是:

#include <iostream>

int main() {
    struct tm tm;
    strptime("2012-10-26 16:00", "%Y-%m-%dT %H:%M", &tm);
    tm.tm_isdst = 0;
    long epoch = mktime(&tm);
    return 0;
}

导致 valgrind 这么说:

==11329== Conditional jump or move depends on uninitialised value(s)
==11329==    at 0x51E9917: __offtime (offtime.c:83)
==11329==    by 0x51EBBE7: __tz_convert (tzset.c:653)
==11329==    by 0x51EA513: __mktime_internal (mktime.c:310)
==11329==    by 0x40078D: main (test.cpp:7)

那么,我是否也应该设置_offtime?

【问题讨论】:

标签: c++ c++11 ctime


【解决方案1】:

来自strptime的官方文档:

struct tm tm;
time_t t;
strptime("6 Dec 2001 12:33:45", "%d %b %Y %H:%M:%S", &tm);
tm.tm_isdst = -1;      /* Not set by strptime(); tells mktime()
                          to determine whether daylight saving time
                          is in effect */
t = mktime(&tm);

所以你应该设置tm_rand.tm_isdst = -1 告诉mktime 从区域设置检查夏令时。或者,您可以将其设置为 01

很遗憾,官方文档没有明确说明tm_isdst 未设置,但至少在示例中提到了。

【讨论】:

  • 正如您所建议的,我已将 tm_isdst 设置为 0,这也会导致未设置 tm 的成员:特别是 _offtime。此外,在我的实际程序中,我看到 2038 年的纪元,所以可能 _offtime 是时区的时间偏移量,它没有被初始化,然后作为无时区纪元的随机加法或减法转换为纪元。
  • @Herbert 两个问题:你不是在阅读%s,所以你需要设置tm_sec;在%d 之后还有一个虚假的T,它可能会干扰解析。
  • 刚刚发现如果只提供日期(即格式为"%Y%m%d"),那么代码在mktime 上运行在valgrind 吐错误。 memset(&amp;tm, 0, sizeof(tm)) 帮助修复了它 - 看起来 tm 的某些部分没有被 strptime 初始化
【解决方案2】:

我不认为strptimetm_isdst 有任何作用,并且不处理时区(好或完全取决于变化)。另一方面,mktime 需要为其计算设置tm_isdst,因此可能未初始化的依赖项会传播到其他代码中。简而言之,您需要决定如何处理时区...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    • 2010-12-15
    • 2015-11-08
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多