ctime, mktime 64位版本

背景介绍

====

     进来因为移植万年历, 需要比较和计算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);
}


 

你可能感兴趣的:(c,time)