目录
Unix时间戳
Unix 时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数,不考虑闰秒
时间戳存储在一个秒计数器中,秒计数器为32位/64位的整型变量
世界上所有时区的秒计数器相同,不同时区通过添加偏移来得到当地时间
UNIX时间戳的0按照ISO 8601规范为 :1970-01-01T00:00:00Z.
一个小时表示为UNIX时间戳格式为:3600秒;一天表示为UNIX时间戳为86400秒,闰秒不计算。
在大多数的UNIX系统中UNIX时间戳存储为32位,这样会引发2038年问题或Y2038。
目前看秒是10位10位数
毫秒是13位10位数
时间 | 秒
1 分钟 | 60
1 小时 | 3600
1 天 | 86400
1 周 | 604800
1 月 (30.44 天) | 2629743
1年 (365.24 天) | 31556736
参考文章:
后面会讲BKP和RTC外设结构
BKP备份寄存器,需要VBAT引脚提供备用电源来维持
知识补充
GMT / UTC
GMT(Greenwich Mean Time)格林尼治标准时间是一种以地球自转为基础的时间计量系统。它将地球自转一周的时间间隔等分为24小时,以此确定计时标准
UTC(Universal Time Coordinated)协调世界时是一种以原子钟为基础的时间计量系统。它规定铯133原子基态的两个超精细能级间在零磁场下跃迁辐射9,192,631,770周所持续的时间为1秒。当原子钟计时一天的时间与地球自转一周的时间相差超过0.9秒时,UTC会执行闰秒来保证其计时与地球自转的协调一致
时间戳转换
time.h教程网站:C 标准库 – <time.h> | 菜鸟教程 (runoob.com)
简介
time.h 头文件定义了四个变量类型、两个宏和各种操作日期和时间的函数。
库变量
下面是头文件 time.h 中定义的变量类型:
序号 | 变量 & 描述 |
---|---|
1 | size_t 是无符号整数类型,它是 sizeof 关键字的结果。 |
2 | clock_t 这是一个适合存储处理器时间的类型。 |
3 | time_t is 这是一个适合存储日历时间的类型。 |
4 | struct tm 这是一个用来保存时间和日期的结构。 |
tm 结构的定义如下:
struct tm {
int tm_sec; /* 秒,范围从 0 到 59 */
int tm_min; /* 分,范围从 0 到 59 */
int tm_hour; /* 小时,范围从 0 到 23 */
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月,范围从 0 到 11 */
int tm_year; /* 自 1900 年起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时 */
};
库宏
下面是头文件 time.h 中定义的宏:
序号 | 宏 & 描述 |
---|---|
1 | NULL 这个宏是一个空指针常量的值。 |
2 | CLOCKS_PER_SEC 这个宏表示每秒的处理器时钟个数。 |
库函数
下面是头文件 time.h 中定义的函数:
序号 | 函数 & 描述 |
---|---|
1 | char *asctime(const struct tm *timeptr) 返回一个指向字符串的指针,它代表了结构 timeptr 的日期和时间。 |
2 | clock_t clock(void) 返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。 |
3 | char *ctime(const time_t *timer) 返回一个表示当地时间的字符串,当地时间是基于参数 timer。 |
4 | double difftime(time_t time1, time_t time2) 返回 time1 和 time2 之间相差的秒数 (time1-time2)。 |
5 | struct tm *gmtime(const time_t *timer) timer 的值被分解为 tm 结构,并用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示。 |
6 | struct tm *localtime(const time_t *timer) timer 的值被分解为 tm 结构,并用本地时区表示。 |
7 | time_t mktime(struct tm *timeptr) 把 timeptr 所指向的结构转换为一个依据本地时区的 time_t 值。 |
8 | size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr) 根据 format 中定义的格式化规则,格式化结构 timeptr 表示的时间,并把它存储在 str 中。 |
9 | time_t time(time_t *timer) 计算当前日历时间,并把它编码成 time_t 格式。 |
C语言的time.h模块提供了时间获取和时间戳转换的相关函数,可以方便地进行秒计数器、日期时间和字符串之间的转换
time_t
默认__time64_t,实际上就是__int64,实际上就是64位有符号整形数据~
struct tm
struct tm组合在一起,代表一个结构体类型名,后面自定义变量名(没有使用typedef,和STM32库函数的结构体方式是一样的效果)
从VScode转到这个结构体的定义,和上面菜鸟教程里的数据稍微有一些出入
struct tm
{ /* A structure for storing the attributes of a broken-down time; (once
* again, it isn't defined elsewhere, so no guard is necessary). Note
* that we are within the scope of <time.h> itself, so we must provide
* the complete structure declaration here.
*/
int tm_sec; /* Seconds: 0-60 (to accommodate leap seconds) */
int tm_min; /* Minutes: 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 */
int tm_year; /* Years since 1900 */
int tm_wday; /* Days since Sunday (0-6) */
int tm_yday; /* Days since Jan. 1: 0-365 */
int tm_isdst; /* +1=Daylight Savings Time, 0=No DST, -1=unknown */
};
char *
【[C语言] 指针的详解与应用-理论结合实践,真正理解指针!】 https://www.bilibili.com/video/BV1Mb4y1X7dz/?share_source=copy_web&vd_source=8af85e60c2df9af1f0fd23935753a933
time()函数
获取系统时钟,这个函数可以直接读取电脑的时间,但是在STM32里不能读取时间;因为STM32是一个离线的裸机系统,不知道时间是多少
代码:
#include <stdio.h>
#include <time.h>
time_t time_cnt; // 实际上是int64类型数据
struct tm time_data;
char *time_str;
int main(void)
{
time_cnt = time(NULL); // 参数不知道,给一个空参数
printf("%d\n", time_cnt);
return 0;
}
输出结果:
带入时间戳转换工具网站 时间戳(Unix timestamp)转换工具 - 在线工具 (tool.lu)
现在时间~2024年2月19日15:46:43
用输出参数的方法获取时间
#include <stdio.h>
#include <time.h>
time_t time_cnt; // 实际上是int64类型数据
struct tm time_data;
char *time_str;
int main(void)
{
time_cnt = time(NULL); // 参数不知道,给一个空参数
printf("%d\n", time_cnt);
time(&time_cnt); // 形参给 输出参数的地址,和上面那一句作用相同
printf("%d\n", time_cnt);
return 0;
}
输出结果:
手动给一个时间数值,用PPT上的时间 北京时间 2023-1-1 23:59:55
转换成日期时间
接着把这个数据转换成日期时间的格式
转换伦敦时间
调用struct tm* gmtime(const time_t*);函数,秒计数器转换为日期时间(转换成格林尼治时间)
代码:
#include <stdio.h>
#include <time.h>
time_t time_cnt; // 实际上是int64类型数据
struct tm time_data;
char *time_str;
int main(void)
{
time_cnt = time(NULL); // 参数不知道,给一个空参数
printf("%d\n", time_cnt);
time(&time_cnt); // 形参给 输出参数的地址,和上面那一句作用相同
printf("%d\n\n", time_cnt);
time_cnt = 1672588795; // 北京时间 2023-1-1 23:59:55
printf("手动赋值北京时间:%d\n", time_cnt); // 手动赋值
printf("转换时间\n"); // 手动赋值
time_data = *gmtime(&time_cnt); // 把手动赋值时间的地址传入进去,返回值是struct tm*,所以取内容(不能指针跨级赋值)
printf("%d\n", time_data.tm_year); // 年
printf("%d\n", time_data.tm_mon); // 月
printf("%d\n", time_data.tm_mday); // 日
printf("%d\n", time_data.tm_hour); // 时
printf("%d\n", time_data.tm_min); // 分
printf("%d\n", time_data.tm_sec); // 秒
printf("%d\n", time_data.tm_wday); // 星期
printf("Hello World!\r\n");
return 0;
}
输出结果:
123年0月1日,15时59分55秒,星期0(星期天),这样的输出结果不对呀!!!
根据程序的定义,年是从1900年经过的年数,月是从1月经过的月数,所以代码里要加上这个偏移数据
修正之后的代码:
#include <stdio.h>
#include <time.h>
time_t time_cnt; // 实际上是int64类型数据
struct tm time_data;
char *time_str;
int main(void)
{
time_cnt = time(NULL); // 参数不知道,给一个空参数
printf("%d\n", time_cnt);
time(&time_cnt); // 形参给 输出参数的地址,和上面那一句作用相同
printf("%d\n\n", time_cnt);
time_cnt = 1672588795; // 北京时间 2023-1-1 23:59:55
printf("手动赋值北京时间:%d\n", time_cnt); // 手动赋值
printf("转换时间\n"); // 手动赋值
time_data = *gmtime(&time_cnt); // 把手动赋值时间的地址传入进去,返回值是struct tm*,所以取内容(不能指针跨级赋值)
printf("%d\n", time_data.tm_year + 1900); // 年
printf("%d\n", time_data.tm_mon + 1); // 月
printf("%d\n", time_data.tm_mday); // 日
printf("%d\n", time_data.tm_hour); // 时
printf("%d\n", time_data.tm_min); // 分
printf("%d\n", time_data.tm_sec); // 秒
printf("%d\n", time_data.tm_wday); // 星期
printf("Hello World!\r\n");
return 0;
}
修正之后输出结果:
现在输出的伦敦时间(格林尼治时间)能与PPT的时间对应上
转换当地时间
调用struct tm* localtime(const time_t*);函数,秒计数器转换为日期时间(当地时间)
localtime函数和gmtime函数作用是相同的,是指localtime会根据市区自动添加小时的偏移
代码:
#include <stdio.h>
#include <time.h>
time_t time_cnt; // 实际上是int64类型数据
struct tm time_data;
char *time_str;
int main(void)
{
// time_cnt = time(NULL); // 参数不知道,给一个空参数
// printf("%d\n", time_cnt);
// time(&time_cnt); // 形参给 输出参数的地址,和上面那一句作用相同
// printf("%d\n\n", time_cnt);
time_cnt = 1672588795; // 北京时间 2023-1-1 23:59:55
printf("手动赋值北京时间:%d\n", time_cnt); // 手动赋值
printf("转换为本地时间\n");
time_data = *localtime(&time_cnt); // 把手动赋值时间的地址传入进去,返回值是struct tm*,所以取内容(不能指针跨级赋值)
printf("%d\n", time_data.tm_year + 1900); // 年
printf("%d\n", time_data.tm_mon + 1); // 月
printf("%d\n", time_data.tm_mday); // 日
printf("%d\n", time_data.tm_hour); // 时
printf("%d\n", time_data.tm_min); // 分
printf("%d\n", time_data.tm_sec); // 秒
printf("%d\n", time_data.tm_wday); // 星期
printf("Hello World!\r\n");
return 0;
}
使用loacltime函数输出结果:
这个函数会根据电脑的设置,自动判断我们处于哪个时区,时间添加偏移,输出数据
成功判断我们在东8区,与手动赋值的北京时间一致
日期时间转换为秒计数器(当地时间)
time_t mktime(struct tm*);
代码:
#include <stdio.h>
#include <time.h>
time_t time_cnt; // 实际上是int64类型数据
struct tm time_data;
char *time_str;
int main(void)
{
// time_cnt = time(NULL); // 参数不知道,给一个空参数
// printf("%d\n", time_cnt);
// time(&time_cnt); // 形参给 输出参数的地址,和上面那一句作用相同
// printf("%d\n\n", time_cnt);
time_cnt = 1672588795; // 北京时间 2023-1-1 23:59:55
printf("手动赋值北京时间:%d\n", time_cnt); // 手动赋值
printf("转换为本地时间\n");
time_data = *localtime(&time_cnt); // 把手动赋值时间的地址传入进去,返回值是struct tm*,所以取内容(不能指针跨级赋值)
printf("%d\n", time_data.tm_year + 1900); // 年
printf("%d\n", time_data.tm_mon + 1); // 月
printf("%d\n", time_data.tm_mday); // 日
printf("%d\n", time_data.tm_hour); // 时
printf("%d\n", time_data.tm_min); // 分
printf("%d\n", time_data.tm_sec); // 秒
printf("%d\n", time_data.tm_wday); // 星期
//上面的逆过程~再转换成秒
time_cnt = mktime(&time_data);
printf("日期时间转换为秒计数器:%d\n", time_cnt);
printf("Hello World!\r\n");
return 0;
}
输出结果:
可以看到,mktime()函数转换完之后,能和手动赋值的北京时间对应上,并且是当地时间!
time_t mktime(struct tm*);函数前面没加const,这个参数struct tm*即是输入参数,又是输出参数,给定年月日,再给出时分秒,就能转换成秒,即可顺带算出是星期几,回填到参数struct tm*
时间转换成字符串
秒计数器转换为字符串(默认格式)
代码:
#include <stdio.h>
#include <time.h>
time_t time_cnt; // 实际上是int64类型数据
struct tm time_data;
char *time_str;
int main(void)
{
// time_cnt = time(NULL); // 参数不知道,给一个空参数
// printf("%d\n", time_cnt);
// time(&time_cnt); // 形参给 输出参数的地址,和上面那一句作用相同
// printf("%d\n\n", time_cnt);
time_cnt = 1672588795; // 北京时间 2023-1-1 23:59:55
printf("手动赋值北京时间:%d\n", time_cnt); // 手动赋值
printf("转换为本地时间\n");
time_data = *localtime(&time_cnt); // 把手动赋值时间的地址传入进去,返回值是struct tm*,所以取内容(不能指针跨级赋值)
printf("%d\n", time_data.tm_year + 1900); // 年
printf("%d\n", time_data.tm_mon + 1); // 月
printf("%d\n", time_data.tm_mday); // 日
printf("%d\n", time_data.tm_hour); // 时
printf("%d\n", time_data.tm_min); // 分
printf("%d\n", time_data.tm_sec); // 秒
printf("%d\n", time_data.tm_wday); // 星期
// 上面的逆过程~再转换成秒
time_cnt = mktime(&time_data);
printf("日期时间转换为秒计数器:%d\n", time_cnt);
time_str = ctime(&time_cnt);
printf(time_str); // 打印这个字符串
return 0;
}
输出结果:
输出默认的时间格式,内容是,星期天、一月一、23:59:55、2023年
日期时间转换为字符串(默认格式)
代码:
#include <stdio.h>
#include <time.h>
time_t time_cnt; // 实际上是int64类型数据
struct tm time_data;
char *time_str;
int main(void)
{
// time_cnt = time(NULL); // 参数不知道,给一个空参数
// printf("%d\n", time_cnt);
// time(&time_cnt); // 形参给 输出参数的地址,和上面那一句作用相同
// printf("%d\n\n", time_cnt);
time_cnt = 1672588795; // 北京时间 2023-1-1 23:59:55
printf("手动赋值北京时间:%d\n", time_cnt); // 手动赋值
printf("转换为本地时间\n");
time_data = *localtime(&time_cnt); // 把手动赋值时间的地址传入进去,返回值是struct tm*,所以取内容(不能指针跨级赋值)
printf("%d\n", time_data.tm_year + 1900); // 年
printf("%d\n", time_data.tm_mon + 1); // 月
printf("%d\n", time_data.tm_mday); // 日
printf("%d\n", time_data.tm_hour); // 时
printf("%d\n", time_data.tm_min); // 分
printf("%d\n", time_data.tm_sec); // 秒
printf("%d\n", time_data.tm_wday); // 星期
// 上面的逆过程~再转换成秒
time_cnt = mktime(&time_data);
printf("日期时间转换为秒计数器:%d\n", time_cnt);
time_str = ctime(&time_cnt); // 秒计数器转换为字符串(默认格式)
printf(time_str); // 打印这个字符串
time_str = asctime(&time_data); // 日期时间转换为字符串(默认格式)
printf(time_str); // 打印这个字符串
return 0;
}
输出结果:
日期时间转换为字符串(自定义格式)
C 库函数 – strftime() | 菜鸟教程 (runoob.com)
size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
- 第一个参数 - 字符数组
- 第二个参数 - 数组长度
- 第三个参数 - 格式字符串
- 第四个参数 - 日期时间
代码:
#include <stdio.h>
#include <time.h>
time_t time_cnt; // 实际上是int64类型数据
struct tm time_data;
char *time_str;
int main(void)
{
// time_cnt = time(NULL); // 参数不知道,给一个空参数
// printf("%d\n", time_cnt);
// time(&time_cnt); // 形参给 输出参数的地址,和上面那一句作用相同
// printf("%d\n\n", time_cnt);
time_cnt = 1672588795; // 北京时间 2023-1-1 23:59:55
printf("手动赋值北京时间:%d\n", time_cnt); // 手动赋值
printf("转换为本地时间\n");
time_data = *localtime(&time_cnt); // 把手动赋值时间的地址传入进去,返回值是struct tm*,所以取内容(不能指针跨级赋值)
printf("%d\n", time_data.tm_year + 1900); // 年
printf("%d\n", time_data.tm_mon + 1); // 月
printf("%d\n", time_data.tm_mday); // 日
printf("%d\n", time_data.tm_hour); // 时
printf("%d\n", time_data.tm_min); // 分
printf("%d\n", time_data.tm_sec); // 秒
printf("%d\n", time_data.tm_wday); // 星期
// 上面的逆过程~再转换成秒
time_cnt = mktime(&time_data);
printf("日期时间转换为秒计数器:%d\n", time_cnt);
time_str = ctime(&time_cnt); // 秒计数器转换为字符串(默认格式)
printf(time_str); // 打印这个字符串
time_str = asctime(&time_data); // 日期时间转换为字符串(默认格式)
printf(time_str); // 打印这个字符串
char t[50];
strftime(t, 50, "%H-%M-%S", &time_data);
printf(t); // 打印这个字符串
return 0;
}
输出结果:
按照指定格式打印了 小时-分钟-秒