C strptime 和 mktime 错误的时间戳

tin*_*415 1 c datetime

我使用以下方法从文本文件解析日期时间:

#define DATETIME_FORMAT "%Y-%m-%d %H:%M:%S"
Run Code Online (Sandbox Code Playgroud)
  if (!strptime(datetimestr, DATETIME_FORMAT, &record->datetime)) {
    return 0;
  } else {
    record->message_size = strlen(messagestr) - 1;
    messagestr[record->message_size] = '\0';
    strcpy(record->message, messagestr);
    printf("parsed '%s' '%s' %ld\n", messagestr, datetimestr, mktime(&record->datetime));
    return 1;
  }
Run Code Online (Sandbox Code Playgroud)

我得到了这些印刷品

parsed 'ETC' '2023-04-03 09:00:19' 1680508819
parsed 'StandUp' '2023-04-03 09:00:47' 1680508847
parsed 'Stop' '2023-04-03 09:11:55' 1680505915
Run Code Online (Sandbox Code Playgroud)

但是当我将它们粘贴到在线纪元转换器时,我得到:

  • 1680508819 => GMT:2023 年 4 月 3 日星期一 8:00:19
  • 1680508847 => GMT:2023 年 4 月 3 日星期一 8:00:47
  • 1680505915 => GMT:2023 年 4 月 3 日星期一 7:11:55

那么为什么第三次约会strptimemktime改造第三次约会完全不同呢?

chu*_*ica 5

为什么 strptime 和 mktime 对第三个日期的转换完全不同?

mktime()struct tm未完全分配时存在不一致行为的风险。


strptime(datetimestr, DATETIME_FORMAT, &record->datetime)填充了一些成员。 tm 应在调用之前初始化。struct tmrecord->datetime

mktime()使用由 分配的成员strptime(..., %Y-%m-%d %H:%M:%S", ...加上其他成员(包括 ).tm_isdst,这会减少一小时。

之前,将整个strptime()零填充*1struct tm并设置.tm_isdst-1 (让我们mktime()确定 DST 设置)。@dalfaB

// Add
memset(&record->datetime, 0, sizeof record->datetime);
record->datetime.tm_isdst = -1;

if (!strptime(datetimestr, DATETIME_FORMAT, &record->datetime)) {
  ...
  printf("parsed '%s' '%s' %lld\n", 
      messagestr, datetimestr, (long long) mktime(&record->datetime));
Run Code Online (Sandbox Code Playgroud)

旁白:使用匹配的打印说明符。 "%ld"可能不匹配time_t。建议转换为宽类型。


请注意,mktime()将 读取struct tm当地时间,而不一定是 GMT 或UTC


*1详细信息:至少struct tm有9 名成员。2 ( , )在确定返回时被忽略。由于各种实现可能具有超过 9 个标准成员,因此我们希望初始化/分配所有成员以获得一致的结果。.tm_yday.tm_wdaygmtime()time_t

struct tm tm = { 0 };或者memset(&tm, 0, sizeof tm);有两种方法可以做到这一点。

strptime(datetimestr, %Y-%m-%d %H:%M:%S", &record->datetime)设置 6 个预期成员(和.tm_yday.tm_wday),但不设置.tm_isdst,也不可能设置其他tm成员。

代码可以进行零初始化和设置.tm_isdst

struct tm tm = { .tm_isdst = -1 };
Run Code Online (Sandbox Code Playgroud)

代码可以使用复合文字.tm_isdst对其他成员进行赋值和清零。

record->datetime = (struct tm){ .tm_isdst = -1 };
Run Code Online (Sandbox Code Playgroud)