mktime和localtime_r时间转换问题

2012-08-26 wcdj


由UNIX时间戳转换为系统时间

date -d'1970-01-01 UTC 2147483647 seconds' +"%Y-%m-%d %T %z"           
2038-01-19 11:14:07 +0800
date -d'1970-01-01 UTC 2147483648 seconds' +"%Y-%m-%d %T %z"
date: invalid date "1970-01-01 UTC 2147483648 seconds"
date -d'1970-01-01 UTC 0 seconds' +"%Y-%m-%d %T"          
1970-01-01 08:00:00 +0800 

显示当前的UNIX时间戳

date +%s

Unix时间与系统时间之间的转换
http://shijianchuo.911cha.com/

/* 
 * 2012-08-26 wcdj
 * 使用Gauss算法解决UNIX时间问题
 * 将UNIX时间2038-01-19 03:14:08
 * 支持扩大到2106-02-07 06:28:16
 */
#include 
#include 
#include 		// struct tm, mktime
#include 	// gettimeofday

#define MAX_TIME_DIFF   (24*60*60)
#define SECS_PER_HOUR   (60 * 60)
#define SECS_PER_DAY    (SECS_PER_HOUR * 24)


// calc microseconds
long long get_time_of_day()
{
	struct timeval tv;
	if (gettimeofday(&tv, NULL) == 0) 
	{
		return tv.tv_sec * 1000000ULL + tv.tv_usec;
	}
	else 
	{
		return 0;
	}
}


static long _timezone = MAX_TIME_DIFF+1;	 

/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
 * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
 *
 * [For the Julian calendar (which was used in Russia before 1917,
 * Britain & colonies before 1752, anywhere else before 1582,
 * and is still in use by some communities) leave out the
 * -year/100+year/400 terms, and add 10.]
 *
 * This algorithm was first published by Gauss (I think).
 *
 * WARNING: this function will overflow on 2106-02-07 06:28:16 on
 * machines were long is 32-bit! (However, as time_t is signed, we
 * will already get problems at other places on 2038-01-19 03:14:08)
 */
static inline unsigned int
my_mktime (unsigned int year, unsigned int mon,
		unsigned int day, unsigned int hour,
		unsigned int min, unsigned int sec)
{
	if(_timezone == MAX_TIME_DIFF+1)
	{
		// 表示_timezone没有初始化
		struct timeval tv;
		struct timezone tz;
		gettimeofday(&tv, &tz);
		_timezone = tz.tz_minuteswest*60;
	}


	if (0 >= (int) (mon -= 2)) {	/* 1..12 -> 11,12,1..10 */
		mon += 12;		/* Puts Feb last since it has leap day */
		year -= 1;
	}

	/*START 防止"1970-01-01 00:00:00"到"1970-01-01 08:00:00"时间段的错误*/

	unsigned int iRet = (((
					(year/4 - year/100 + year/400 + 367*mon/12 + day) +
					year*365 - 719499
					)*24 + hour /* now have hours */
				)*60 + min /* now have minutes */
			)*60 + sec  /* finally seconds */
		+ _timezone;  /* time_zone */

	if(iRet < 0)
	{
		iRet = 0;
	}

	return iRet;

	/*END*/
}


static long _timezone_ = MAX_TIME_DIFF+1;	
static const unsigned short int __mon_yday[2][13] =
{
	/* Normal years.  */
	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
	/* Leap years.  */
	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};


