Unix时间戳和北京时间的相互转换

一、问题背景

最近项目中需要上传包含时间戳的设备数据到服务器平台。原本想把“年”,“月”,“日”,“时”,“分”, “秒”分别用一个uint8_t的数据类型去存储,即占用6个字节。但是在平台配置协议时,只有一种叫“Unix时间戳”的数据类型。Unix时间戳只占用4个字节,而且Unix时间戳在服务器端更加通用,但是在单片机上没有想Linux环境下现成的time(),localtime(),mktime()等库函数调用。所以考虑自己实现Unix时间戳和北京时间的相互转换。

二、Unix时间戳简介

Unix时间戳:
是从1970年1月1日00:00:00开始到当前时刻经过的秒数。
例如:一个小时表示为Unix时间戳格式为:3600秒;一天表示为Unix时间戳为86400秒。 
当然由于时区的关系,北京时间在算出来的秒数后面需要加上8个小时(8*3600秒)。

比如在linux中,我们获取Unix时间戳可以用:

 
  1. typedef long time_t; /* time value */

  2. time_t time(time_t * timer)

调用后会返回一个time_t类型的值(即long)。由于在大多数32位的设备上,long为4个字节有符号数,所以最大秒数为:2^23,大约2038年就会存在溢出的问题。所以后面的设备都用64位去存储,当然这不是本文探讨的地方。

为什么使用Unix时间戳?
在服务器端使用Unix时间戳更加通用。

三、算法转换思路

北京时间转Unix时间戳:

这个转换比较简单,用当前的时间的年月日时分秒,依次减去1970/1/1 00:00:00即可。只要注意闰年的情况就行,最后注意需要加上北京时区的8个小时。

Unix时间戳转北京时间:

不严谨的说每隔4年就有一个闰年(此处暂不考虑2100年这样的非闰年,因为time_t限制,可取的范围只有1970~2038),所以可以将4年看做一个周期(即365+365+365+366=1461天)。通过总天数除以1461得到周期的个数,然后1970加上周期的个数乘以4就是年份。总天数对1461取余就是这个周期内的天数,然后根据平闰年去判断年月日时分秒。

