Linux-C++获取当前时间与计算时间间隔

科技   2024-11-04 12:13   北京  

在嵌入式软件开发中,有时会用到对日期时间的判断与处理,比如记录某个事件发生的时间,比较某个时刻已过去的时间等等。

记录时间,可以使用ISO8601国际标准格式的时间,便于与其它软件交互时做到统一。

本篇就来介绍ISO8601格式时间的生成以及两个ISO8601格式的时间间隔的计算。

1 与时间相关的定义

在介绍具体的编程实现之前,需要先了解需要用到的一些与时间相关的类型定义与函数接口

1.1 类型与结构体

1.1.1 timeval

存储秒和微秒

struct timeval
{

long tv_sec; /*秒*/
long tv_usec; /*微秒*/
};

1.1.2 tm

表示日历时间格式的时间

struct tm {
int tm_sec; /* seconds after the minute - [0,59] 秒*/
int tm_min; /* minutes after the hour - [0,59] 分钟*/
int tm_hour; /* hours since midnight - [0,23] 小时*/
int tm_mday; /* day of the month - [1,31] 日*/
int tm_mon; /* months since January - [0,11],月,使用时一般会加1的偏移量 */
int tm_year; /* years since 1900,年,使用时一般会加1900的偏移量 */
int tm_wday; /* days since Sunday - [0,6] 周*/
int tm_yday; /* days since January 1 - [0,365] */
int tm_isdst; /* daylight savings time flag */
};

1.2 函数

1.2.1 time

time_t time (time_t *time);

参数可以为空,用于获取时间戳。

将自1970年1月1日以来经过的秒数存储在时间戳指针time指向的位置(time为空则不做此处理),并返回相等值的临时time变量;

1.2.2 gettimeofday

int gettimeofday(struct timeval *tv, struct timezone *tz);

参数tv不可为空,tz通常不写默认为空,用于获取系统时间结构(struct tm)。

将自1970年1月1日以来经过的精度为微秒的时间存储于tv结构。获取时间成功返回0,失败返回-1。

1.2.3 localtime

struct tm *localtime (const time_t *time);

参数time不可为空。将时间戳time转换为tm结构;

1.2.4 localtime_r

struct tm *localtime_r(const time_t *timer, struct tm *buf);

将传入参数timer表示的秒数转换为日历时间格式,保存结果在buf,同时也会保存结果一个全局静态变量中,返回这全局静态变量的指针。

注:

  • localtime_r函数通过传入tm参数指针保存转换结果,使localtime_r函数线程安全。
  • 如果使用localtime_r函数返回值表示日历,仍然是线程不安全的,通常仅通过返回值是否为空,判断localtime_r函数转换时间是否成功。

2 获取ISO8601格式的时间

2.1 ISO8601时间格式介绍

国际标准化组织的国际标准ISO 8601是日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。

最新为ISO8601:2019 ,第一版为ISO8601:1988,第二版为ISO8601:2000。

根据ISO8601标准,北京时间2024年11月3日16点37分可以表示为:

2024-11-03T16:37:00+08:00

2.1.1 日期格式

标准日期格式为 YYYY-MM-DD,其中:

  • YYYY 代表四位数的年份,如2024;
  • MM 代表两位数的月份,范围01~12;
  • DD 代表两位数的日,范围01~31。

2.1.2 时间格式

完整时间表示为:HH:MM:SS

  • HH表示两位数的小时,24小时制;
  • MM表示两位数的分钟;
  • SS表示两位数的秒;

可进一步精确到毫秒,表示为:HH:MM:SS.sss

2.1.3 日期时间格式

日期和时间的组合表示为:YYYY-MM-DDTHH:MM:SS

  • T是日期和时间之间的分隔符

注:

“T” 是一个全球统一且不常见的字符,使用 “T” 可以清楚地区分日期和时间这两个不同的概念,避免混淆。

2.1.4 时区表示

ISO8601支持对时区的标准化表示,使用Z表示协调世界时(UTC),或者使用±hh:mm格式表示与UTC的偏移,例如:

  • Z表示 UTC 时间;
  • +08:00表示比 UTC 快8小时的时区;
  • -05:00表示比 UTC 慢5小时的时区;

例如:

  • 2024-03-19T15:26:00Z表示UTC时间下午3点26分0秒;
  • 2024-03-19T15:26:00+08:00表示北京时间下午3点26分0秒;


2.2 编程实现ISO8601时间的获取

代码思路如下:

  • 获取自1970年1月1日以来经过的秒和微秒,存储在timeval中
  • 将秒数通过localtime_r转换为日历时间格式
  • 结合日历时间和微妙数,格式化为ISO8601格式的时间
