突然发现格林尼治时间换算为北京时间不是简单的+8的问题

严 泽远发布

从GPS里读出了一条命令:
$GPRMC,105404.739,A,2307.5573,N,11323.3638,E,2.65,348.60,040411,,,A*65
提取出来格林尼治时间是:
2011-04-04 10:54:04.739
换算成北京时间+8是18:54:04.739 很简单,没啥问题。
但是晚上又读到一条命令:
$GPRMC,213201.236,A,2307.5573,N,11323.3638,E,2.65,348.60,040411,,,A*65
换算成北京时间就不是简单的+8的问题了,差点儿疏忽,不然会差很远啊。

刚刚写好了一个算法
用于计算各个时区对应的格林尼治时间,自己还没发现大的BUG,欢迎同学们拍砖!


/********************************************************************************************************
** 	函数名称:			bit		IsLeapYear(uchar	iYear)
**	功能描述:			判断闰年(仅针对于2000以后的年份)
**	入口参数:			iYear	两位年数
**	出口参数:			bit		1:为闰年	0:为平年
**	创 建 者:			严泽远
**	创建时间:			2011-04-05 14:29
**	版    本:			v1.0.0
********************************************************************************************************/
bit		IsLeapYear(uchar iYear)
{
	uint	Year;
	Year	=	2000+iYear;
	if((Year&3)==0)
	{
    	return ((Year%400==0) || (Year%100!=0));
	}
 	return 0;
}

/********************************************************************************************************
** 	函数名称:			void	GMTconvert(uchar *DT,uchar GMT,bit AREA)
**	功能描述:			格林尼治时间换算世界各时区时间
**	入口参数:			*DT:	表示日期时间的数组 格式 YY,MM,DD,HH,MM,SS
**						GMT:	时区数
**						AREA:	1(+)东区 W0(-)西区
**	创 建 者:			严泽远
**	创建时间:			2011-04-05 14:08
**	版    本:			v1.0.0
********************************************************************************************************/
void	GMTconvert(uchar *DT,uchar GMT,bit AREA)
{
	uchar	YY,MM,DD,hh,mm,ss;		//年月日时分秒暂存变量
	
	if(GMT==0)	return;				//如果处于0时区直接返回
	if(GMT>12)	return;				//时区最大为12 超过则返回	 	

	YY	=	*DT;					//获取年
	MM	=	*(DT+1);				//获取月
	DD	=	*(DT+2);				//获取日
	hh	=	*(DT+3);				//获取时
	mm	=	*(DT+4);				//获取分
	ss	=	*(DT+5);				//获取秒

	if(AREA)						//东(+)时区处理
	{
		if(hh+GMT<24)	hh	+=	GMT;//如果与格林尼治时间处于同一天则仅加小时即可
		else						//如果已经晚于格林尼治时间1天则进行日期处理
		{
			hh	=	hh+GMT-24;		//先得出时间
			if(MM==1 || MM==3 || MM==5 || MM==7 || MM==8 || MM==10)	//大月份(12月单独处理)
			{
				if(DD<31)	DD++;
				else
				{
					DD	=	1;
					MM	++;
				}
			}
			else if(MM==4 || MM==6 || MM==9 || MM==11)				//小月份2月单独处理)
			{
				if(DD<30)	DD++;
				else
				{
					DD	=	1;
					MM	++;
				}
			}
			else if(MM==2)	//处理2月份
			{
				if((DD==29) || (DD==28 && IsLeapYear(YY)==0))		//本来是闰年且是2月29日 或者不是闰年且是2月28日
				{
					DD	=	1;
					MM	++;
				}
				else	DD++;
			}
			else if(MM==12)	//处理12月份
			{
				if(DD<31)	DD++;
				else		//跨年最后一天
				{		   	
					DD	=	1;
					MM	=	1;
					YY	++;
				}
			}
		}
	}
	else
	{	
		if(hh>=GMT)	hh	-=	GMT;	//如果与格林尼治时间处于同一天则仅减小时即可
		else						//如果已经早于格林尼治时间1天则进行日期处理
		{
			hh	=	hh+24-GMT;		//先得出时间
			if(MM==2 || MM==4 || MM==6 || MM==8 || MM==9 || MM==11)	//上月是大月份(1月单独处理)
			{
				if(DD>1)	DD--;
				else
				{
					DD	=	31;
					MM	--;
				}
			}
			else if(MM==5 || MM==7 || MM==10 || MM==12)				//上月是小月份2月单独处理)
			{
				if(DD>1)	DD--;
				else
				{
					DD	=	30;
					MM	--;
				}
			}
			else if(MM==3)	//处理上个月是2月份
			{
				if((DD==1) && IsLeapYear(YY)==0)					//不是闰年
				{
					DD	=	28;
					MM	--;
				}
				else	DD--;
			}
			else if(MM==1)	//处理1月份
			{
				if(DD>1)	DD--;
				else		//新年第一天
				{		   	
					DD	=	31;
					MM	=	12;
					YY	--;
				}
			}
		}
	}	 	

	*DT	=	YY;					//更新年
	*(DT+1)	=	MM;				//更新月
	*(DT+2)	=	DD;				//更新日
	*(DT+3)	=	hh;				//更新时
	*(DT+4)	=	mm;				//更新分
	*(DT+5)	=	ss;				//更新秒
}	

