将unix时间戳转换为没有系统库的日期

时间:2014-02-06 03:56:29

标签: c date timestamp embedded

我正在构建一个嵌入式项目,它显示从显示器上的GPS模块检索的时间,但我还想显示当前日期。我目前有时间作为unix时间戳,而progject是用C语言编写的。

我正在寻找一种从时间戳计算当前UTC日期的方法,将闰年考虑在内?请记住,这是针对没有FPU的嵌入式项目,因此模拟浮点数学,尽可能地避免它的性能。

修改

在查看@R ...的代码后,我决定自己写一篇文章并提出以下内容。

void calcDate(struct tm *tm)
{
  uint32_t seconds, minutes, hours, days, year, month;
  uint32_t dayOfWeek;
  seconds = gpsGetEpoch();

  /* calculate minutes */
  minutes  = seconds / 60;
  seconds -= minutes * 60;
  /* calculate hours */
  hours    = minutes / 60;
  minutes -= hours   * 60;
  /* calculate days */
  days     = hours   / 24;
  hours   -= days    * 24;

  /* Unix time starts in 1970 on a Thursday */
  year      = 1970;
  dayOfWeek = 4;

  while(1)
  {
    bool     leapYear   = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
    uint16_t daysInYear = leapYear ? 366 : 365;
    if (days >= daysInYear)
    {
      dayOfWeek += leapYear ? 2 : 1;
      days      -= daysInYear;
      if (dayOfWeek >= 7)
        dayOfWeek -= 7;
      ++year;
    }
    else
    {
      tm->tm_yday = days;
      dayOfWeek  += days;
      dayOfWeek  %= 7;

      /* calculate the month and day */
      static const uint8_t daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
      for(month = 0; month < 12; ++month)
      {
        uint8_t dim = daysInMonth[month];

        /* add a day to feburary if this is a leap year */
        if (month == 1 && leapYear)
          ++dim;

        if (days >= dim)
          days -= dim;
        else
          break;
      }
      break;
    }
  }

  tm->tm_sec  = seconds;
  tm->tm_min  = minutes;
  tm->tm_hour = hours;
  tm->tm_mday = days + 1;
  tm->tm_mon  = month;
  tm->tm_year = year;
  tm->tm_wday = dayOfWeek;
}

3 个答案:

答案 0 :(得分:12)

首先除以86400;剩下的部分可以用来获得结果的HH:MM:SS部分。现在,你自1970年1月1日起离开了很多天。然后,我将把它作为一个常数调整为自2000年3月1日以来的天数(可能是负数);这是因为2000年是闰年周期的400的倍数,因此可以轻松(或至少更容易)计算使用分部已经过了多少闰年。

我不会试图更详细地解释这一点,而是会引用您的实现:

http://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c?h=v0.9.15

答案 1 :(得分:0)

目前尚不清楚为什么不能使用标准库;如果尺寸存在问题,则只会链接代码使用的库组件,并且time_tstruct tm转换将非常小,但更重要的是正确和有效。

然而,更好的问题是为什么不直接使用GPS模块中的数据?它位于标准RMC sentence的第八个字段中,几乎可以肯定由设备输出。

2011年11月25日的例子:

$ GPRMC,001225,A,2832.1834,N,08101.0536,W,12,25,的 251211 下,1.2,E,A * 03

答案 2 :(得分:0)

这是mktime()的便携式实现。它包括对您可能删除的DST的支持,以便仅为UTC减小一些大小。它还可以对数据进行标准化(例如,如果您有65秒,它会增加分钟并将秒数设置为5,因此可能会有一些您不需要的开销。

它似乎比你已经达到的解决方案更复杂;你可能想考虑是否有原因?我或许可以将它们作为测试(在PC而不是嵌入式)上实现,并迭代大量的纪元时间值,并将结果与​​PC编译器自己的std::mktime进行比较(使用C ++将避免名称冲突,而不会重命名)。如果它们都产生相同的结果,则根据需要使用最快/最小的实现,否则使用正确的实现!

我认为典型的库mktime执行二进制收敛,比较localtime()与目标的回报。这比直接的日历计算效率低,但我认为这样做是为了确保从struct tmtime_t(反之亦然)的往返转换产生相同的结果。我上面建议的可移植实现使用相同的收敛技术,但替换localtime()以删除库依赖项。因此,在反思时,我怀疑直接计算方法在您的情况下是可取的,因为您不需要可逆性 - 只要它当然是正确的。