std::string GetISO8601NowTime()
{
timeval tv{}; //存储自1970年1月1日以来经过的秒和微秒
gettimeofday(&tv, nullptr); //获取自1970年1月1日以来经过的秒和微秒

tm stTM{}; //存储日历时间格式的时间
localtime_r(&tv.tv_sec, &stTM); //将传入参数的秒数转换为日历时间格式

char sTmp[64]{}; //格式化为ISO8601格式的时间
sprintf(sTmp, "%04d-%02d-%02dT%02d:%02d:%02d.%03ld",
stTM.tm_year + 1900, stTM.tm_mon + 1, stTM.tm_mday,
stTM.tm_hour, stTM.tm_min, stTM.tm_sec, tv.tv_usec/1000);

return std::string(sTmp) + "+08:00"; //这里时区暂使用固定的东八区
}

3 计算两个时间的间隔

前面实现了ISO8601时间的获取,如果有两个ISO8601格式的时间,如何计算这两个时间的间隔呢。

在实现该功能前,需要再来介绍需要用到的两个函数。

3.1 函数

3.1.1 strptime

string parse time。parse,解析,用于将string格式的时间解析为tm格式

extern char *strptime (__const char *__restrict __s,
__const char *__restrict __fmt,
struct tm *__tp)
;
  • 参数1: 输入一个char 的指针,可通过c_str()兼容
  • 参数2: 统一为一个char的指针, 用于格式控制的字符串指针,可通过c_str()兼容
  • 参数3: 分解时间的存储,struct tm类型的指针,可定义一个struct tm类型,然后&实现

strftime:string format time。format,格式。把 time 格式化为 string

3.1.2 mktime

time_t mktime(struct tm *timeptr);

用于将结构体 struct tm 表示的日历时间转换为对应的秒数时间戳。

3.2 编程实现

代码思路如下:

  • 将string格式的时间解析为tm格式的日历时间
  • 再将日历时间转换为对应的秒数时间戳
  • 比较两个时间戳 的差值即可
time_t ISO8601ToTimeT(std::string &dateTime)
{
tm stTM{};
//%F是一个代表完整日期的标记,等同于%Y-%m-%d; %T是一个代表完整时间的标记,等同于%H:%M:%S
strptime(dateTime.c_str(), "%FT%T", &stTM); //将string格式的时间解析为tm格式

time_t t = mktime(&stTM); //将日历时间转换为对应的秒数时间戳
return t;
}

uint64_t TimeDurationSec(std::string &oldT, std::string &newT)
{
auto oldPoint = std::chrono::system_clock::from_time_t(ISO8601ToTimeT(oldT));
auto newPoint = std::chrono::system_clock::from_time_t(ISO8601ToTimeT(newT));
return std::chrono::duration_cast<std::chrono::seconds>(newPoint - oldPoint).count();
}

3 测试代码

来编写一个测试代码来验证刚才实现的功能。

  • 先定义一个ISO8601格式的已过去的时间,作为测试时间间隔的old数据
  • 调用编写的GetISO8601NowTime获取当前的ISO8601格式的时间
  • 调用TimeDurationSec来计算两个时间的差值,间隔的秒数
  • 以天、小时、分钟、秒的形式打印出来过去的时间间隔
#include <stdio.h>
#include <ctime>
#include <sys/time.h>
#include <string>
#include <chrono>

//函数实现参考前面代码

int main()
{
std::string t1 = "2024-11-01T17:31:09.000";
std::string t2 = GetISO8601NowTime();
printf("t1(old):%s\nt2(now):%s\n", t1.c_str(), t2.c_str());

uint64_t deltaTotalSec = TimeDurationSec(t1, t2);
uint64_t deltaDay = deltaTotalSec / (3600*24);
uint32_t deltaHour = deltaTotalSec % (3600*24) / 3600;
uint32_t deltaMin = deltaTotalSec % 3600 / 60;
uint32_t deltaSec = deltaTotalSec % 60;
printf("delta sec:%lu(%lu day, %u hour, %u min, %u sec)\n", deltaTotalSec, deltaDay, deltaHour, deltaMin, deltaSec);

return 0;
}

运行结果如下:

4 总结

本篇介绍了ISO8601格式时间的生成和两个ISO8601格式的时间间隔的计算。首先介绍需要用到的一些函数,然后介绍编程实现的思路,编写代码,实现所需的功能,最后进行编译运行测试。


END

来源:码农爱学习

版权归原作者所有,如有侵权,请联系删除

推荐阅读
遇到一位被国产MCU伤透了心的老板
稚晖君的机器人开源了,含全套图纸+代码
美国民兵III型核导弹制导系统和计算机内部欣赏

→点关注,不迷路←

嵌入式微处理器
关注嵌入式相关技术和资讯,你想知道的都在这里。
 最新文章