背景介绍
====
进来因为移植万年历, 需要比较和计算1970之前和之后的时间,比如1900-2100。javascript 中Date.UTC() 对于小于
1970 的日期返回负值,对大的日期也支持,很好用。于是想如何在C中做。
我试图改造过 ADOdb Date Library, part of the ADOdb abstraction library
Download: http://phplens.com/phpeverywhere/,把PHP翻译为C, 但是它写的啰嗦, 很是麻烦, 还有性能问题。
最后想到可以参考标准库, 设计决策, 不要使用负值time_t, 处理起来麻烦;规定1900为0起点,-1表示出错。
全文如下,FYI:
/*time64.c -- 64 bit version of time.c author: ludi ref P.J. PLAUCER "the standard C library.pdf" doc: time_t -- unsigned int -- [1900,2036] unix -- 1970.01.01 00:00:00 -- (70*365+17)*86400 seconds passed since 1900. _I64_MAX = 9223372036854775807 -- enough for big bang time--15 billion years = 47304 * 10^13 seconds. */ #include <stdint.h> #include <limits.h> #include <stdio.h> typedef struct datetime{ int second; /*seconds after the minute [0,61]*/ int minute; /*minutes after the hour [0,59]*/ int hour; /*hours since midnight [0,23]*/ int mday; /*day of the month [1,31]*/ int mon; /*months since January [0,11]*/ int year; /*years since _1900_*/ int wday; /*days since Sunday [0,6]*/ int yday; /*days since January 1 [0,365]*/ }datetime_t; typedef int64_t date_t; #define _TBIAS ((date_t)(70*365+17)*86400) #define WDAY 1 static const short lmos[] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}; static const short mos[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; #define MONTAB(y) (y & 03 || y == 0 ? mos : lmos) int isLeapYear(int year) {/*四年一闰,百年不闰,四百年再闰*/ return (year%4==0&&year%100!=0)||year%400==0; } void test_leap() { int start = 1901, end = 50050; int i, sum = 0, day; for(i = start; i <= end; ++i){ if(isLeapYear(i))++sum; } end = end - start; day = end/4 - end/100 + (1900+end)/400 - 1900/400; printf("real %d, esti %d\n", sum, day); } int Daysto(int year, int mon) {/*compute extra days to start of month*/ int days, end; /*correct for leap year: 1801-*/ if(0 < year){ end = year - 1; days = end/4 - end/100 + (1900+end)/400 - 1900/400; }else if(year <= -4){ days = 1 + (4-year)/4; }else{ days = 0; } return days + MONTAB(year)[mon]; } datetime_t Ttotm( date_t secarg) {/*convert scalar time to datetime*/ int year, mon; date_t days, i; date_t secs; datetime_t dt; const short *pm; secs = secarg + _TBIAS; days = secs/86400; dt.wday = (days + WDAY)%7; for(year = days/365; days < (i = Daysto(year, 0) + 365*year);) --year; days -= i; dt.year = year; dt.yday = days; pm = MONTAB(year); for(mon = 12; days < pm[--mon]; ) ; dt.mon = mon; dt.mday = days - pm[mon] + 1; secs %= 86400; dt.hour = secs/3600; secs %= 3600; dt.minute = secs/60; dt.second = secs%60; return dt; } date_t tmtoT(datetime_t *t) {/*convert time structure to scalar time*/ long double dsecs; int mon, year, ymon; date_t secs; ymon = t->mon/12; mon = t->mon - ymon*12; if(mon < 0) mon += 12, --ymon; if(ymon < 0 && t->year < INT_MIN - ymon || 0 < ymon && INT_MAX - ymon < t->year) return (date_t)-1; year = t->year + ymon; dsecs = 86400.0 * (Daysto(year, mon) - 1) + 31536000.0 * year + 86400.0 * t->mday; dsecs += 3600.0 * t->hour + 60.0 * t->minute + (double)t->second; if(dsecs < 0.0) return (date_t)(-1); secs = (date_t)dsecs - _TBIAS; return secs; } int main() { test_leap(); datetime_t dt = { .year = 1900 - 1900, .mon = 6 - 1, .mday = 18, .hour = 18, .minute = 48, .second = 0, }; datetime_t d2 = {0}; date_t t = tmtoT(&dt); d2 = Ttotm(t); /*you can compare with: var t = Date.UTC(3013,5,18, 18, 48,0)/1000; document.write(" t " + t); */ printf("t %I64d \n", t); printf("%d-%d-%d %02d:%02d:%02d\n", d2.year + 1900, d2.mon + 1, d2.mday, d2.hour, d2.minute, d2.second); }