21. 时间和日期
谈到时间(包括日期),必须先明确下面的概念:
格林威治时间(Coordinated Universal Time)
.世界标准时间
(UTC).这里面需要知道下面一些概念:时区(time zone)
:由于世界各国家与地区经度不同,地方时也有所不同,因此将其划分为24个时区
,其中UTC
(Coordinated Universal Time,世界标准时间)(以前称为格林威治时间(GMT))为0时区。夏令时
:现在用的不多了。建议百度下概念。
1970年1月1日-00.00.00(UTC)
:posix标准的固定时间点
(epoch)。计算机使用整型数计秒的起始点。分解时间(broken-down time)
:就是将时间按照年、月、日、时、分、秒等格式显示的时间。如Wed Apr 1 11:23:33 2020
。- 注意:它与
时区
(time zone)相关。
- 注意:它与
简单日历时间(simple calendar time)
:就是从固定时间点
(1970年1月1日-00.00.00(UTC))到当前时间点所经过的秒数。计算机在计时时就使用它。- 注意:它与
时区
(time zone)无关。固定为0区(UTC).
- 注意:它与
其次,我们首先需要了解几个时间相关的数据类型和数据结构:
clock_t
数据类型:系统滴答计数。可用于测量进程和cpu的时间。time_t
数据类型:代表从一个固定时间点
到当前时间点所经过的秒数的数据类型。struct timespec
数据类型:类似time_t
。它包含更精确的时间ns。结构体成员如下struct timespec{ time_t tv_sec; /* 秒, */ long tv_nsec; /* 纳秒 */ //eg:tv_sec + tv_nsec为从epoch开始到目前所经过的时间 };
struct tm
:表示分解时间
的数据类型,结构体成员如下struct tm { int tm_sec; /* Seconds (0-60) */ int tm_min; /* Minutes (0-59) */ int tm_hour; /* Hours (0-23) */ int tm_mday; /* Day of the month (1-31) */ int tm_mon; /* Month (0-11) */ int tm_year; /* Year - 1900 */ int tm_wday; /* Day of the week (0-6, Sunday = 0) */ int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */ int tm_isdst; /* Daylight saving time */ }; //tm_isdst:和夏令时有关。 //gnu扩展了两个参数tm_gmtoff和tm_zone,是和时区相关的 //其中tm_gmtoff指定了当前时间在日期变更线两边的秒数(东为正,西为负) //tm_zone为当前时区的字符串名称,如CST.
有了上面的基础。它的相关函数就很容易理解和使用了。因为计算机使用简单日历时间
,而使用者一般使用分解时间
.所以时间相关的函数很多都是关于它俩转换的。使用下面的函数都需要包含头文件time.h
。
大体分为下面几类:
- 获取、修改系统时间相关的函数。
- 通过
简单日历时间
来获取(或转换)为分解时间
相关函数. - 根据
分解时间
来获取(或转换)为简单日历时间
相关函数。 - 将
简单日历时间
转换为字符串相关函数。 - 将
分解时间
转换为字符串相关函数。 - 将字符串转换为
分解时间
、简单日历时间
相关函数。 时区
操作相关函数.- 让系统睡眠相关的函数。
获取、修改系统时间相关的函数
time_t time(time t *result)
- 描述:获取时间(秒数),这个秒数从1970-01-01 00:00:00 +0000 (UTC)计算。
- result:如果不为空,返回值同时保存在这个参数中。
- return:返回时间值。
int clock_gettime(clockid t clock, struct timespec *ts)
- 描述:获取时间,精确到ns.
- clock:
- CLOCK_REALTIME:系统真实的时间.随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变.
- CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响.
- CLOCK_PROCESS_CPUTIME_ID:此进程总共占cpu的时间。
- CLOCK_THREAD_CPUTIME_ID:此进程总共占cpu的时间。
- ts:获取到的时间。
- return:成功为0,失败为-1.
int clock_getres(clockid t clock, struct timespec *res)
- 描述:获取时间精度.
- clock:同上.
- res:获取到的时间精度。
int gettimeofday (struct timeval *tp, void *tzp)
- 描述:获取时间,精确到ms.不建议使用,一般用clock_gettime()代替。
int clock_settime(clockid t clock, const struct timespec *ts)
- 描述:修改当前时间。需要有相应的权限。这个函数不关心时区。对于在系统运行时所做的小更改,最好使用ntp_adjtime()来实现从一个时间到另一个时间的平稳过渡。
- 其他同上。
将日历时间转换为分解时间相关函数
struct tm *localtime(const time_t *timep)
- 描述:将日历时间转换成系统当前时区的分解时间。由于它不是线程安全函数。所以建议使用它的代替品localtime_r()函数。
- timep:日历时间指针。一般通过time()函数获取。
- return:返回分解时间的结构体指针。
struct tm *localtime_r(const time_t *timep, struct tm *result)
- 功能同localtime()函数,但它是线程安全的。
- result:用于保存转换后的分解函数数据。
struct tm *gmtime(const time_t *timep)
- 描述:类似localtime(),它是将日历时间转换成时0区(即UTC时间)的分解时间。
struct tm *gmtime_r(const time_t *timep, struct tm *result)
- 功能同gmtime()函数,但它是线程安全的。
将分解时间转换为日历时间相关函数
time_t mktime(struct tm *tm)
- 描述:将分解时间转换成日历时间,同时填充tm对象中的某些成员。如
tm_day
,tm_yday
,tm_isdst
等。如果结构成员超出其有效区间,则将其规范化。如4月40号会被修改为4月9号。此函数使用的时区为系统默认时区。 - return:如果指定的tm不能转换成日历时间,返回-1.成功则返回日历时间.
- 描述:将分解时间转换成日历时间,同时填充tm对象中的某些成员。如
time_t timelocal(struct tm *tm)
- 描述:功能类似mktime().
time_t timegm(struct tm *tm)
- 描述:功能类似mktime().但是它将输入值tm当作UTC时间。(不管tm中设置的时区是什么)。
将时间转换为字符串相关函数
char *asctime(const struct tm *tm)
- 描述:将分解时间转换成字符串。如
"Tue May 21 13:46:22 1991\n"
。它不是线程安全的,建议使用asctime_r()
代替。 - return:返回一个静态的字符串指针。
- 描述:将分解时间转换成字符串。如
char *asctime_r(const struct tm *brokentime, char *buffer)
- 描述:效果和asctime()一样。它是线程安全的。
- buffer:长度要至少大于26字节。
- return:返回buffer的指针。
char *ctime(const time_t *time)
- 描述:将日历时间转换为分解时间。效果等同于
asctime (localtime (time))
它不是线程安全的,建议使用ctime_r()
代替。 - return:返回一个静态的字符串指针。
- 描述:将日历时间转换为分解时间。效果等同于
char *ctime_r(const time_t *time, char *buffer)
- 描述:ctime()一样。它是线程安全的。
- buffer:长度要至少大于26字节。
- return:返回buffer的指针。
size_t strftime(char *s, size_t max, const char *format,const struct tm *tm)
- 描述:此函数类似
sprintf()
.用户通过format
参数自己指定转换格式,得到分解时间的字符串。 - format:可以指定的格式:
%A
:指定星期(全名)。%B
:指定月份(全名)。%C
:指定年(比如1970会显示70)。- …具体查看man手册
- return:返回buffer的指针。
- 描述:此函数类似
char *strptime(const char *s, const char *format, struct tm *tm)
- 描述:将时间字符串转换成分解时间.
struct tm *getdate(const char *string)
- 描述:将时间字符串转换成分解时间.线程不安全。建议使用
getdate_r()
代替。
- 描述:将时间字符串转换成分解时间.线程不安全。建议使用
操作时区相关的函数、变量
注意使用下面的全局变量前最好使用tzset()
刷新一下。
TZ
- 环境变量。与时域相关。
char * tzname[2]
- 全局变量。包含两个变量。其中
tzname[0]
是一个指向一个标准时间域字符串。如EST
.tzname[1]
跟夏令时有关。
- 全局变量。包含两个变量。其中
long int timezone
- 全局变量。UTC时区与当地时区的差值。单位为秒。
int daylight
- 全局变量。如果应用夏令时规则,此变量的值为非零。
void tzset(void)
- 用
TZ
环境变量初始化tzname
变量。用户一般无需调用此函数,因为在调用其他时间转换函数时被自动调用。
- 用
使用例程
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include "signal.h"
void main(void)
{
time_t curtime;
char timeStrBuff[64];
struct tm *ploctime,timeInfo;
/* 获取当前时间(日历时间) */
curtime = time(NULL);
printf("calender time:%ld \n",curtime);
/* 转换为分解时间 */
ploctime = localtime_r(&curtime,&timeInfo);
char *pTimeStr = asctime_r(ploctime,timeStrBuff);
printf("%s",pTimeStr);
/* 转换成自定义格式 */
strftime (timeStrBuff, 64, "Today is %A, %B %d.\n", ploctime);
printf("%s",timeStrBuff);
strftime (timeStrBuff, 64, "The time is %I:%M %p.\n", ploctime);
printf("%s",timeStrBuff);
}
关于技术交流
此处后的文字已经和题目内容无关,可以不看。
qq群:825695030
微信公众号:嵌入式的日常
如果上面的文章对你有用,欢迎打赏、点赞、评论。