/* Copyright (C) 1991, 1993, 1997, 1998, 2002 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

/******************************************************************************
版权所有：	
文件名称：	offtime.c
文件版本：	01.01
创建作者:	   sunxi
创建日期：	2010-05-06
功能说明：	时间格式转化。
其它说明：  原始文件来自gcc\glibc-2.6\time\offtime.c
修改记录：
*/

/*------------------------------- 头文件 --------------------------------------
*/
//#include <linux/time.h>
#include "head.h"


/*------------------------------- 宏定义 --------------------------------------
*/
/* 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))


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


/*------------------------------ 类型结构 -------------------------------------
*/


/*------------------------------ 全局变量 -------------------------------------
*/
/* How many days come before each month (0-12).  */
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 }
}; 

/*------------------------------ 函数声明 -------------------------------------
*/
uint32_t
_mktime(const unsigned int year0, const unsigned int mon0,
       const unsigned int day, const unsigned int hour,
       const unsigned int min, const unsigned int sec);

/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/
/******************************************************************************
函数名称：	rtc_to_timespec
函数版本：	v1.00
创建作者：	杨海涛
创建日期：	2011-2-22
函数说明：	将rtc_time_t结构的数据转换为timespec格式
参数说明：
	p_ts:	输出的timespec结构的时间
	p_rtc：	输入的rtc_time_t结构的时间
返回值：	
	 0：	成功
	-1：	失败
修改记录：	
*/
int rtc_to_timespec(struct rtc_time_t * p_rtc, struct timespec * p_ts)
{
#if 1
	if(check_rtc(p_rtc)!= true)
	{
		return -1;
	}
#else
    if(p_rtc->month > 12 || p_rtc->day > 31 || p_rtc->hour > 24 || p_rtc->min > 60 || p_rtc->ms > 60000)
    {
        return -1;
    }
#endif

    //将系统时间转换为timespec格式
	p_ts->tv_sec = _mktime(p_rtc->year + 2000, p_rtc->month, p_rtc->day, p_rtc->hour, p_rtc->min, (p_rtc->ms)/1000);
	p_ts->tv_nsec = p_rtc->ms%1000*1000000;
	
	//RTC和GPS中的时间是北京时间(东8区)，我们需要将其调整为UTC时间。
	//p_ts->tv_sec -= 8*60*60;
	
	return 0;
}


/******************************************************************************
函数名称：	timespec_to_rtc
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-11-04
函数说明：	将timespec结构的数据转换为rtc_time_t格式
参数说明：
	   ts:	输入的timespec结构的时间
	p_rtc：	输出的rtc_time_t结构的时间
 is_round:	是否需要四舍五入到ms
返回值：	
	0：		成功
	其它：	失败
修改记录：
2015-5-14: sunxi 	增加is_round参数。当只需要ms精度时，应该设置为1，使ms的精度更高；
					如果需要此函数的结果和ts中ns组成更高精度的时间记录，应设置为0，且使用ns时，
					也不应该进行四舍五入操作，否则将导致组合出来的时间不正确。(是否难以理解?直接问我好了!)
*/
int timespec_to_rtc(struct timespec ts, struct rtc_time_t * p_rtc,int is_round)
{
	long int days, rem, y;
	const unsigned short int *ip;

	if(p_rtc == 0)
	{
		return -1;
	}

	if ((uint32_t)ts.tv_nsec >= NSEC_PER_SEC)
	{
		memset(p_rtc,0,sizeof(*p_rtc));
		return -2;
	}

	// 根据是否需要四舍五入对ms进行处理
	if(is_round)
	{
		ts.tv_nsec += NSEC_PER_MSEC/2;
		if(ts.tv_nsec >= NSEC_PER_SEC)
		{
			ts.tv_nsec -= NSEC_PER_SEC;
			ts.tv_sec++;
		}
	}

	//以日期为界，分开算
	days = ts.tv_sec/ SECS_PER_DAY;
	rem =  ts.tv_sec % SECS_PER_DAY;

	//得到时分秒
	p_rtc->hour = rem / SECS_PER_HOUR;
	rem %= SECS_PER_HOUR;
	p_rtc->min= rem / 60;

	// ms,前面已作四舍五入处理
	p_rtc->ms = rem % 60*MSEC_PER_SEC + ts.tv_nsec/NSEC_PER_MSEC;

#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))

	//算年
	//这个算法有点意思，一下子没有看懂，下次再看
	y = 1970; //UTC
	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;
    }

    //rtc_time_t中的年从2000年起
  	p_rtc->year = y - 2000;
  	if (p_rtc->year != y - 2000)
	{
		/* The year cannot be represented due to overflow.  */
		memset(p_rtc,0,sizeof(*p_rtc));
		return -3;
	}

	//得到月日
	ip = __mon_yday[__isleap(y)];
	for (y = 11; days < (long int) ip[y]; --y)
		continue;
	days -= ip[y];
	
	//rtc_time_t中month是1~12
	//p_rtc->month = y;
	p_rtc->month = y + 1;
	p_rtc->day = days + 1;
  
  return 0;
}

