swo*_*ert 5 python embedded datetime epoch ntp
我试图找出从纪元秒(自NTP纪元1900-01-01 00:00)转换为日期时间字符串(MM/DD/YY,hh:mm:ss)的最佳方法,没有任何库/模块/外部功能,因为它们在嵌入式设备上不可用.
我的第一个想法是查看Python datetime模块源代码,但这对我来说并不是很有用.
我在Python中的初始尝试使用了自0001-01-01以来的日期转换,使用了getDateFromJulianDay从C++源代码改编为Python ,并结合模运算来获取时间.它有效,但有更好的方法吗?
def getDateFromJulianDay(julianDay):
# Gregorian calendar starting from October 15, 1582
# This algorithm is from:
# Henry F. Fliegel and Thomas C. van Flandern. 1968.
# Letters to the editor:
# a machine algorithm for processing calendar dates.
# Commun. ACM 11, 10 (October 1968), 657-. DOI=10.1145/364096.364097
# http://doi.acm.org/10.1145/364096.364097
ell = julianDay + 68569;
n = (4 * ell) / 146097;
ell = ell - (146097 * n + 3) / 4;
i = (4000 * (ell + 1)) / 1461001;
ell = ell - (1461 * i) / 4 + 31;
j = (80 * ell) / 2447;
d = ell - (2447 * j) / 80;
ell = j / 11;
m = j + 2 - (12 * ell);
y = 100 * (n - 49) + i + ell;
return y,m,d
# NTP response (integer portion) for Monday, March 25, 2013 at 6:40:43 PM
sec_since_1900 = 3573225643
# 2415021 is the number of days between 0001-01-01 and 1900-01-01,
# the start of the NTP epoch
(year,month,day) = getDateFromJulianDay(2415021 + sec_since_1900/60/60/24)
seconds_into_day = sec_since_1900 % 86400
(hour, sec_past_hour) = divmod(seconds_into_day,3600)
(min, sec) = divmod(sec_past_hour,60)
print 'year:',year,'month:',month,'day:',day
print 'hour:',hour,'min:',min,'sec:',sec
Run Code Online (Sandbox Code Playgroud)
为什么我这样做:我从NTP服务器获取当前时间,并将此时间用于更新仅接受日期,时间和时区的硬件实时时钟(RTC)的面值:MM/DD/YY,HH:MM:SS,±ZZ.我计划在以后实现真正的NTP功能.关于时间同步方法的讨论最好留在其他地方,例如这个问题.
笔记:
getDateFromJulianDay最初提出的函数计算量太大,无法在嵌入式设备上有效使用,包含对大long变量的许多乘法和除法运算,或者最初用C++编写的longlong变量.
我想我已经找到了嵌入式设备的有效时代算法.
在没有结果的谷歌搜索之后,我发现自己回到了Stack Overflow,发现了将纪元时间转换为"真实"日期/时间的问题,询问了自写时间到目前为止的实现,并提供了一个合适的算法.这个问题的答案引用了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;
}
Run Code Online (Sandbox Code Playgroud)
另外,对问题的分析为什么gmtime以这种方式实现?帮助确认该gmtime功能相当有效.
使用raspberryginger.com MINIX Doxygen文档的网站,我能找到被列入了C宏和常量gmtime.c从loc_time.h.相关代码段:
#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];
Run Code Online (Sandbox Code Playgroud)
并且extern const int _ytab在misc.c中定义:
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 }
};
Run Code Online (Sandbox Code Playgroud)
我找到的其他一些东西:
gmtime函数开始将月份,星期几和日期编号为零,(最大范围分别为0-11,0-6,0-365),而月份日期从数字1开始, (1-31),请参阅IBM gmtime()参考资料.我重写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)
Run Code Online (Sandbox Code Playgroud)
修改我将C++ gmtime函数重新分解为我的Python函数ntp_time_to_date(ntp_time):
gmtime到ntp_time_to_date:
(dayclock % 3600) / 60并dayclock / 3600发生在幕后的divmod(dayclock,3600)和divmod(sec_past_hour,60).divmod(sec_past_hour,60)避免了模数dayclock(0-86399)到60通过dayclock % 60,而是模数为sec_past_hour(0-3599)到60之内divmod(sec_past_hour,60).long一旦值小于65535,就立即键入转换变量,以大大减少代码执行时间.
ntp_time,自1900年以来的秒数(0-4294967295)dayclock,秒到一天(0-86399)Python ntp_time_to_date函数(及其依赖项)在嵌入式版本的Python 1.5.2+以及Python 2.7.3上的Telit GC-864上成功运行,但如果可以的话,当然可以使用日期时间库.
如果您使用的是 Telit GC-864,Python 解释器似乎会在每行代码执行之间插入某种延迟。
对于 Telit GC-864,我的问题中的函数getDateFromJulianDay(julianDay)比我的答案中的函数更快ntp_time_to_date(ntp_time)。
代码行数在 GC-864 上的执行时间比代码的复杂性更重要——我知道这很奇怪。我的问题中的函数getDateFromJulianDay(julianDay)有一些复杂的操作,可能有 15 行代码。我的答案中的函数ntp_time_to_date(ntp_time)具有更简单的计算复杂性,但while循环会导致 100 多行代码执行:
在实际 GC-864(注意:不是GC-864-V2)上运行的计时测试结果,每次试验使用相同的 NTP 时间输入(每个函数输出“3/25/2013 18:40”)。使用 printf 语句调试进行计时,计算机上的串行终端将为 GC-864 发送的每一行添加时间戳。
getDateFromJulianDay(julianDay)试验:
ntp_time_to_date(ntp_time)试验:
这种变化部分源于 GC-864 蜂窝调制解调器定期为蜂窝网络任务提供服务。
为了完整性,尽可能快地long对变量进行类型转换的优化具有相当显着的效果。如果没有这种优化:intntp_time_to_date(ntp_time)
在 Python 1.5.2+ 中运行 .pyo 文件的 Telit GC-864 上执行任何涉及计算的操作并不是一个好主意。对于遇到此问题的人来说,使用具有内置 NTP 功能的 GC-864-V2 是一个可能的解决方案。此外,新型机器对机器 (M2M) 又名物联网 (IoT) 手机调制解调器的功能更加强大。
如果您的 GC-864 遇到类似问题,请考虑使用更新、更现代的手机调制解调器。