Unix、UTC、GPS时间戳及转换

打印 上一主题 下一主题

主题 998|帖子 998|积分 2994

UTC时间
UTC时间的英文全称:Universal Time Coordinated,中文名称:协调世界时。俗的明白为,这个时间是全世界通用的,即全世界都公用的一个时间。可以认为格林威治时间就是时间协调时间(GMT=UTC),格林威治时间和UTC时间均用秒数来计算的。
起始时间为:1970年1月1日
北京时间:UTC+8

本地时间
计算机体现的时间
本地时间 = UTC 时间 + 时区 (北京时间是东八区,也就是 +8小时)
UTC + 时区差 = 本地时间
时区差东为正,西为负。在此,把东八区时区差记为 +0800,

UNIX时间
在计算机中看到的UTC时间都是从(1970年01月01日 0:00:00)开始计算秒数的。所看到的UTC时间那就是从1970年这个时间点起到具体时间共有多少秒。 这个秒数就是Unix时间戳,与时区无关。应用于大多数Unix系统(linux,Ubuntu,ros)
考虑到闰秒的话,更精确的定义为从协调世界时(UTC时间)1970年1月1日0时0分0秒起至现在颠末闰秒调解之后的总秒数。
所以Unix时间转换为本地时间,应该先将 Unix时间转换为UTC时间(UTC是0时区的时间),然后再将UTC时间转换为本地时间:
Unix时间戳转换为本地时间的在线工具

GPS时间系统
GPS使用UTC原子钟时间,起始时间为:1980年1月6日;
  1. 与unix两者相差:err=315964800秒
  2. GPS时间表示:周数Weeks和周内秒Secs,转成秒:
  3. sec = Weeks *7 * 24 * 3600 +Secs
  4. 和ros的时间转为同一时间基准:
  5. s = sec + err - 闰秒次数
  6. unix_timestamp = gps_timestamp + 315964800 - LEAPSEC
  7. 其中315964800 为两个时间起始相差的固定秒数
  8. LEAPSEC :闰秒
