Linux下时间处理相关函数

1 .系统时间和 RTC 时间

  Linux 系统下包含两个时间: 系统时间RTC 时间
  系统时间: 是由主芯片的定时器进行维护的时间, 一般情况下都会选择芯片上最高精度的定时器作为系统时间的定时基准, 以避免在系统运行较长时间后出现大的时间偏移。特点是掉电后不保存。
  RTC 时间: 是指系统中包含的 RTC 芯片内部所维护的时间。 RTC 芯片都有电池+系统电源的双重供电机制, 在系统正常工作时由系统供电, 在系统掉电后由电池进行供电。 因此系统电源掉电后 RTC 时间仍然能够正常运行。
  每次 Linux 系统启动后在启动过程中会检测和挂载 RTC 驱动, 在挂载后会自动从RTC芯片中读取时间并设置到系统时间中去。 此后如果没有显式的通过命令去控制 RTC 的读写操作,系统将不会再从 RTC 中去获取或者同步设置时间。


2.时间处理相关命令

2.1 获取系统时间

[wbyq@wbyq ~]$ date
2023年 01月 10日 星期二 19:43:23 CST
[wbyq@wbyq ~]$  cat /proc/driver/rtc #查看 rtc 时间


2.2 date 命令功能与参数


  date 可以用来显示或设定系统的日 期与时间。

 
  • 命令参数
[wbyq@wbyq ~]$ date --help
用法:date [选项]... [+格式]
 或:date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]

给定的格式FORMAT 控制着输出,解释序列如下:

  %%	一个文字的 %
  %a	当前locale 的星期名缩写(例如: 日,代表星期日)
  %A	当前locale 的星期名全称 (如:星期日)
  %b	当前locale 的月名缩写 (如:一,代表一月)
  %B	当前locale 的月名全称 (如:一月)
  %c	当前locale 的日期和时间 (如:2005年3月3日 星期四 23:05:25)
  %C	世纪;比如 %Y,通常为省略当前年份的后两位数字(例如:20)
  %d	按月计的日期(例如:01)
  %D	按月计的日期;等于%m/%d/%y
  %e	按月计的日期,添加空格,等于%_d
  %F	完整日期格式,等价于 %Y-%m-%d
  %g	ISO-8601 格式年份的最后两位 (参见%G)
  %G	ISO-8601 格式年份 (参见%V),一般只和 %V 结合使用
  %h	等于%b
  %H	小时(00-23)
  %I	小时(00-12)
  %j	按年计的日期(001-366)
  %k   hour, space padded ( 0..23); same as %_H
  %l   hour, space padded ( 1..12); same as %_I
  %m   month (01..12)
  %M   minute (00..59)
  %n   a newline
  %N   nanoseconds (000000000..999999999)
  %p   locale's equivalent of either AM or PM; blank if not known
  %P   like %p, but lower case
  %q   quarter of year (1..4)
  %r   locale's 12-hour clock time (e.g., 11:11:04 PM)
  %R   24-hour hour and minute; same as %H:%M
  %s   seconds since 1970-01-01 00:00:00 UTC
  %S	秒(00-60)
  %t	输出制表符 Tab
  %T	时间,等于%H:%M:%S
  %u	星期,1 代表星期一
  %U	一年中的第几周,以周日为每星期第一天(00-53)
  %V	ISO-8601 格式规范下的一年中第几周,以周一为每星期第一天(01-53)
  %w	一星期中的第几日(0-6),0 代表周一
  %W	一年中的第几周,以周一为每星期第一天(00-53)
  %x	当前locale 下的日期描述 (如:12/31/99)
  %X	当前locale 下的时间描述 (如:23:13:48)
  %y	年份最后两位数位 (00-99)
  %Y	年份
  %z +hhmm		数字时区(例如,-0400)
  %:z +hh:mm		数字时区(例如,-04:00)
  %::z +hh:mm:ss	数字时区(例如,-04:00:00)
  %:::z			数字时区带有必要的精度 (例如,-04,+05:30)
  %Z			按字母表排序的时区缩写 (例如,EDT)


