最初提出的getDateFromJulianDay
函数计算量太大,无法在嵌入式设备上有效使用,其中包含对大型long
变量或最初用 C++ 编写的变量longlong
的许多乘法和除法运算。
我想我为嵌入式设备找到了一个有效的时代算法。
谷歌搜索无果后,我发现自己又回到了 Stack Overflow,找到了Converting epoch time to “real” date/time这个问题,询问自写的 epoch time to date implementation 并提供了一个合适的算法。该问题的答案引用了gmtime.c 源代码,并提供了编写 Python 转换算法所需的 CI 源代码:
/*
* gmtime - convert the calendar time into broken down time
*/
/* $Header: /opt/proj/minix/cvsroot/src/lib/ansi/gmtime.c,v 1.1.1.1 2005/04/21 14:56:05 beng Exp $ */
#include <time.h>
#include <limits.h>
#include "loc_time.h"
struct tm *
gmtime(register const time_t *timer)
{
static struct tm br_time;
register struct tm *timep = &br_time;
time_t time = *timer;
register unsigned long dayclock, dayno;
int year = EPOCH_YR;
dayclock = (unsigned long)time % SECS_DAY;
dayno = (unsigned long)time / SECS_DAY;
timep->tm_sec = dayclock % 60;
timep->tm_min = (dayclock % 3600) / 60;
timep->tm_hour = dayclock / 3600;
timep->tm_wday = (dayno + 4) % 7; /* day 0 was a thursday */
while (dayno >= YEARSIZE(year)) {
dayno -= YEARSIZE(year);
year++;
}
timep->tm_year = year - YEAR0;
timep->tm_yday = dayno;
timep->tm_mon = 0;
while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) {
dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon];
timep->tm_mon++;
}
timep->tm_mday = dayno + 1;
timep->tm_isdst = 0;
return timep;
}
另外,分析一下为什么gmtime是这样实现的?帮助确认该gmtime
功能相当有效。
使用raspberryginger.com minix Doxygen 文档站点,我能够从loc_time.h中找到 gmtime.c中包含的 C 宏和常量。相关代码片段:
#define YEAR0 1900 /* the first year */
#define EPOCH_YR 1970 /* EPOCH = Jan 1 1970 00:00:00 */
#define SECS_DAY (24L * 60L * 60L)
#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365)
#define FIRSTSUNDAY(timp) (((timp)->tm_yday - (timp)->tm_wday + 420) % 7)
#define FIRSTDAYOF(timp) (((timp)->tm_wday - (timp)->tm_yday + 420) % 7)
#define TIME_MAX ULONG_MAX
#define ABB_LEN 3
extern const int _ytab[2][10];
并且在misc.cextern const int _ytab
中定义:
const int _ytab[2][12] = {
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
我发现的其他一些东西:
- gmtime.c文件参考对于查找依赖项非常有帮助。
- 该
gmtime
函数从数字 0 开始索引月份、星期几和一年中的某一天(最大范围分别为 0-11、0-6、0-365),而月份日期从数字 1 开始, (1-31),请参阅IBMgmtime()
参考资料。
gmtime
我为 Python 1.5.2+重新编写了函数:
def is_leap_year(year):
return ( not ((year) % 4) and ( ((year) % 100) or (not((year) % 400)) ) )
def year_size(year):
if is_leap_year(year):
return 366
else:
return 365
def ntp_time_to_date(ntp_time):
year = 1900 # EPOCH_YR for NTP
ytab = [ [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
[ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ]
(dayno,dayclock) = divmod(ntp_time, 86400L)
dayno = int(dayno)
# Calculate time of day from seconds on the day's clock.
(hour, sec_past_hour) = divmod(dayclock,3600)
hour = int(hour)
(min, sec) = divmod(int(sec_past_hour),60)
while (dayno >= year_size(year)):
dayno = dayno - year_size(year)
year = year + 1
month = 1 # NOTE: month range is (1-12)
while (dayno >= ytab[is_leap_year(year)][month]):
dayno = dayno - ytab[is_leap_year(year)][month]
month = month + 1
day = dayno + 1
return (year, month, day, hour, min, sec)
我将 C++gmtime
函数重构为 Python 函数的修改ntp_time_to_date(ntp_time)
:
- 将纪元从 1970 年的 UNIX 纪元更改为 1900 年的 NTP 纪元(NTP的主要纪元)。
- 稍微简化了一天中的时间计算。
gmtime
比较一天中的时间计算ntp_time_to_date
:
(dayclock % 3600) / 60
和都发生在和dayclock / 3600
的幕后。divmod(dayclock,3600)
divmod(sec_past_hour,60)
- 唯一真正的区别是
divmod(sec_past_hour,60)
避免dayclock
通过 60对 (0-86399) 取dayclock % 60
模,而是在sec_past_hour
内对 (0-3599) 取 60 取模divmod(sec_past_hour,60)
。
- 删除了我不需要的变量和代码,例如星期几。
- 将月份的索引更改为从 1 开始,因此月份范围是 (1-12) 而不是 (0-11)
long
值小于 65535 时立即
键入强制类型转换变量,以大大减少代码执行时间。
- 需要长变量是:
ntp_time
, 自 1900 年以来的秒数 (0-4294967295)
dayclock
, 秒到一天 (0-86399)
- 其余变量中最大的是该日期内的计算年份。
Pythonntp_time_to_date
函数(及其依赖项)在 Python 1.5.2+ 的嵌入式版本以及 Python 2.7.3 上的 Telit GC-864 上成功运行,但当然可以使用 datetime 库。