int timespec_to_rtchms(struct timespec ts, struct rtc_time_t * p_rtc,int is_round)
{
	long int rem;

	if(p_rtc == 0)
	{
		return -1;
	}

	if ((uint32_t)ts.tv_nsec >= NSEC_PER_SEC)
	{
		memset(p_rtc,0,sizeof(*p_rtc));
		return -2;
	}

	// 根据是否需要四舍五入对ms进行处理
	if(is_round)
	{
		ts.tv_nsec += NSEC_PER_MSEC/2;
		if(ts.tv_nsec >= NSEC_PER_SEC)
		{
			ts.tv_nsec -= NSEC_PER_SEC;
			ts.tv_sec++;
		}
	}

	//以日期为界，分开算
	rem =  ts.tv_sec % SECS_PER_DAY;

	//得到时分秒
	p_rtc->hour = rem / SECS_PER_HOUR;
	rem %= SECS_PER_HOUR;
	p_rtc->min= rem / 60;

	// ms,前面已作四舍五入处理
	p_rtc->ms = rem % 60*MSEC_PER_SEC + ts.tv_nsec/NSEC_PER_MSEC;

	return 0;	
}

uint32_t mktimehms(const unsigned int hour,
       const unsigned int min, const unsigned int sec)
{
	return (uint32_t)((hour*60 + min)*60 + sec); /* finally seconds */
}

int check_rtc(struct rtc_time_t  *pct)
{
	//时间有效检查
	if(pct->year>99) return false;
	if(pct->month>12||pct->month<1) return false;
	if(pct->day>31||pct->day<1)return false;
	if(pct->hour>23)return false;
	if(pct->min>59) return false;
	if(pct->ms>=60000) return false;

	if(pct->month==2)  // 2月特殊处理
	{
		unsigned short year=pct->year+2000;
		if(year%4==0)  //闰年
		{
			if(pct->day>29)return false;
		}	
		else
		{
			if(pct->day>28)return false;
		}
	}
	if(pct->month==4||pct->month==6||pct->month==9||pct->month==11)	
	{
		if(pct->day>30)
		{
			return false;
		}
	}
	return true;
}

/*------------------------------ 内部函数 -------------------------------------
内部函数以下划线‘_’开头，不需要检查参数的合法性.
*/
/* 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 where 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)
 */
// 本函数来自Linux内核
uint32_t
_mktime(const unsigned int year0, const unsigned int mon0,
       const unsigned int day, const unsigned int hour,
       const unsigned int min, const unsigned int sec)
{
	unsigned int mon = mon0, year = year0;

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

	return ((((uint32_t)
		  (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 */
}



 
/*------------------------------ 测试函数 -------------------------------------
一个实体文件必须带一个本模块的测试函数来进行单元测试，如果的确不方便在本模块中
进行单元测试，必须在此注明实际的测试位置（例如在哪个实体文件中使用哪个测试函数）.
*/


/*------------------------------ 文件结束 -------------------------------------
*/

