为什么没有strptime()在这个例子中正确设置tm_wday?

时间:2016-11-23 12:17:44

标签: c gcc time clang strptime

我使用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

2 个答案:

答案 0 :(得分:1)

我认为原因很简单,就是设计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 *) &t, 0, sizeof(t));

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

  

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

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

答案 1 :(得分:0)

根据评论中的观察结果,这是一个源自您的代码的程序,该程序说明了即使它没有解释发生了什么:

#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日是1月1日第0天的第108天。