2.2 设定时间


  date -s 设置当前时间: 只用root权限才能设置,其他用户只能查看。

[xsw@xsw c_2020]$ date -s 20211005
date: 无法设置日 期: 不允许的操作
//设置时间 2020 年 10 月 15 号, 未写具体时间默认 00:00:00
[xsw@xsw c_2020]$ sudo date -s 20201015
2020 年 10 月 15 日 星期四 00:00:00 CST
[xsw@xsw c_2020]$ sudo date -s 00:00:30 //只设置时间 0 时 0 分 30s,不会修改日 期
2020 年 12 月 04 日 星期五 00:00:30 CST
[xsw@xsw c_2020]$ sudo date -s "2020-12-25 12:25:38" //设置具体时间
2020 年 12 月 25 日 星期五 12:25:38 CST
[xsw@xsw c_2020]$ sudo -s "12:35:48 20200805" //设置具体时间
2020 年 08 月 05 日 星期三 12:35:48 CST
[xsw@xsw c_2020]$ sudo date -s "12:35:48 2020-08-05" //设置具体时间
2020 年 08 月 05 日 星期三 12:35:48 CST
[xsw@xsw c_2020]$ sudo date --date "2020-12-12 12:12:12"
2020 年 12 月 12 日 星期六 12:12:12 CST
[xsw@xsw c_2020]$ sudo date --date 12:40:00
2020 年 12 月 04 日 星期五 12:40:00 CST


  • 时间加减
[wbyq@wbyq ~]$ date +%Y%m%d #显示年月日
20230110
[wbyq@wbyq ~]$ date +%Y%m%d --date="+1 day"  #时间+1天
20230111
[wbyq@wbyq ~]$ date +%Y%m%d --date="-1 day" #时间-1天
20230109
[wbyq@wbyq ~]$ date +%Y%m%d --date="-1 month"  #时间-1个月
20221210
[wbyq@wbyq ~]$ date +%Y%m%d --date="+1 month"  #时间+1个月
20230210


3.时间处理相关函数


  头文件: #include <time.h>


  • 相关结构体
struct tm{
	int tm_sec Seconds [0,60]. //秒
	int tm_min Minutes [0,59].//分钟
	int tm_hour Hour [0,23].//小时
	int tm_mday Day of month [1,31].//天
	int tm_mon Month of year [0,11].//月
	int tm_year Years since 1900.//年, 从 1900 年开始
	int tm_wday Day of week [0,6] (Sunday =0).//星期, 0 表示星期天
	int tm_yday Day of year [0,365].//一年中的第多少天
	int tm_isdst Daylight Savings flag.//日 光节约时制
};

3.1 获取系统秒单位时间 time



函数原型: time_t time(time_t _);
函数功能: 获取当前系统秒单位时间, 返回类型为 time_t,其实就是 long 类型, time 函
数获取的时间基准为 1970 年 1 月 1 号 0 时 0 分 0 秒。
形 参: 如果填 NULL 获取当前系统时间的总秒数保存在返回值中。
   如果填其他参数, 则获取到的秒单位时间保存在该变量中。


  • 示例:
#include <stdio.h>
#include <time.h>
int main(int argc,char **argv)
{
time_t time_sec;
time_sec=time(NULL);//或者 time(&time_sec);
printf("总秒数: %ld\n",time_sec);
return 0;
}[
xsw@xsw c_2020]$ gcc test.c
[xsw@xsw c_2020]$ ./a.out
总秒数: 1607067005


3.2 秒单位时间转字符串 ctime



函数原型: char_ctime(const time_t _); char _ctime_r(const time_t _, char_);
函数功能: 将秒单位时间转换为字符串类型时间
形 参: const time_t _ —总秒数
   char _ —用户缓冲区(至少 26 字节, 保存转换的字符串时间)
返 回 值: 返回转换完成字符串时间。


  • 示例
#include <stdio.h>
#include <time.h>
int main(int argc,char **argv)
{
	char buff[30];
	char *p;
	time_t time_sec;
	time_sec=time(NULL);
	printf("总秒数: %ld\n",time_sec);
	p=ctime_r(&time_sec,buff);
	printf("字符串时间: %s",p);
	printf("buff:%s",buff);
	return 0;
}
[xsw@xsw c_2020]$ ./a.out
总秒数: 1607072182
字符串时间: Fri Dec 4 16:56:22 2020
buff:Fri Dec 4 16:56:22 2020