复制代码
GPS时间系统接纳原子时AT1秒长作时间基准,秒长定义为铯原子CS133基态的两个超精致能级间跃迁辐射振荡9192631170周所持续的时间。
时间起算的原点定义在1980年1月6日世界协调时UTC0时,启动后不跳秒,保证时间的连续。以后随着时间积累,GPS时与UTC时的整秒差以及秒以下的差异通过期间服务部分定期公布。
GPS卫星广播星历接纳WGS-84(G873)世界大地坐标系
闰秒
闰秒是在协调世界时(UTC)中增长或减少一秒,使得UTC时与原子时[5]之间的差不超过0.9秒。
需要闰秒的部分原因是因为一个太阳日并不总是86400秒。当要增长正闰秒时,这一秒是增长在第二天的00:00:00之前,效果是延缓UTC第二天的开始。当天23:59:59的下一秒被记为23:59:60,然后才是第二天的00:00:00。如果是负闰秒的话,23:59:58的下一秒就是第二天的00:00:00了,但现在还没有负闰秒调解的需求。
最近的一次添加闰秒是在2016年12月31日23:59:60。
闰秒是对“协调世界时”(UTC)所做出的基本调解,以便令其与太阳时间保持同步。
2016年7月6日,法国国际地球自转构造发布公告:国际标定时间将在格林尼治时间2016年12月31日23时59分59秒实施一个正闰秒,即增长1秒,届时将出现59分60秒的特别征象。由于北京处于东8区,所以中国将在2017年1月1日7:59:59后面增长1秒,出现7:59:60的特别征象。2016年最后一天,环球多一秒!
一样平常来说,闰秒会被添加在某一年的6月30日大概12月31日。两次闰秒之间的时间间隔并不固定,最短的一次只隔了6个月,最长的一次隔了7年。多出来的一秒对于平凡人的生活或许没有太多影响,但是对于时间连续精度有严格要求的航天、通讯、金融等领域有很大影响。好比一秒钟,飞船已经飞过了8公里!
本次闰秒调解后UTC时间与GPS时间的差将变为18秒,即UTC相对于GPS时慢了18秒
UTC时间转UNIX时间戳
UTC时间转换成UNIX时间戳的方法其实很简朴,因为此条件下输入的年代日是固定的。因此对于这个题目, 我们可以直接按照年代日时分的情势进行计算, 统计每它们的秒数之和。最后加入最后的秒数就可以得到当前的UNIX 时间戳了。我们知道闰年的秒数为31622400s,平年的秒数为31536000s。
步伐的计划思绪为:
1) 统计从1970年至今一共过了多少平年,多少闰年,统计完成之后,根据闰年宁静年的秒数计算出从1970年1月1日至本年一共颠末了多少秒。
2) 计算出当前年份是平年还是闰年, 以此推算出从年初到上个月已往了多少天。计算完成之后,可以将本月的到昨天的天数一起统计进来,最终可以将年代日三个单位的秒数一起统计出来。
3) 将剩余的时分秒利用同样的方式进行累加,最终得出当前的UNIX时间戳。
具体代码如下:
  1. #include <stdio.h>
  2. #include "timex_test1.h"
  3. time_tt stCurrentTime;
  4. int main(void)
  5. {
  6.   stCurrentTime.year = 2023;
  7.   stCurrentTime.month = 4;
  8.   stCurrentTime.date = 13;
  9.   stCurrentTime.hour = 9;
  10.   stCurrentTime.minute = 30;
  11.   stCurrentTime.second = 3;
  12.   printf("%ds", UTCToUnixTimeStamp(&stCurrentTime));
  13.   return 0;
  14. }
  15. UnixTimeStamp_t UTCToUnixTimeStamp(time_tt *time)
  16. {
  17.   int FlatYearMonthDay[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  18.   int LeapYearMonthDay[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  19.   time_tt *tempTime;        //定义临时变量存储时间int i;
  20.   tempTime = time;        //指向传入参数
  21.   int LeapYearNumber;                //闰年
  22.   int FlatYearNumber; //平年
  23.   int ThisYear;
  24.   int LastMonDays = 0;
  25.   UnixTimeStamp_t TimeStamp = 0;
  26. /*step1: 统计从1970年至今年,一共包含多少平年和闰年,并且计算其总秒数*/
  27. for ( i = UNIX_TIME_STAMP_YEAR; i < tempTime->year; i ++ )
  28. {
  29.   if((((i % 4) == 0) && ((i % 100) != 0)) || ((i % 400) == 0))
  30.   {
  31.     LeapYearNumber ++;
  32.   }
  33.   else
  34.   {
  35.     FlatYearNumber ++;
  36.   }
  37. }
  38. TimeStamp = LeapYearNumber * 31622400 + FlatYearNumber * 31536000;
  39. /*step2: 判断今年是平年还是闰年*/
  40. if((((tempTime->year % 4) == 0) && ((tempTime->year % 100) != 0)) || ((tempTime->year % 400) ==0))
  41. {
  42.   ThisYear = LEAP_YEAR;
  43. }
  44. else
  45. {
  46.   ThisYear = FLAT_YEAR;
  47. }
  48. for ( i = 1; i < tempTime->month; i ++)
  49. {
  50.   if(ThisYear == LEAP_YEAR)
  51.   {
  52.     LastMonDays += LeapYearMonthDay[i];
  53.   }
  54.   else if(ThisYear == FLAT_YEAR)
  55.   {
  56.     LastMonDays += FlatYearMonthDay[i];
  57.   }
  58. }
  59. LastMonDays = LastMonDays + tempTime->date - 1; //统计当月到昨天为止的天数TimeStamp += LastMonDays * 86400;
  60. /*step3. 计算出剩余的时分秒*/
  61. TimeStamp += tempTime->hour * 3600;
  62. TimeStamp += tempTime->minute * 60;
  63. TimeStamp += tempTime->second;
  64. return TimeStamp;
  65. }
复制代码
timex_test1.h
  1. #ifndef   __TIMEX_TEST1_H_
  2. #define   __TIMEX_TEST1_H_
  3. #define UNIX_TIME_STAMP_YEAR  1970
  4. #define LEAP_YEAR             1
  5. #define FLAT_YEAR             0
  6. typedef struct timex_test1
  7. {
  8.   int year;
  9.   int month;
  10.   int date;
  11.   int hour;
  12.   int minute;
  13.   int second;
  14. } time_tt;
  15. typedef unsigned int UnixTimeStamp_t;
  16. UnixTimeStamp_t UTCToUnixTimeStamp(time_tt *time);
  17. #endif
复制代码
UNIX时间转UTC时间戳
1)时分秒的转换
当UNIX时间戳的计数值小于86400(24h)的时候,我们很轻易就能写出转换成UTC时间的步伐, 因为小时数就是“UnixTimeStamp” 对3200 取模, 分数就是将不能凑满小时的“UnixTimeStamp” 对60 取模, 剩余不能凑满分数的“UnixTimeStamp” 即为当前时间的秒数。
2)年代日的转换
接下来,我们将要再对“日”的上一层单位进行讨论,即“月”数值,这也将是这个步伐最为复杂的一部分内容。
这个复杂点主要体现在两个方面:
(1) 每个月的天数不等。众所周知,一年中每个月的天数都是不同的,1,3,5,7,8, 10,12为大月,一个月有31天;4,6,9,11为小月,一个月有30天。
(2) 闰年平年的影响。由于公历的偏差,导致了一年中最为特别的一个月份2月,当此年为闰年时,2月份有29天,此年为平年时,2月份有28天。
上面两个原因,导致了年代日计算的复杂性。
但是,困难只是表面上的,我们细致思索下,就很轻易得出规律。这个规律的突破口即为闰年出现的时间, 因为闰年每四年出现一次, 那么我们可以列出从1970 年开始的几个年份。如下图所示:

