2014-02-06 70 views
6

我正在构建一个嵌入式项目,该项目显示从显示器上的GPS模块中检索的时间,但我也想显示当前日期。我目前有时间作为unix时间戳,并且该项目用C语言编写。将unix时间戳转换为不带系统库的日期

我正在寻找一种方法来计算从时间戳中计算当前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; 
} 
+0

闰秒呢? – wonce

+0

只有当他们的计算不存在会导致大的误差(我怀疑)。 – Geoffrey

+0

由于闰秒,UTC和GPS时间现在有16秒不同。这不会很快变化。换句话说,如果你只是显示当前日期,你可以忽略闰秒。 – kmort

回答

10

先除以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

+1,用于将年初移至3月1日。这使得** Oct **在** 8th **月份和** Dec **支持** 10th **月份 - 因为很久以前。 – chux

+0

我从来没有想到过那个部分。我只是认为这使得以单位为单位处理闰年变得更容易,因为额外的一天恰好是在年底,而不是在中间。 –

+0

罗马人在上个月(2月)把传统的闰日放在了传统中。他们还将今年的第一天从三月份的某个时间转移到一月一日。 – chux

0

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

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

例,2011年11月25日:

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

+0

因为我的GPS模块没有返回NMEA数据,而是返回SiRF二进制协议,该协议返回周数,以及自本周开始以来的秒数。 此外,我不能使用标准库,只是因为它没有在我需要使用的环境中实现。 – Geoffrey

+0

够公平的,但也许你需要解决的问题是缺乏标准库? – Clifford

+0

我不了解SiRF,但uBlox可以输出混合的UBX和NMEA。虽然SiRF是专有的,但NMEA无处不在,所以使用NMEA也会给您更大的便携性。 – Clifford

0

这里有一个便携执行mktime()。它包括对DST的支持,您可能会删除这些DST,以便仅为UTC缩小尺寸。它也对数据进行了规范化处理(例如,如果你有65秒的时间,它会增加分钟数并将秒数设置为5,所以也许会有一些你不需要的开销)

看起来有点复杂你已经到达的解决方案;你可能想考虑是否有这样的原因?我可能会实现这两个测试(在PC上而不是嵌入式),并遍历大范围的历元时间值并比较结果与PC编译器自己的std::mktime(使用C++将避免名称冲突,而不必重命名)。如果它们都产生相同的结果,则根据需要使用最快/最小的实现,否则使用正确的一个!

I认为典型的图书馆mktime执行二元收敛比较localtime()与目标的回报。这比直接的日历计算效率要低,但我认为这样做是为了确保从struct tmtime_t(反之亦然)和返回的往返转换产生相同的结果。我上面提到的便携式实现使用了相同的收敛技术,但是替换了localtime()以删除库依赖关系。因此,经过反思,我怀疑直接计算方法在你的情况下更可取,因为你不需要可逆性 - 只要它当然是正确的。

相关问题