3.3 秒单位时间转为结构体 location



函数原型: struct tm _localtime(const time_t _timep); _
      struct tmlocaltime_r(const time_t _timep, struct tm _result);
函数功能: 转换得到当前日 期时间。
形 参: const time_t _timep – 总秒数 struct tm _result —用户缓冲区(时间结构体)
返 回 值: 转换完成的时间结构体


  • 示例
#include <stdio.h>
#include <time.h>
int main(int argc,char **argv)
{
	struct tm *Time;
	time_t time_sec;
	time_sec=time(NULL);
	printf("总秒数: %ld\n",time_sec);
	Time=localtime(&time_sec);
	printf("%d/%d/%d -- %d:%d:%d 星期:%d\n",Time->tm_year+1900,
											Time->tm_mon+1,
											Time->tm_mday,
											Time->tm_hour,
											Time->tm_min,
											Time->tm_sec,
											Time->tm_wday);
	printf("本年中的第%d 天\n",Time->tm_yday);
	return 0;
}


3.4 时间结构体转秒单位 mktime



函数原型: time_t mktime(struct tm _tm);
形 参: struct tm _tm —时间结构体
返 回 值: 转换的总秒数(从 1970 年 1 月 1 日 0 时 0 分 0 秒)


  • 示例
#include <stdio.h>
#include <time.h>
int main(int argc,char **argv)
{
	struct tm Time;
	time_t time_sec;
	time_sec=time(NULL);
	printf("总秒数: %ld\n",time_sec);
	localtime_r(&time_sec,&Time);
	printf("%d/%d/%d -- %d:%d:%d 星期:%d\n",Time.tm_year+1900,
	Time.tm_mon+1,
	Time.tm_mday,
	Time.tm_hour,
	Time.tm_min,
	Time.tm_sec,
	Time.tm_wday);
	printf("本年中的第%d 天\n",Time.tm_yday);
	time_sec=mktime(&Time);
	printf("转换秒单位时间为: %ld\n",time_sec);
	return 0;
}
[wbyq@wbyq ~]$ ./a.out 
总秒数: 1673352223
2023/1/10 -- 20:3:43 星期:2
本年中的第9 天
转换秒单位时间为: 1673352223

3.5 时间结构体转字符串 asctime



函数原型: char _asctime(const struct tm _);
      char _asctime_r(const struct tm _restrict, char _restrict);
功 能: 将 struct tm 时间结构体转换字符串时间返回。
形 参: const struct tm _ —时间结构体,
    char _restrict —用户缓冲区, 保存转换完成的字符串时间。
返 回 值: 转换完成的字符串时间。
输出格式: Tue Jan 10 20:19:03 2023 —星期 月 日 时: 分: 秒 年


  • 示例
#include <stdio.h>
#include <time.h>
int main(int argc,char **argv)
{
	char buff[50];
	struct tm Time;
	time_t time_sec;
	time(&time_sec);
	printf("总秒数: %ld\n",time_sec);
	localtime_r(&time_sec,&Time);
	printf("%d/%d/%d -- %d:%d:%d 星期:%d\n",Time.tm_year+1900,
	Time.tm_mon+1,
	Time.tm_mday,
	Time.tm_hour,
	Time.tm_min,
	Time.tm_sec,
	Time.tm_wday);
	printf("本年中的第%d 天\n",Time.tm_yday);
	asctime_r(&Time,buff);
	printf("buff:%s\n",buff);
	return 0;
}
[wbyq@wbyq ~]$ ./a.out 
总秒数: 1673353143
2023/1/10 -- 20:19:3 星期:2
本年中的第9 天
buff:Tue Jan 10 20:19:03 2023



3.6 秒单位时间转格林威治时间 gmtime



函数原型: struct tm _gmtime(const time_t _);
      struct tm _gmtime_r(const time_t _restrict, struct tm _restrict);
