如何在无OS的32位嵌入式系统用ANSI C解决Y2036 NTP问题
随着2036年日益临近(且无法避免),我们对当前这套简易的SNTP时间同步算法产生了担忧:
// NTP structure received in udp_data_buffer uint32_t ntpStamp = ntohl(&((uint8_t *)udp_data_buffer)[0x28]); time_t unixTime = ntpStamp - 2208988800U; struct tm * buf; buf = localtime(&unixTime); uint16_t year = (uint16_t)(buf->tm_year + 1900); uint8_t month = (uint8_t)(buf->tm_mon + 1); uint8_t day = (uint8_t)buf->tm_mday; uint8_t hour = (uint8_t)buf->tm_hour; uint8_t min = (uint8_t)buf->tm_min; uint8_t sec = (uint8_t)buf->tm_sec; // now sync the RTC chip
硬件平台为32位Cortex-M3(使用NXP redlib),无操作系统。
libconfig-arm.h中time_t的定义如下:
typedef unsigned int time_t; /* date/time in unix secs past 1-Jan-70 */
我们的需求是:如何让该算法可靠应对Y2036(甚至Y2038)问题?
我们研究NTP规范后发现,它并未提供直接的解决方案:
Eras cannot be produced by NTP directly, nor is there need to do so. When necessary, they can be derived from external means, such as the filesystem or dedicated hardware.
我们不认同这一表述:为什么NTP不需要直接生成纪元信息?为什么不在协议中传输纪元信息?如果能传输的话,就不用采用这类“权宜之计”了:
As the NTP timestamp format has been in use for the last 17 years, it remains a possibility that it will be in use 40 years from now when the seconds field overflows. As it is probably inappropriate to archive NTP timestamps before bit 0 was set in 1968, a convenient way to extend the useful life of NTP timestamps is the following convention: If bit 0 is set, the UTC time is in the range 1968-2036 and UTC time is reckoned from 0h 0m 0s UTC on 1 January 1900. If bit 0 is not set, the time is in the range 2036-2104 and UTC time is reckoned from 6h 28m 16s UTC on 7 February 2036. Note that when calculating the correspondence, 2000 is not a leap year. Note also that leap seconds are not counted in the reckoning.
即便采用这个“权宜之计”,NTP的可用期限也只能到2104年。我们虽然不关心2104年的情况,但这显然不是通用解决方案。该方案已在一些场景中得到应用,比如Apache Commons Net的NTP时间戳实现。
(注:赋值语句uint16_t year = (uint16_t)(buf->tm_year + 1900);在遥远的Y65536年存在缺陷,但这是所用RTC芯片的固有限制,无法修改。)
内容的提问来源于stack exchange,提问作者tk_