四、具体代码

 
  1. #include <stdio.h>

  2. #include <string.h>

  3. #include "stdint.h"

  4.  
  5.  
  6. #define FOURYEARDAY (365+365+365+366) //4年一个周期内的总天数(1970~2038不存在2100这类年份,故暂不优化)

  7. #define TIMEZONE (8) //北京时区调整

  8.  
  9. typedef struct rtc_time_struct

  10. {

  11. uint16_t ui8Year; // 1970~2038

  12. uint8_t ui8Month; // 1~12

  13. uint8_t ui8DayOfMonth; // 1~31

  14. uint8_t ui8Week;

  15. uint8_t ui8Hour; // 0~23

  16. uint8_t ui8Minute; // 0~59

  17. uint8_t ui8Second; // 0~59

  18.  
  19. }rtc_time_t;

  20.  
  21. static uint8_t month_day[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //平年

  22. static uint8_t Leap_month_day[12]={31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //闰年

  23. const uint16_t dayPerYear[4] = {365, 365, 365, 366};

  24.  
  25. // 判断是否是闰年

  26. // year: 需要判断的年

  27. // return:1:闰年

  28. // 0: 平年

  29. uint8_t isLeapYear(uint16_t year)

  30. {

  31. uint8_t res=0;

  32.  
  33. if(year%4 == 0) // 能够被4整除

  34. {

  35. if((year%100 == 0) && (year%400 != 0)) //能够被100整除,但是不能够被400整除

  36. {

  37. res = 0;

  38. }

  39. else

  40. {

  41. res =1;

  42. }

  43. }

  44. return res;

  45. }

  46.  
  47. // 将Unix时间戳转换为北京时间

  48. // unixTime: 需要判断的Unix时间戳

  49. // *tempBeijing:返回的北京时间

  50. // return:none

  51. // note:没对输入参数正确性做判断

  52. void covUnixTimeStp2Beijing(uint32_t unixTime, rtc_time_t *tempBeijing)

  53. {

  54. uint32_t totleDaynum=0, totleSecNum=0;

  55. uint16_t remainDayofYear, tempDay=0;

  56. uint8_t *pr, tempYear=0;

  57.  
  58.  
  59. totleDaynum = unixTime/(24*60*60); //总天数(注意加括号)

  60. totleSecNum = unixTime%(24*60*60); //当天剩余的秒速

  61.  
  62. memset(tempBeijing, 0x00, sizeof(rtc_time_t));

  63. //1.计算哪一年

  64. tempBeijing->ui8Year = 1970 + (totleDaynum/FOURYEARDAY)*4;

  65. remainDayofYear = totleDaynum%FOURYEARDAY+1;

  66. while(remainDayofYear >= dayPerYear[tempYear]){

  67. remainDayofYear -= dayPerYear[tempYear];

  68. tempBeijing->ui8Year++;

  69. tempYear++;

  70. }

  71.  
  72. //2.计算哪一月的哪一天

  73. pr = isLeapYear(tempBeijing->ui8Year)?Leap_month_day:month_day;

  74. while(remainDayofYear > *(pr+tempBeijing->ui8Month))

  75. {

  76. remainDayofYear -= *(pr+tempBeijing->ui8Month);

  77. tempBeijing->ui8Month++;

  78. }

  79. tempBeijing->ui8Month++; //month

  80. tempBeijing->ui8DayOfMonth = remainDayofYear; //day

  81.  
  82. //3.计算当天时间

  83. tempBeijing->ui8Hour = totleSecNum/3600;

  84. tempBeijing->ui8Minute = (totleSecNum%3600)/60; //error:变量搞错

  85. tempBeijing->ui8Second = (totleSecNum%3600)%60;

  86.  
  87. //4.时区调整

  88. tempBeijing->ui8Hour +=TIMEZONE;

  89. if(tempBeijing->ui8Hour>23){

  90. tempBeijing->ui8Hour -= 24;

  91. tempBeijing->ui8DayOfMonth++;

  92. }

  93. }

  94.  
  95. // 将北京时间转换为Unix时间戳

  96. // year: 需要判断的年

  97. // return:Unix时间戳(从1970/1/1 00:00:00 到现在的秒数)

  98. // note:没对输入参数正确性做判断

  99. uint32_t covBeijing2UnixTimeStp(rtc_time_t *beijingTime)

  100. {

  101. uint32_t daynum=0, SecNum=0; //保存北京时间到起始时间的天数

  102. uint16_t tempYear=1970, tempMonth=0;

  103.  
  104.  
  105. //1.年的天数

  106. while(tempYear < beijingTime->ui8Year)

  107. {

  108. if(isLeapYear(tempYear)){

  109. daynum += 366;

  110. }

  111. else{

  112. daynum += 365;

  113. }

  114. tempYear++;

  115. }

  116. //2.月的天数

  117. while(tempMonth < beijingTime->ui8Month-1)

  118. {

  119. if(isLeapYear(beijingTime->ui8Year)){ //闰年

  120. daynum += Leap_month_day[tempMonth];

  121. }

  122. else{

  123. daynum += month_day[tempMonth];

  124. }

  125. tempMonth++;

  126. }

  127. //3.天数

  128. daynum += (beijingTime->ui8DayOfMonth-1);

  129.  
  130. //4.时分秒

  131. SecNum = daynum*24*60*60; //s

  132. SecNum += beijingTime->ui8Hour*60*60;

  133. SecNum += beijingTime->ui8Minute*60;

  134. SecNum += beijingTime->ui8Second;

  135.  
  136. //5.时区调整

  137. SecNum -= TIMEZONE*60*60;

  138.  
  139. return SecNum;

  140. }

  141.  
  142.  
  143. //测试主函数

  144. int main()

  145. {

  146. rtc_time_t testTime;

  147. uint32_t UnixTimsStamp=0;

  148.  
  149. // 测试用例:平/闰年,闰月,8点前等

  150. // 使用时,修改这里就可以

  151. testTime.ui8Year = 2016;

  152. testTime.ui8Month = 02;

  153. testTime.ui8DayOfMonth = 29;

  154. testTime.ui8Hour = 05;

  155. testTime.ui8Minute = 20;

  156. testTime.ui8Second = 00;

  157.  
  158. UnixTimsStamp = covBeijing2UnixTimeStp(&testTime);

  159. printf("%d/%02d/%02d-%02d:%02d:%02d convert is: %u\n\n", \

  160. testTime.ui8Year, testTime.ui8Month, testTime.ui8DayOfMonth, \

  161. testTime.ui8Hour, testTime.ui8Minute, testTime.ui8Second, UnixTimsStamp);

  162.  
  163. covUnixTimeStp2Beijing(UnixTimsStamp, &testTime);

  164. printf("%u convert is: %d/%02d/%02d-%02d:%02d:%02d\n", UnixTimsStamp,

  165. testTime.ui8Year, testTime.ui8Month, testTime.ui8DayOfMonth, \

  166. testTime.ui8Hour, testTime.ui8Minute, testTime.ui8Second);

  167. }

运行结果如下:

五、结果验证

通过站长工具验证,测试ok:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值