函数功能: 秒单位时间转格林威治时间(GMT)
形参: const time_t _ – 秒单位时间
    struct tm _restrict —保存转换好的时间结构体


  • 示例
#include <stdio.h>
#include <time.h>
int main(int argc,char **argv)
{
	struct tm Time;
	time_t time_sec;
	time(&time_sec);
	printf("总秒数: %ld\n",time_sec);
	gmtime_r(&time_sec,&Time);
	printf("%d/%d/%d -- %d:%d:%d 星期:%d\n",Time.tm_year+1900,
											Time.tm_mon+1,
											Time.tm_mday,
											Time.tm_hour,
											Time.tm_min,
											Time.tm_sec,
											Time.tm_wday);
	printf("本年中的第%d 天\n",Time.tm_yday);
	return 0;
}
[wbyq@wbyq ~]$ ./a.out 
总秒数: 1673353526
2023/1/10 -- 12:25:26 星期:2
本年中的第9 天


3.7 格式化写入 strptime



函数原型: char _strptime(const char _s, const char _format, struct tm _tm);
函数功能: 将字符串 s 的时间内容写入到结构体 tm 中
形 参: s – 要写入的实际字符串
      format —要写入的格式, 长用格式如下:
      %Y —年
      %m —月 (1~12)
      %d —日 (1~31)
      %H —小时(24 小时制)
      %M —分钟
      %S —秒
      tm —保存转换后的时间结构体


 
  • 示例
#define _XOPEN_SOURCE
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
int main(int argc,char **argv)
{
	struct tm time_tm;
	char time_buff[100];
	/*格式化写入函数*/
	strptime("2023/01/09 -- 16:25:56","%Y/%m/%d -- %H:%M:%S",&time_tm);
	/*格式化转换*/
	strftime(time_buff,sizeof(time_buff),"%Y/%m/%d -- %H:%M:%S week:%w 第%j 天",&time_tm);
	printf("time_buff:%s\n",time_buff);
}
[wbyq@wbyq ~]$ ./a.out 
time_buff:2023/01/09 -- 16:25:56 week:1 第009 天



3.8 格式化输出 strftime



函数原型: size_t strftime(char _s, size_t max, const char _format,const struct tm _tm);
函数功能: 将结构体 tm 的内容格式化输出到地址 s 中
形 参: s – 保存转换后的字符串
     max —buff 的空间大小
     format —要输出的格式(同 strptime)


  • 示例
#include <stdio.h>
#include <time.h>
int main(int argc,char **argv)
{
	time_t sec;
	time(&sec);//获取系统的秒单位时间
	struct tm result;
	localtime_r(&sec,&result);//将秒单位时间转换为时间结构体
	char time_str[200];
	strftime(time_str,sizeof(time_str),"%Y/%m/%d --%H:%M:%S 周:%u 第%j 天",&result);
	printf("time:%s\n",time_str);
}
[wbyq@wbyq ~]$ ./a.out 
time:2023/01/10 --20:30:24 周:2 第010 天



3.9 Linux 下获取系统高精度时间



//获取系统的高精度时间
#include <sys/time.h>
int gettimeofday(struct timeval _tv, struct timezone _tz);//获取时间
int settimeofday(const struct timeval _tv, const struct timezone _tz);//设置时间
形参: tv —时间结构体
    struct timeval {
       time_t tv_sec; // 秒时间
       suseconds_t tv_usec; // 微秒时间
       };
   struct timezone 结构体如下
   struct timezone{
    int tz_minuteswest;//格林威治时间往西方的时差
    int tz_dsttime; // DST 时间的修正方式
   };
    timezone 参数若不使用则传入 NULL 即可。
注意: 设置和获取的时间基准: 1970/01/01 0:0:0


  • 示例
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
int main(int argc,char **argv)
{
	struct timeval tv,tv2;
	gettimeofday(&tv, NULL);//获取时间
	sleep(1);//延时 1s
	gettimeofday(&tv2, NULL);//获取时间
	printf("运行时长:%ld s:%ld us\n",tv2.tv_sec-tv.tv_sec,tv2.tv_usec-tv.tv_usec);
}
[wbyq@wbyq ~]$ ./a.out 
运行时长:1 s:1094 us