严 泽远

☑男性☑80后☑已婚☑没大房☑没好车☑没钱☑没工作☑没......

9 条评论

hikook · 2012 年 9 月 25 日 下午 4:57

你好,程序中在计算西区并处理上个月为2月时,是否应该加上处理闰年,即上个月为29号的情况?谢谢!

    ♂唯有→奋斗 · 2012 年 9 月 25 日 下午 5:58

    看一下程序,在这里两种情况2月份都已经处理过了呀!

泥偶 · 2016 年 4 月 20 日 下午 12:45

没有处理闰秒。GPS时间换算成协调世界时,还需要加上闰秒。大概要加上十几秒吧。这是从1980年代到现在累积下来的闰秒。每过半年到一两年还会再增加闰秒。

    ♂唯有→奋斗 · 2016 年 4 月 20 日 下午 1:22

    GPS给的时间是没有处理闰秒的吗?呵呵!

      泥偶 · 2016 年 4 月 20 日 下午 2:15

      闰秒是会随着时间流逝增加的。比如最近一次是在UTC时间2015年6月30日23:59:59之后增加一秒。有些GPS模块会把历史积累的十几秒钟给加上去。那样的话直接读串口输出就行了。但是随着时间流逝,闰秒会增加,除非升级GPS模块的固件,不然不会修正的。或者自己再额外矫正。

        ♂唯有→奋斗 · 2016 年 4 月 20 日 下午 4:21

        GPS的同步授时是取的GPS卫星的时间,并不是自己的时间,也不会随着时间流逝如何如何,我们所有的CDMA和3G基站都是用的GPS授时,这跟GPS模块的固件没有任何关系,遵循的是NMEA0183协议。

          泥偶 · 2016 年 4 月 20 日 下午 11:53

          你说的对。现在GPS报文里面已经包括了 TAI − GPS 的时间差。以下是维基百科上抄来的相关内容。

          https://en.wikipedia.org/wiki/Global_Positioning_System#Timekeeping

          Leap seconds

          While most clocks derive their time from Coordinated Universal Time (UTC), the atomic clocks on the satellites are set to GPS time (GPST; see the page of United States Naval Observatory). The difference is that GPS time is not corrected to match the rotation of the Earth, so it does not contain leap seconds or other corrections that are periodically added to UTC. GPS time was set to match UTC in 1980, but has since diverged. The lack of corrections means that GPS time remains at a constant offset with International Atomic Time (TAI) (TAI − GPS = 19 seconds). Periodic corrections are performed to the on-board clocks to keep them synchronized with ground clocks.[118]

          The GPS navigation message includes the difference between GPS time and UTC. As of July 2015, GPS time is 17 seconds ahead of UTC because of the leap second added to UTC on June 30, 2015.[119][120] Receivers subtract this offset from GPS time to calculate UTC and specific timezone values. New GPS units may not show the correct UTC time until after receiving the UTC offset message. The GPS-UTC offset field can accommodate 255 leap seconds (eight bits).

泥偶 · 2016 年 4 月 20 日 下午 2:11

也有不是整小时的时区。比如印度用 UTC+5:30。尼泊尔用 UTC+5:45。

泥偶 · 2016 年 4 月 20 日 下午 2:24

其实也有使用 UTC+13:00 和 UTC+14:00 运作的国家和地区。

泥偶进行回复 取消回复

邮箱地址不会被公开。 必填项已用*标注

error: Content is protected !!