由于闰年每四年出现一次,因此我们由图3中可以得出一个简朴方法,即可以从1790年开始,每四年组成一个集合,每一个集合的都是由1年闰年加上3年平年组成的,它们的时间都是相等的,即126230400秒。
因此这个月数的求解步调就可以变为:
(1) 计算从1970 年开始到当前的UNIX时间戳为止, 一共过了多少个“ 集合年( 平年+ 闰年)”;
(2) 计算出当前的UNIX时间戳位于本“集合年“的哪一年,这样就可以判断当年年份是平年
还是闰年;
(3) 判断了当前年份是平年还是闰年之后,就可以推算出2月份有多少天,然后可以根据上述的递归法,求解出当前位于某一月,某一天。
所以,我们先求出当前的年份,得出当前年份之后, 我们就可以很轻易使用“ 能被4 整除且不能被100 整除, 大概能被400 整除的年份是闰年”这一条规则算出当年年份是闰年还是平年。接着, 我们可以直接将当前年份剩余的时间戳连合平年还是闰年, 查表计算出当前的月份。
  1. #include <stdio.h>
  2. #include "timex_test2.h"
  3. utc_t UtcTime;
  4. int main(void)
  5. {
  6.   int retVal;
  7.   int u32UnixTimeStamp = 0; int hour, minute, sec;
  8.   int flat_year_month_day[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  9.   int leap_year_month_day[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  10.   int year_temp = 0;
  11.   int day_temp = 0;
  12.   unsigned int cnt_temp = 0;
  13.   int is_leap_or_flat_year; int i;
  14.   printf("input the UNIX time stamp:");
  15.   scanf("%d", &u32UnixTimeStamp);
  16.   cnt_temp = u32UnixTimeStamp;
  17. /*判断当前UNIX时间戳经过了多少个4年*/
  18.   while(cnt_temp >= 126230400)
  19.   {
  20.     year_temp ++;
  21.     cnt_temp = cnt_temp - 126230400;
  22.   }
  23.   /*计算出当前的4年周期起始年份*/
  24.   UtcTime.year = UNIX_TIME_YEAR + (4 * year_temp);
  25.   /*计算出当前的年份*/
  26.   /*这部分代码可使用循环做精简,为了直观,我将其写开*/
  27.   if(cnt_temp >= 31536000)
  28.   {
  29.     UtcTime.year ++;
  30.     cnt_temp -= 31536000;
  31.     /*Flat year*/
  32.     if(cnt_temp >= 31536000)
  33.     {
  34.       UtcTime.year ++;
  35.       cnt_temp -= 31536000;
  36.     /*Leap year*/
  37.     if(cnt_temp >= 31622400)
  38.     {
  39.       UtcTime.year ++;
  40.       cnt_temp -= 31622400;
  41.     /*Flat year*/
  42.     if(cnt_temp >= 31536000)
  43.     {
  44.       UtcTime.year ++;
  45.       cnt_temp -= 31536000;
  46.     }
  47.     }
  48.     }
  49. /*计算当前年份是平年还是闰年*/
  50. if((((UtcTime.year % 4) == 0) && ((UtcTime.year % 100) != 0)) || ((UtcTime.year % 4) ==0))
  51. {
  52.   is_leap_or_flat_year = LEAP_YEAR;
  53. }
  54. else
  55. {
  56.   is_leap_or_flat_year = FLAT_YEAR;
  57. }
  58. /*计算出不足一年剩余的天数*/
  59. day_temp = cnt_temp / 86400;
  60. /*剩余不足86400s的时间戳,计算出时间*/
  61. UtcTime.hour = (cnt_temp - day_temp * 86400) / 3600;        //Calculate hours
  62. UtcTime.minute = (cnt_temp - day_temp * 86400 - UtcTime.hour * 3600) / 60; //Calculate minutes
  63. UtcTime.second = cnt_temp % 60;
  64. /*将天数结合平年还是闰年查表计算出当前的月份*/
  65. if(is_leap_or_flat_year == FLAT_YEAR)
  66. {
  67.   if(day_temp >= flat_year_month_day[i + 1])
  68.   {
  69.     UtcTime.month ++;
  70.     day_temp -= flat_year_month_day[i + 1];
  71.   }
  72. }
  73. else if(is_leap_or_flat_year == LEAP_YEAR)
  74. {
  75.   if(day_temp >= leap_year_month_day[i + 1])
  76.   {
  77.     UtcTime.month ++;
  78.     day_temp -= leap_year_month_day[i + 1];
  79.   }
  80. }
  81. }
  82. /*由于天数从1开始,因此需要加1*/
  83. UtcTime.date = day_temp + 1;
  84. printf("\nTime transform successfully\n");
  85. printf("++++++++++++++++++++++++++++++++++\n");
  86. printf("\nUTC time is : %dy - %dm - %dd\n", UtcTime.year, UtcTime.month, UtcTime.date);
  87. printf("\nUTC time is : %dh - %dm : %ds\n", UtcTime.hour, UtcTime.minute, UtcTime.second);
  88. printf("++++++++++++++++++++++++++++++++++\n");
  89. printf("\n");
  90. printf("\n");
  91. return 0;
  92. }
复制代码
timex_test2.h
  1. #ifndef   __TIMEX_H_
  2. #define   __TIMEX_H_
  3. /*定义UTC时间结构体类型*/
  4. typedef struct
  5. {
  6. int year;
  7. int month;
  8. int date;
  9. int hour;
  10. int minute;
  11. int second;
  12. } utc_t;
  13. /*定义UNIX时间戳的起始UNIX时间*/
  14. #define UNIX_TIME_YEAR        1970
  15. #define UNIX_TIME_MONTH        1
  16. #define UNIX_TIME_DATE        1
  17. #define UNIX_TIME_HOUR        0
  18. #define UNIX_TIME_MINIUTE        0
  19. #define UNIX_TIME_SECOND        0
  20. #define LEAP_YEAR        1
  21. #define FLAT_YEAR
  22. #endif
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大号在练葵花宝典

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表