3.10 Linux 下延时函数



#include <unistd.h>
unsigned int sleep(unsigned int seconds);//秒延时
int usleep(useconds_t usec);//微秒延时
#include <time.h>
int nanosleep(const struct timespec _req, struct timespec _rem);//纳秒延时
形参: req —要延时的时间
   struct timespec
   {
       time_t tv_sec; /_ seconds _/
       long tv_nsec; /_ nanoseconds _/
   };
   rem —若 nanosleep 提前终止, 则 rem 中保存剩余时长


  • 示例
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
int main(int argc,char **argv)
{
	struct timeval tv,tv2;
	gettimeofday(&tv, NULL);//获取时间
	sleep(1);//延时 1s
	gettimeofday(&tv2, NULL);//获取时间
	printf("运行时长:%ld s:%ld us\n",tv2.tv_sec-tv.tv_sec,tv2.tv_usec-tv.tv_usec);
}
[wbyq@wbyq ~]$ ./a.out 
运行时长:1 s:1094 us


3.11 Linux 下定时函数

#include <sys/time.h>

int getitimer(int which, struct itimerval _curr_value);//获取定时器时间
//设置定时器时间
int setitimer(int which, const struct itimerval _new_value,struct itimerval _old_value);
形参: which 定时器类型, 支持以下三种:
       ITIMER_REAL: 以系统真实的时间来计算, 它送出 SIGALRM 信号。
       ITIMER_VIRTUAL: 以该进程在用户态下花费的时间来计算, 它送出 SIGVTALRM 信号。
       ITIMER_PROF: 以该进程在用户态下和内核态下所费的时间来计算, 它送出 SIGPROF 信号。
      new_value —设置定时时间
      old_value —保存之前时间, 一般填 NULL 即可
            struct itimerval
            {
                  struct timeval it_interval; /_ 定时器周期间隔时间 _/
                  struct timeval it_value; /_定时器初始定时时间_/
            };
            struct timeval
            {
                  time_t tv_sec; /_ 秒时间 _/
                  suseconds_t tv_usec; /_微秒时间_/
            };
注意: 若只指定 it_value, 则只会定时一次;
    若同时指定 it_interval, 则时间到到达后会将 it_value 值重新初始化为 it_interval 设定的值, 以实现重复定时器;
    若将 it_interval、 it_value 都清空, 则定时器关闭;


  • 单次定时示例
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
struct timeval tv;
void sig_work(int sig)
{
	gettimeofday(&tv,NULL);
	printf("[%ld s:%ld us]定时时间到, 捕获到信号:%d\n",tv.tv_sec,tv.tv_usec,sig);
}
int main(int argc,char **argv)
{
	signal(SIGALRM,sig_work);
	struct itimerval new_value=
	{
		.it_value=//初始定时时间
		{
			.tv_sec=1,
			.tv_usec=0
		}
	};
	gettimeofday(&tv,NULL);
	printf("[%ld s:%ld us]开启定时器\n",tv.tv_sec,tv.tv_usec);
	setitimer(ITIMER_REAL,&new_value,NULL);
	while(1)
	{
		
	}
}

  • 重复定时示例
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
struct timeval tv;
void sig_work(int sig)
{
	gettimeofday(&tv,NULL);
	printf("[%ld s:%ld us]定时时间到, 捕获到信号:%d\n",tv.tv_sec,tv.tv_usec,sig);
}
int main(int argc,char **argv)
{
	signal(SIGALRM,sig_work);
	struct itimerval new_value=
	{
		.it_interval=//重复定时时间
		{
			.tv_sec=2,
			.tv_usec=0
		},
		.it_value=//初始定时时间
		{
			.tv_sec=1,
			.tv_usec=0
		}
	};
	gettimeofday(&tv,NULL);
	printf("[%ld s:%ld us]开启定时器\n",tv.tv_sec,tv.tv_usec);
	setitimer(ITIMER_REAL,&new_value,NULL);
	while(1)
	{
		
	}
}