Unix、UTC、GPS时间戳及转换

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日;

与unix两者相差:err=315964800秒

GPS时间表示:周数Weeks和周内秒Secs,转成秒:

sec = Weeks *7 * 24 * 3600 +Secs

和ros的时间转为同一时间基准:

s = sec + err - 闰秒次数

即
unix_timestamp = gps_timestamp + 315964800 - LEAPSEC

其中315964800 为两个时间起始相差的固定秒数
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时间戳。

具体代码如下:

#include <stdio.h>
#include "timex_test1.h" 
time_tt stCurrentTime; 

int main(void)
{
  stCurrentTime.year = 2023;
  stCurrentTime.month = 4;
  stCurrentTime.date = 13;
  stCurrentTime.hour = 9;
  stCurrentTime.minute = 30;
  stCurrentTime.second = 3;
  printf("%ds", UTCToUnixTimeStamp(&stCurrentTime));
  return 0;
}

UnixTimeStamp_t UTCToUnixTimeStamp(time_tt *time)
{
  int FlatYearMonthDay[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  int LeapYearMonthDay[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  time_tt *tempTime;	//定义临时变量存储时间int i;
  tempTime = time;	//指向传入参数
  int LeapYearNumber;		//闰年
  int FlatYearNumber; //平年
  int ThisYear;
  int LastMonDays = 0;
  UnixTimeStamp_t TimeStamp = 0;

/*step1: 统计从1970年至今年,一共包含多少平年和闰年,并且计算其总秒数*/ 
for ( i = UNIX_TIME_STAMP_YEAR; i < tempTime->year; i ++ )
{
  if((((i % 4) == 0) && ((i % 100) != 0)) || ((i % 400) == 0))
  {
    LeapYearNumber ++;
  }
  else
  {
    FlatYearNumber ++;
  }
}
TimeStamp = LeapYearNumber * 31622400 + FlatYearNumber * 31536000;

/*step2: 判断今年是平年还是闰年*/
if((((tempTime->year % 4) == 0) && ((tempTime->year % 100) != 0)) || ((tempTime->year % 400) ==0))
{
  ThisYear = LEAP_YEAR;
}
else
{
  ThisYear = FLAT_YEAR;
}

for ( i = 1; i < tempTime->month; i ++)
{
  if(ThisYear == LEAP_YEAR)
  {
    LastMonDays += LeapYearMonthDay[i];
  }
  else if(ThisYear == FLAT_YEAR)
  {
    LastMonDays += FlatYearMonthDay[i];
  }
}
LastMonDays = LastMonDays + tempTime->date - 1; //统计当月到昨天为止的天数TimeStamp += LastMonDays * 86400;

/*step3. 计算出剩余的时分秒*/
TimeStamp += tempTime->hour * 3600; 
TimeStamp += tempTime->minute * 60; 
TimeStamp += tempTime->second;
return TimeStamp;
}

timex_test1.h

#ifndef   __TIMEX_TEST1_H_
#define   __TIMEX_TEST1_H_

#define UNIX_TIME_STAMP_YEAR  1970
#define LEAP_YEAR             1
#define FLAT_YEAR             0
typedef struct timex_test1
{
  int year;
  int month; 
  int date; 
  int hour;
  int minute; 
  int second;
} time_tt;

typedef unsigned int UnixTimeStamp_t;
UnixTimeStamp_t UTCToUnixTimeStamp(time_tt *time);
#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 整除的年份是闰年”这一条规则算出当年年份是闰年还是平年。接着, 我们可以直接将当前年份剩余的时间戳结合平年还是闰年, 查表计算出当前的月份。

#include <stdio.h>
#include "timex_test2.h" 
utc_t UtcTime;
int main(void)
{
  int retVal;
  int u32UnixTimeStamp = 0; int hour, minute, sec;
  int flat_year_month_day[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  int leap_year_month_day[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  int year_temp = 0; 
  int day_temp = 0;
  unsigned int cnt_temp = 0; 
  int is_leap_or_flat_year; int i;
  printf("input the UNIX time stamp:"); 
  scanf("%d", &u32UnixTimeStamp);

  cnt_temp = u32UnixTimeStamp;
/*判断当前UNIX时间戳经过了多少个4年*/ 
  while(cnt_temp >= 126230400)
  {
    year_temp ++;
    cnt_temp = cnt_temp - 126230400;
  }
  /*计算出当前的4年周期起始年份*/
  UtcTime.year = UNIX_TIME_YEAR + (4 * year_temp);
  /*计算出当前的年份*/
  /*这部分代码可使用循环做精简,为了直观,我将其写开*/
  if(cnt_temp >= 31536000)
  {
    UtcTime.year ++;
    cnt_temp -= 31536000;
    /*Flat year*/
    if(cnt_temp >= 31536000)
    {
      UtcTime.year ++;
      cnt_temp -= 31536000;
    /*Leap year*/
    if(cnt_temp >= 31622400)
    {
      UtcTime.year ++;
      cnt_temp -= 31622400;
    /*Flat year*/
    if(cnt_temp >= 31536000)
    {
      UtcTime.year ++;
      cnt_temp -= 31536000;
    }
    }
    }

/*计算当前年份是平年还是闰年*/
if((((UtcTime.year % 4) == 0) && ((UtcTime.year % 100) != 0)) || ((UtcTime.year % 4) ==0))
{
  is_leap_or_flat_year = LEAP_YEAR;
}
else
{
  is_leap_or_flat_year = FLAT_YEAR;
}
/*计算出不足一年剩余的天数*/
day_temp = cnt_temp / 86400;

/*剩余不足86400s的时间戳,计算出时间*/
UtcTime.hour = (cnt_temp - day_temp * 86400) / 3600;	//Calculate hours 
UtcTime.minute = (cnt_temp - day_temp * 86400 - UtcTime.hour * 3600) / 60; //Calculate minutes 
UtcTime.second = cnt_temp % 60;

/*将天数结合平年还是闰年查表计算出当前的月份*/
if(is_leap_or_flat_year == FLAT_YEAR)
{
  if(day_temp >= flat_year_month_day[i + 1])
  {
    UtcTime.month ++;
    day_temp -= flat_year_month_day[i + 1];
  }
}
else if(is_leap_or_flat_year == LEAP_YEAR)
{
  if(day_temp >= leap_year_month_day[i + 1])
  {
    UtcTime.month ++;
    day_temp -= leap_year_month_day[i + 1];
  }
}
}

/*由于天数从1开始,因此需要加1*/
UtcTime.date = day_temp + 1;
printf("\nTime transform successfully\n");
printf("++++++++++++++++++++++++++++++++++\n");
printf("\nUTC time is : %dy - %dm - %dd\n", UtcTime.year, UtcTime.month, UtcTime.date); 
printf("\nUTC time is : %dh - %dm : %ds\n", UtcTime.hour, UtcTime.minute, UtcTime.second); 
printf("++++++++++++++++++++++++++++++++++\n");
printf("\n");
printf("\n"); 
return 0;
}

timex_test2.h

#ifndef   __TIMEX_H_
#define   __TIMEX_H_

/*定义UTC时间结构体类型*/ 
typedef struct
{
int year; 
int month; 
int date; 
int hour;
int minute; 
int second;
} utc_t;

/*定义UNIX时间戳的起始UNIX时间*/
#define UNIX_TIME_YEAR	1970
#define UNIX_TIME_MONTH	1
#define UNIX_TIME_DATE	1
#define UNIX_TIME_HOUR	0
#define UNIX_TIME_MINIUTE	0
#define UNIX_TIME_SECOND	0
#define LEAP_YEAR	1
#define FLAT_YEAR

#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/10622.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

测试名词介绍

测试名词介绍一&#xff1a;敏捷测试1. 定义&#xff1a;2. 敏捷测试的核心&#xff1a;3. 敏捷测试的8大原则和传统测试的区别二&#xff1a;测试名词介绍瀑布模型回归测试Alpha测试Beta测试性能测试白盒测试黑盒测试灰盒测试三&#xff1a;测试流程单元测试 (unit test)集成测…

ChatGPT想干掉开发人员,做梦去吧

很多人都发现ChatGPT可以做一些代码相关的工作&#xff0c;不仅可以写一些基础的类似python、java、js的代码段&#xff0c;还可以做一定量的调优&#xff0c;于是就开始担忧起来&#xff0c;到哪天我的开发工作会不会被ChatGPT这个工具给取代了&#xff1f; 目录 1. ChatGPT…

前 K 个高频元素(力扣刷题代码随想录刷题)

给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 思路&#xff1a; 要统计元素出现频率对频率排序找出前K个高频元素首先统计元素出现的频率&#xff0c;这一类的问题可以使用map来进行统计。 然后是对频率…

【血泪建议】软件测试岗位现状,可惜之前没人告诉我,肠子都晦青了....

谈到现状&#xff0c;国内的软件测试行情目前呈现了两极分化的极端情况。 一个是早期的手工测试人员吐槽工作不好做&#xff0c;即使有工作也是外包&#xff0c;而且薪资太低&#xff1b;一方面是很多互联网企业感叹自动化测试人才难找&#xff0c;有技术的自动化测试工程师&a…

批量自动翻译软件-准确的翻译软件

现代社会&#xff0c;在全球化背景下&#xff0c;语言障碍是碍企业发展的主要因素之一。而翻译软件的出现&#xff0c;为人们跨越语言障碍带来了新的解决方案。针对翻译需要大量文字内容的情况&#xff0c;有一些能翻译大量文字的翻译软件&#xff1a; 147CGPT翻译软件特点&…

面试篇-揭开Spring Bean加载的神秘面纱

SpringBean加载完整过程 启动spring容器&#xff08;创建beanfactory&#xff09;->加载配置(注解、xml)->实例化bean(执行构造方法)->注入依赖->初始化bean&#xff08;设置属性值&#xff09;->使用->销毁 解析和读取 XML 配置文件或注解配置类&#xff0…

【C++基础】引用(引用的概念;引用的特性;常引用;使用场景:做输出型参数、大对象传参、做输出型返回值、返回大对象的引用);引用和指针的区别)

六、引用 6.1 引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。&#xff08;语法上&#xff09; 格式&#xff1a;类型& 引用变量名(对象名) …

用vscode运行Java程序初体验

最近开始学习Java编程了&#xff0c;以前学习过C、C 、Python&#xff0c;主要用微软的visual studio code来运行python程序&#xff0c;于是就尝试了用vscode来运行java代码&#xff0c;记录一下使用的经验&#xff0c;帮助大家少走弯路。 安装了Java的集成编辑器IDE "Ec…

Docker环境安装

Docker环境安装Docker简介Docker工作原理Docker的应用场景Docker 的优点CentOS Docker 安装与配置Docker 安装Docker 配置Docker容器概念Docker容器操作拉取镜像删除镜像容器相关命令创建并启动容器停止和恢复容器删除容器Docker简介 Docker 是一个开源的应用容器引擎&#xf…

不卷不成魔,新时代的IT人员更需要卷,不卷不成活

简介 从2022年开始至今&#xff0c;IT界发生了很多巨大的变革带来了许多巨大的变化。 这些变革、这些变化导致了有人欢喜有人悲、有人迷茫有人焦虑。1年半来&#xff0c;迷茫、焦虑、精神内耗了也都差不多了&#xff0c;大家都已经认识到了现实&#xff0c;作为凡人的我们所能…

【防止恶意用户注册】-- 手机在网状态 API 的防欺诈应用解析

简介 手机在网状态 API 支持传入手机号码&#xff0c;查询手机号在网状态&#xff0c;返回在网、在网不可用、不在网&#xff08;销号/未启用/停机&#xff09;等多种状态&#xff0c;查询手机号在网状态之后&#xff0c;可以根据具体的业务需求来进行不同的处理。 本文主要介…

由libunifex来看Executor的任务构建

前言 之前的一篇文章讲述了future的优缺点&#xff0c;以及future的组合性&#xff0c;其中也讲述了构建任务DAG一些问题&#xff0c;同时给出了比较好的方案则是Executor。 Executor还未进入标准&#xff08;C23&#xff09;&#xff0c;Executor拥有惰性构建及良好的抽象模型…

c/c++:windows平台下依赖的动态库,c底层是汇编语言,程序断点调试,反汇编,vs快捷键

c/c&#xff1a;windows平台下依赖的动态库&#xff0c;c底层是汇编语言&#xff0c;程序断点调试&#xff0c;反汇编&#xff0c;vs快捷键 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;此时学会c的话&#xff0c; 我所知…

20230412-使用STM32实现内部flash模拟U盘

最近用STM32F103CBT6搞了个U盘功能 ​ 工程师干了几年后&#xff0c;基本会有小外包的生活&#xff0c;算是赚外快吧&#xff0c;搞小钱改善伙食&#xff0c;嘻嘻。。。。 ​ 最近有个客户找到我&#xff0c;说是否通过ST的单片机搞个U盘功能&#xff0c;有些文件通过U盘拖拽…

005:Mapbox GL添加全屏显示功能

第005个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中添加全屏显示功能 。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共60行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https://…

1~3年的测试工程师薪资陷入了瓶颈期,如何突破自己实现涨薪?

对于技术人员而言&#xff0c;职业规划一般分为两个方向&#xff1a;做技术、做管理。进入软件测试行业的新人都会从最基础的执行开始&#xff0c;然后是基本的功能测试。 随后大家会根据个人职业发展来进一步细化&#xff0c;有的走管理路线&#xff0c;成为主管、经理、项目…

Python 小型项目大全 76~81

七十六、井字棋 原文&#xff1a;http://inventwithpython.com/bigbookpython/project76.html 井字棋是一种在3 3网格上玩的经典纸笔游戏。玩家轮流放置 X 或 O 标记&#xff0c;试图连续获得三个。大多数井字棋都以平局告终&#xff0c;但如果你的对手不小心&#xff0c;你也…

AI 时代,提示词便是生产力

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;蚂蚁集团高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《EffectiveJava》独家解析》专栏作者。 热门文章推荐…

瑞芯微RK3568核心板强在何处?

RK3568核心板产品简介 RK3568核心板是武汉万象奥科基于瑞芯微Rockchip的RK3568设计的一款高性能核心板。该处理器集成了最新的高性能CPU、GPU&#xff0c;并拥有丰富的接口&#xff0c;非常适用于工业自动化控制、人机界面、中小型医疗分析器、电力等多种行业应用。 HD-RK3568-…

项目8:用户注册和登录的前后端联调

项目8&#xff1a;用户注册和登录的前后端联调 1.前端项目使用 2.前端项目注册模块 3.后端完成项目注册 4.前端项目登录模块 5.后端完成项目登录 6.用户认证&#xff08;校验用户是否登录&#xff09; 项目8&#xff1a;用户注册和登录的前后端联调 1.前端项目使用 直接…