[英]Epoch Seconds to Date Conversion on a Limited Embedded Device
I am trying to figure out the best way to convert from epoch seconds (since NTP epoch 1900-01-01 00:00) to a datetime string (MM/DD/YY,hh:mm:ss) without any libraries/modules/external functions, as they are not available on an embedded device. 我试图找出从纪元秒(自NTP纪元1900-01-01 00:00)转换为日期时间字符串(MM / DD / YY,hh:mm:ss)的最佳方法,没有任何库/模块/外部功能,因为它们在嵌入式设备上不可用。
My first thought was to look at the Python datetime module source code , however that was not very useful to me. 我的第一个想法是查看Python datetime模块源代码 ,但这对我来说并不是很有用。
My initial attempt in Python uses a conversion of days since 0001-01-01 to date using getDateFromJulianDay
adapted to Python from C++ source , combined with modulo operations to obtain time. 我在Python中的初步尝试使用从0001-01-01开始的日期转换,使用适用于C ++源代码的 Python的
getDateFromJulianDay
,结合模运算来获取时间。 It works, but is there a better way? 它有效,但有更好的方法吗?
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
Why I'm doing this: I am getting the current time from an NTP server, and taking this time at face value for updating a hardware real time clock (RTC) that only accepts the date, time and time zone: MM/DD/YY,hh:mm:ss,±zz. 为什么我这样做:我从NTP服务器获取当前时间,并将此时间用于更新仅接受日期,时间和时区的硬件实时时钟(RTC)的面值:MM / DD / YY,HH:MM:SS,±ZZ。 I plan to implement true NTP capabilities at a later date.
我计划在以后实现真正的NTP功能。 A discussion of time synchronization methods is best left elsewhere, such as this question .
关于时间同步方法的讨论最好留在其他地方,例如这个问题 。
Notes: 笔记:
The getDateFromJulianDay
function originally proposed is too computationally intensive for effective use on an embedded device, containing many multiplication and division operations on large long
variables or, as originally written in C++, longlong
variables . 最初提出的
getDateFromJulianDay
函数计算量太大,无法在嵌入式设备上有效使用,包含对大型long
变量的许多乘法和除法运算,或者最初用C ++编写的long
longlong
变量 。
I think I hunted down an efficient epoch to date algorithm for an embedded device. 我想我已经找到了嵌入式设备的有效时代算法。
After fruitless Googling, I found myself back on Stack Overflow, and found the question Converting epoch time to “real” date/time , asking about self-written epoch time to date implementation and provides a suitable algorithm. 在没有结果的谷歌搜索之后,我发现自己回到了Stack Overflow,发现了将纪元时间转换为“真实”日期/时间的问题 ,询问自编时间实现的时间,并提供了一个合适的算法。 This answer to the question references the gmtime.c source code , and provided the source in CI needed to write a Python conversion algorithm:
这个问题的答案引用了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;
}
Additionally, the analysis of the question Why is gmtime implemented this way? 另外,对问题的分析 为什么gmtime以这种方式实现? helped affirm that the
gmtime
function is fairly efficient. 帮助确认
gmtime
功能相当有效。
Using the raspberryginger.com minix Doxygen documentation site , I was able to find the C macros and constants that were included in gmtime.c from loc_time.h . 使用raspberryginger.com MINIX Doxygen文档的网站 ,我能找到被列入了C宏和常量gmtime.c从loc_time.h 。 The relevant code snippet:
相关代码段:
#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];
And the extern const int _ytab
was defined in misc.c : 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 }
};
Some other things I found: 我找到的其他一些东西:
gmtime
function starts indexing the Month, Day of Week, and Day of Year at the number zero, (maximal ranges of 0-11, 0-6, 0-365, respectively), whereas the Day of Month starts at the number 1, (1-31), see the IBM gmtime()
reference . gmtime
函数开始将月份,星期几和日期编号为零,(最大范围分别为0-11,0-6,0-365),而月份日期从数字1开始,(1-31),请参阅IBM gmtime()
参考 。 I re-wrote the gmtime
function for Python 1.5.2+: 我重写了Python 1.5.2+的
gmtime
函数:
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)
Modifications I made re-factoring the C++ gmtime
function to my Python function ntp_time_to_date(ntp_time)
: 修改我将C ++
gmtime
函数重新分解为我的Python函数ntp_time_to_date(ntp_time)
:
gmtime
to ntp_time_to_date
: gmtime
时间计算与ntp_time_to_date
进行ntp_time_to_date
:
(dayclock % 3600) / 60
and dayclock / 3600
occur behind the scenes in divmod(dayclock,3600)
and divmod(sec_past_hour,60)
. (dayclock % 3600) / 60
和dayclock / 3600
发生在divmod(dayclock,3600)
和divmod(sec_past_hour,60)
。 divmod(sec_past_hour,60)
avoids modulo of dayclock
(0-86399) by 60 via dayclock % 60
, and instead does modulo of sec_past_hour
(0-3599) by 60 within divmod(sec_past_hour,60)
. divmod(sec_past_hour,60)
避免了模dayclock
通过(0-86399)60 dayclock % 60
,而是确实的模sec_past_hour
60(0-3599)内divmod(sec_past_hour,60)
long
as soon as values were less than 65535 to greatly decrease code execution time. long
值小于65535,就将long
变量从long
键入,以大大减少代码执行时间。
ntp_time
, seconds since 1900 (0-4294967295) ntp_time
,自1900年以来的秒数(0-4294967295) dayclock
, seconds into day (0-86399) dayclock
,秒到天(0-86399) The Python ntp_time_to_date
function (with its dependencies) runs successfully on the Telit GC-864 on an embedded version of Python 1.5.2+, as well as on Python 2.7.3, but of course use the datetime library if you can. Python
ntp_time_to_date
函数(及其依赖项)在嵌入式版本的Python 1.5.2+以及Python 2.7.3上的Telit GC-864上成功运行,但如果可以,当然可以使用datetime库。
If you are using a Telit GC-864, the Python interpreter seemingly inserts some sort of delay in-between each line of code execution. 如果您使用的是Telit GC-864,Python解释器似乎会在每行代码执行之间插入某种延迟。
For a Telit GC-864, the function in my question getDateFromJulianDay(julianDay)
is faster than the function in my answer, ntp_time_to_date(ntp_time)
. 对于泰利特GC-864,在我的问题的功能
getDateFromJulianDay(julianDay)
比我的回答,函数更快ntp_time_to_date(ntp_time)
The number of code lines dominates execution time on a GC-864 more than the complexity of the code - weird, I know. 代码行的数量主导着GC-864上的执行时间,而不是代码的复杂性 - 我知道这很奇怪。 The function
getDateFromJulianDay(julianDay)
in my question has a handful of complex operations, maybe 15 lines of code. 我的问题中的函数
getDateFromJulianDay(julianDay)
有一些复杂的操作,可能是15行代码。 The function in my answer ntp_time_to_date(ntp_time)
has simpler computation complexity, but the while
loops cause 100+ lines of code execution: 我的答案
ntp_time_to_date(ntp_time)
的函数具有更简单的计算复杂性,但while
循环导致100多行代码执行:
Timing test results running on an actual GC-864 (note: not a GC-864-V2) using the same NTP time input for each trial (each function outputs "3/25/2013 18:40"). 在实际GC-864上运行的定时测试结果(注意: 不是 GC-864-V2),每次试验使用相同的NTP时间输入(每个功能输出“3/25/2013 18:40”)。 Timing was performed using printf statement debugging, and a serial terminal on a computer would time stamp each line sent by the GC-864.
使用printf语句调试执行时序,计算机上的串行终端将为GC-864发送的每一行添加时间戳。
getDateFromJulianDay(julianDay)
Trials: getDateFromJulianDay(julianDay)
试验:
ntp_time_to_date(ntp_time)
Trials: ntp_time_to_date(ntp_time)
试验:
The variability partially stems from the GC-864 cell modem servicing cellular network tasks periodically. 可变性部分源于定期为蜂窝网络任务提供服务的GC-864小区调制解调器。
For completeness, the optimization of typecasting the long
variables to int
as quickly as possible in ntp_time_to_date(ntp_time)
has a fairly significant effect. 为了完整性,在
ntp_time_to_date(ntp_time)
尽可能快地将long
变量类型转换为int
的优化具有相当显着的效果。 Without this optimization: 没有这个优化:
Doing anything computationally involved on a Telit GC-864 running .pyo files in Python 1.5.2+ is not a great idea. 在Python 1.5.2+中运行Telit GC-864运行.pyo文件的任何计算任务都不是一个好主意。 Using the GC-864-V2, which has a built-in NTP capability is a possible solution for someone who comes across this issue.
对于遇到此问题的人来说,使用具有内置NTP功能的GC-864-V2是一种可行的解决方案。 Moreover, newer machine-to-machine (M2M) aka internet of things (IoT) cell phone modems are much more capable.
此外,更新的机器对机器(M2M)又称物联网(IoT)手机调制解调器功能更强大。
If you have a similar issue with the GC-864, consider using a newer and more modern cell phone modem . 如果您遇到与GC-864类似的问题,请考虑使用更新,更现代的手机调制解调器 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.