【问题标题】:Why doesn't strptime() set tm_wday properly in this example?为什么 strptime() 在这个例子中没有正确设置 tm_wday?
【发布时间】:2017-04-07 10:57:03
【问题描述】:

我正在使用strptime(3) 来解析代表日期的字符串:

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

int main () {
  struct tm t;
  strptime("2015-04-19 12:00:00", "%F %T", &t); /* Sunday */
  printf("%d\n", t.tm_wday); /* Should print 0 */
  return 0;
}

根据cal -y 2015 的输出,该日期是星期日。但是当我在 OSX 上编译它时(大概是用 clang)它会打印出6:

$ gcc timetest.c ; ./a.out
6

而在 Debian 上它会打印(正确的)0:

$ gcc timetest.c ; ./a.out
0

有什么不同的解释吗?

更新

这是相同的程序,除了 t 使用有效时间初始化并且我报告了 strptime() 的返回值:

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

int main () {
  time_t epoch = 0;
  struct tm t;
  char *ret;
  t = *localtime(&epoch);
  ret = strptime("2015-04-19 12:00:00", "%F %T", &t); /* Sunday */
  printf("%d\n", t.tm_wday); /* Should print 0 */
  printf("strptime() returned %p (%d)\n", ret, *ret);
  return 0;
}

这是输出:

$ gcc timetest.c ; ./a.out
6
strptime() returned 0x10c72af83 (0)

这是我使用的clang 版本:

$ clang -v
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.1.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

【问题讨论】:

  • OSX 上 strptime() 的返回值是多少?在调用 strptime() 之前使用有效日期初始化 t 会发生什么?
  • 我已经根据@Jens 的建议更新了程序。
  • 它在 OS X 上给我 0。clang-700.1.81
  • 逐字记录代码并在 macOS Sierra 10.12.1 上运行(使用 GCC 6.2.0 或 XCode 8.0 中的 Clang),我得到了结果 6。我在后面添加了两行:time_t rt = mktime(&amp;t);打电话给strptime()printf("%lld\n", (long long)rt);,结果变成了0。现在,对此,我没有解释!
  • @JonathanLeffler 对,我可以确认,在拨打strptime() 之后再拨打mktime() 对我来说是一个可用的解决方法。但我无法解释观察到的行为。

标签: c gcc time clang strptime


【解决方案1】:

根据 cmets 中的观察结果,这是一个从您的代码派生的程序,即使它没有解释,也说明了正在发生的事情:

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

static void dump_struct_tm(const char *tag, const struct tm *t)
{
    printf("%s:\n", tag);
    printf("  Time: %.2d:%.2d:%.2d  ", t-> tm_hour, t->tm_min, t->tm_sec);
    printf("  Date: %.4d-%.2d-%.2d\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday);
    printf("  Wday: %d Yday: %.3d (DST %d Zone [%s] offset %ld)\n", 
           t->tm_wday, t->tm_yday, t->tm_isdst, t->tm_zone, t-> tm_gmtoff);
}

int main(void)
{
    time_t epoch = 0;
    struct tm t;
    char *ret;
    t = *localtime(&epoch);
    dump_struct_tm("Epoch", &t);
    putchar('\n');

    ret = strptime("2015-04-19 12:00:00", "%F %T", &t);
    dump_struct_tm("strptime()", &t);
    time_t rt = mktime(&t);
    dump_struct_tm("mktime()", &t);
    printf("Weekday: %d\n", t.tm_wday);
    printf("strptime() returned %p (%d)\n", ret, *ret);
    printf("Unix time: %lld\n\n", (long long)rt);

    t.tm_isdst = -1;
    ret = strptime("2015-04-19 12:00:00", "%F %T", &t);
    dump_struct_tm("strptime()", &t);
    rt = mktime(&t);
    dump_struct_tm("mktime()", &t);
    printf("Weekday: %d\n", t.tm_wday);
    printf("strptime() returned %p (%d)\n", ret, *ret);
    printf("Unix time: %lld\n\n", (long long)rt);

    return 0;
}

这会在不同点分析struct tm(由 macOS Sierra 上的手册页定义)。请注意tm_isdst 的设置如何改变行为。

Epoch:
  Time: 16:00:00    Date: 1969-12-31
  Wday: 3 Yday: 364 (DST 0 Zone [PST] offset -28800)

strptime():
  Time: 12:00:00    Date: 2015-04-19
  Wday: 6 Yday: 108 (DST 0 Zone [(null)] offset -28800)
mktime():
  Time: 13:00:00    Date: 2015-04-19
  Wday: 0 Yday: 108 (DST 1 Zone [PDT] offset -25200)
Weekday: 0
strptime() returned 0x100c82f0c (0)
Unix time: 1429473600

strptime():
  Time: 12:00:00    Date: 2015-04-19
  Wday: 6 Yday: 108 (DST -1 Zone [(null)] offset -25200)
mktime():
  Time: 12:00:00    Date: 2015-04-19
  Wday: 0 Yday: 108 (DST 1 Zone [PDT] offset -25200)
Weekday: 0
strptime() returned 0x100c82f0c (0)
Unix time: 1429470000

我仍然不清楚为什么strptime() 错误地填充了tm_wday 字段,特别是因为它似乎使tm_yday 字段正确。 4 月 19 日是一年中的第 108 天,而 1 月 1 日是第 0 天。

【讨论】:

    【解决方案2】:

    我认为原因很简单,按照设计,the strptime function only sets the fields that appear in the format。本质上,strptime(3) 只是使用提供的格式将给定字符串中的字段解析为引用结构,而不执行其他计算或逻辑。由于您的代码使用%F %T 格式,因此只修改了与%Y-%m-%d%H:%M:%S(即tm_{year,mon,mday,hour,min,sec})对应的字段。

    您可以通过将t.tm_wday 显式设置为strptime 不应设置的某个已知值(例如123)来进行实验,并验证该调用不会更改它。请注意,您可能应该在使用 struct tm 之前对其进行初始化,因为这些字段中的任何一个都可能包含随机值,例如struct tm t; memset((void *) &amp;t, 0, sizeof(t));.

    此外,这个Linux strptime(3) man page 包含以下注释,这使我相信它所描述的特殊行为是非标准的(尽管显然是可取的):

    glibc 实现不会触及那些未明确指定的字段,除非它会在年、月或日元素中的任何一个发生更改时重新计算 tm_wdaytm_yday 字段。

    This answer shows how 您可以使用strptime/mktime/localtime(或gmtime)的三重奏来填充tm.tm_wday 字段以获取UNIX 纪元之后的日期。

    【讨论】:

      猜你喜欢
      • 2011-05-14
      • 1970-01-01
      • 2020-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-05
      • 1970-01-01
      相关资源
      最近更新 更多