int my_localtime_r(unsigned int *timep, struct tm *result)
{
	const unsigned short int *ip;
	if(_timezone_ == MAX_TIME_DIFF + 1)
	{
		struct timeval tv;
		struct timezone tz;
		gettimeofday(&tv, &tz);
		_timezone_ = tz.tz_minuteswest*60;
	}

	int days = (*timep) / SECS_PER_DAY;
	unsigned int rem = (*timep) % SECS_PER_DAY;
	rem -= _timezone_;
	while (rem < 0)
	{   
		rem += SECS_PER_DAY;
		--days;
	}   
	while (rem >= SECS_PER_DAY)
	{   
		rem -= SECS_PER_DAY;
		++days;
	} 

	result->tm_hour = rem / SECS_PER_HOUR;
	rem %= SECS_PER_HOUR;
	result->tm_min = rem / 60; 
	result->tm_sec = rem % 60; 
	/* January 1, 1970 was a Thursday.  */
	result->tm_wday = (4 + days) % 7;
	if (result->tm_wday < 0)
		result->tm_wday += 7;

	int y = 1970;

#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
#ifndef __isleap
	/* Nonzero if YEAR is a leap year (every 4 years,
	 * 	 *    except every 100th isn't, and every 400th is).  */
# define __isleap(year) \
	((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#endif

	while (days < 0 || days >= (__isleap (y) ? 366 : 365))
	{   
		/* Guess a corrected year, assuming 365 days per year.  */
		long int yg = y + days / 365 - (days % 365 < 0); 

		/* Adjust DAYS and Y to match the guessed year.  */
		days -= ((yg - y) * 365 
				+ LEAPS_THRU_END_OF (yg - 1)
				- LEAPS_THRU_END_OF (y - 1));
		y = yg; 
	}   
	result->tm_year = y - 1900;
	if (result->tm_year != y - 1900)
	{   
		/* The year cannot be represented due to overflow.  */
		return -1;
	}   
	result->tm_yday = days;
	ip = __mon_yday[__isleap(y)];
	for (y = 11; days < (long int) ip[y]; --y)
		continue;
	days -= ip[y];
	result->tm_mon = y;
	result->tm_mday = days + 1;

	return 0;
}



// return readable format time
size_t get_date_str(time_t tsrc, char *buffer, size_t len)
{	
	struct tm tm = {0};

	/* 
	 * modify 
	 */
	//localtime_r(&tsrc, &tm);// the reentrant version
	my_localtime_r((unsigned int *)&tsrc, &tm);


	int iMicrosec = get_time_of_day() % 1000000;

	return snprintf(buffer, len, "%04d-%02d-%02d %02d:%02d:%02d %d",
			tm.tm_year + 1900, 
			tm.tm_mon + 1, 
			tm.tm_mday,
			tm.tm_hour, 
			tm.tm_min, 
			tm.tm_sec,
			iMicrosec);
}


int main(int argc, char** argv)
{

	unsigned int now = get_time_of_day()/1000000;
	printf("unixi timestamp: %u\n", now);// date +%s

	char szTime[128] = {0};		
	get_date_str(now, szTime, sizeof(szTime));
	printf("current time: [%s]\n", szTime);


#if 1
	/* test for localtime_r*/

	// [1]
	now = 0;
	memset(szTime, 0x0, sizeof(szTime));
	get_date_str(now, szTime, sizeof(szTime));
	printf("time: [%s]\n", szTime);

	// [2]
	now = 4294967295U;// max of unsigned int
	memset(szTime, 0x0, sizeof(szTime));
	get_date_str(now, szTime, sizeof(szTime));
	printf("time: [%s]\n", szTime);

	// [3]
	now = 2147483647;// max of int
	memset(szTime, 0x0, sizeof(szTime));
	get_date_str(now, szTime, sizeof(szTime));
	printf("time: [%s]\n", szTime);


#endif


	/* test for mktime */
	time_t rawtime;
	struct tm * timeinfo;
	int year, month, day;
	// prompt user for date
	printf("Enter year: ");scanf("%d", &year);
	printf("Enter month: ");scanf("%d", &month);
	printf("Enter day: ");scanf("%d", &day);

	// get current timeinfo and modify it to the user's choice
	time(&rawtime);
	timeinfo = localtime(&rawtime);
	timeinfo->tm_year = year - 1900;
	timeinfo->tm_mon = month -1;
	timeinfo->tm_mday = day;

	unsigned int rettime; 
	/*if (-1 != (rettime = mktime(timeinfo)))
	{
		printf("mktime ret: %d\n", rettime);
	}
	else
	{
		printf("mktime err!\n");
	}*/

	if(timeinfo->tm_year + 1900 > 2106
			|| (timeinfo->tm_year + 1900 == 2106 && timeinfo->tm_mon + 1 > 2)
			|| (timeinfo->tm_year + 1900 == 2106 && timeinfo->tm_mon + 1 == 2 && timeinfo->tm_mday > 6))
	{
		printf("Error, overflow 4294967295!\n");
	}
	else
	{
		rettime = my_mktime(year, month, day, 0, 0, 0);
		printf("mktime ret: %u\n", rettime);
	}



	return 0;
}
/*
output:

unixi timestamp: 1345548878
current time: [2012-08-21 19:34:38 737638]
time: [1970-01-01 08:00:00 737648]
time: [2106-02-07 14:28:15 737652]
time: [2038-01-19 11:14:07 737655]


*/

参考

[1]  Linux源码中的mktime算法解析 (关于Gauss算法的解释)



你可能感兴趣的:(GNU/Linux,C/C++)