【问题标题】:tm struct converts to wrong time_ttm 结构转换为错误的 time_t
【发布时间】:2022-11-12 22:10:57
【问题描述】:

我有以下代码将从GPS卫星接收到的datetime string解析为struct tm,然后使用mktime()从中获取epoch,结果在我的Debian机器上是正确的,但在我的ESP32上是错误的与ESP-IDF,您对为什么会发生这种情况有什么建议吗?夏令时或者时区东西?

#include "rs_time.h"

time_t time_from_gnss_info_time(const char * datetime_str){
    time_t epoch;
    struct tm tm;
    sscanf(
        datetime_str,
        "%4d%2d%2d%2d%2d%2d",
        &tm.tm_year,
        &tm.tm_mon,
        &tm.tm_mday,
        &tm.tm_hour,
        &tm.tm_min,
        &tm.tm_sec
    );
    epoch = mktime(&tm); // result is '1462765068' or Mon May  9 03:37:48 2016
    printf("the date and time is: %s %ld ",ctime(&epoch), time(NULL));
    return epoch;

}

当datetime_str 为'20210913221332' 时,使用mktime() 后epoch 的值是:1462765068,ctime() 表示也是:Mon May 9 03:37:48 2016

【问题讨论】:

  • 您的tm_year 用法错误:来自人ctime:int tm_year; /* Year - 1900 */ 。应该是2021 - 1900 = 31
  • 你没有初始化tm
  • 在将tm 传递给mktime 之前,您必须设置tm.tm_isdst。如果不确定,您可以简单地将其设置为-1 或其他负值,以表明它是未知的。您不应该让该对象未初始化,因为这可能会向mktime 提供有关夏令时是否有效的错误信息。
  • @Mathieu 是的,但是 2021 - 1900 = 121 :-) 像我这样的老人还记得 Y2K,我们 year % 100 错了

标签: c timezone esp32 esp-idf


【解决方案1】:
  • .tm_year 是自 1900 年以来的年数。.tm_mon 是自一月以来的月数。

  • mktime() 使用struct tm 的所有成员(.tm_wday, .tm_yday 除外)来形成返回值。 OP 的代码没有初始化/设置 .tm_isdst 或其他 - struct tm 可能有比平常更多的 9 个成员。将它们全部初始化。

  • mktime() 形成 time_t 假设 struct tm 是本地时间,而不是 GPS 通常提供的通用时间。可能需要 TZ 调整 - 尚未显示。

  • time_t 肯定与 long 不匹配。投射到广泛的类型。 (time_t 甚至可能是浮点数。)并匹配说明符。

  • 添加错误检查。

time_t time_from_gnss_info_time(const char *datetime_str) {
  // Code is likely scanning per the current daylight time setting.
  // Use .tm_isdst = -1 to let mktime to determine it.
  // Or use 0 for standard time or any + value for daylight time.
  // All other members are zeroed
  // struct tm tm; // Not this
  struct tm tm = {.tm_isdst = -1};
  if (sscanf(datetime_str, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon,
      &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
    return -1;
  }
  // Adjust some members.
  tm.tm_year -= 1900;
  tm.tm_mon--;
  time_t epoch = mktime(&tm);
  printf("The date and time is: %s%lld
", ctime(&epoch),
      (long long) time(NULL));
  return epoch;
}

int main(void) {
  time_t t = time_from_gnss_info_time("20210913221332");
  printf("%lld
", (long long) t);
}

输出

The date and time is: Mon Sep 13 22:13:32 2021
1664290016
1631589212

可以进行额外的错误检查:额外的 txt、空格、符号、极值、...


改进的字符串解析

// if (sscanf(datetime_str, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon,
//    &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {

// Look for trailing junk.
char ch;
if (sscanf(datetime_str, "%4d%2d%2d%2d%2d%2d%c", &tm.tm_year, &tm.tm_mon,
  &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &ch) != 6) {

【讨论】:

  • 谢谢你为了分享您的知识,特别是对于 sscanf 错误检查和有关转换值的提示,我还认为 struct tm 应该使用 tm_isdst 而不是 isdst 初始化
  • @AhmadMansoori 代码已更正。
  • @AhmadMansoori 原始代码打印了当前的time_t。我希望epoch 是需要的。
  • 是的,你是对的,这也是我的愿望,这部分是错误的。
猜你喜欢
  • 1970-01-01
  • 2017-04-11
  • 2015-05-06
  • 1970-01-01
  • 2014-02-02
  • 2019-04-12
  • 2016-05-23
  • 2018-06-04
  • 2021-04-18
相关资源
最近更新 更多