linux时间函数介绍

1. linux文件的时间
linux文件的时间分为三种,访问时间(access time)修改时间(modify time)和创造时间(create time)。其中:
文件的 Access time,atime 是在读取文件或者执行文件时更改的。
文件的 Modified time,mtime 是在写入文件时随文件内容的更改而更改的
文件的 Create time,ctime 是在写入文件、更改所有者、权限或链接设置时随Inode 的内容更改而更改的。
因此,更改文件的内容即会更改 mtime ctime ,但是文件的 ctime 可能会在 mtime 未发生任何变化时更改 - 在权限更改,但是文件内容没有变化的情况下。
ls(1) 命令可用来列出文件的 atime ctime mtime
ls -lc filename 列出文件的 ctime
ls -lu filename 列出文件的 atime
ls -l filename 列出文件的 mtime
2. linux 时间函数编程
通过学习许多 C/C++ 库,你可以有很多操作、使用时间的方法。但在这之前你需要了解一些 时间 日期 的概念,主要有以下几个:
Coordinated Universal Time UTC ):协调世界时,又称为世界标准时间,也就是大家所熟知的格林威治标准时间( Greenwich Mean Time GMT )。比如,中国内地的时间与 UTC 的时差为 +8 ,也就是 UTC+8 。美国是 UTC-5  
Calendar Time :日历时间,是用 从一个标准时间点到此时的时间经过的秒数 来表示的时间。这个标准时间点对不同的编译器来说会有所不同,但对一个编译系统来 说,这个标准时间点是不变的,该编译系统中的时间对应的日历时间都通过该标准时间点来衡量,所以可以说日历时间是 相对时间 ,但是无论你在哪一个时区, 在同一时刻对同一个标准时间点来说,日历时间都是一样的。  
epoch :时间点。时间点在标准 C/C++ 中是一个整数,它用此时的时间和标准时间点相差的秒数(即日历时间)来表示。  
clock tick :时钟计时单元(而不把它叫做时钟滴答次数),一个时钟计时单元的时间长短是由 CPU 控制的。一个 clock tick 不是 CPU 的一个时钟周期,而是 C/C++ 的一个基本计时单位。

2.1 相关函数和结构

 

#include <time.h>
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);
time_t mktime(struct tm *tm);
 
struct timeval { 
     int tv_sec; 
     int tv_usec; 
}; 
其中tv_sec是由凌晨开始算起的秒数,tv_usec则是微秒(10E-6 second)。 
 
struct timezone { 
     int tv_minuteswest; 
     int tv_dsttime; 
}; 
tv_minuteswest是格林威治时间往西方的时差,tv_dsttime则是时间的修正方式。 
 
struct timespec 
     long int tv_sec; 
     long int tv_nsec; 
}; 
tv_nsec是nano second(10E-9 second)。 
 
struct tm 
     int tm_sec; 
     int tm_min; 
     int tm_hour; 
     int tm_mday; 
     int tm_mon; 
     int tm_year; 
     int tm_wday; 
     int tm_yday; 
     int tm_isdst; 
}; 
tm_sec表「秒」数,在[0,61]之间,多出来的两秒是用来处理跳秒问题用的。 
tm_min表「分」数,在[0,59]之间。 
tm_hour表「时」数,在[0,23]之间。 
tm_mday表「本月第几日」,在[1,31]之间。 
tm_mon表「本年第几月」,在[0,11]之间。 
tm_year要加1900表示那一年。 
tm_wday表「本第几日」,在[0,6]之间。 
tm_yday表「本年第几日」,在[0,365]之间,闰年有366日。 
tm_isdst表是否为「日光节约时间」。 
 
struct    itimerval {
   struct   timeval it_interval;
   struct   timeval it_value;
};
it_interval成员表示间隔计数器的初始值,而it_value成员表示间隔计数器的当前值。#ifndef _TIME_T_DEFINED 
typedef long time_t; /* 时间值 */ 
#define _TIME_T_DEFINED /* 避免重复定义 time_t */ 
#endif
其中 time() 函数来获得日历时间( Calendar Time )。
gmtime()
localtime() ,用于获取日历时间,也就是我们平时所说的年、月、日、时、分、秒等信息,这些信息保存在一个名为 tm 的结构体中。其中 gmtime() 函数是将日历时间转化为世界标准时间(即格林尼治时间),并返回一个 tm 结构体来保存这个时间,而 localtime() 函数是将日历 时间转化为本地时间。比如现在用 gmtime() 函数获得的世界标准时间是 2005 7 30 7 18 20 ,那么我用 localtime() 函数在 中国地区获得的本地时间会比世界标准时间晚 8 个小时,即 2005 7 30 15 18 20 。下面是个例子:

 

#include <time.h> 
#include "stdio.h" 
int main(void) 
struct tm *local; 
time_t t; 
t=time(NULL); 
local=localtime(&t); 
printf("Local hour is: %d/n",local->tm_hour); 
local=gmtime(&t); 
printf("UTC hour is: %d/n",local->tm_hour); 
return 0; 
运行结果是:
Local hour is: 15 
UTC hour is: 7
 asctime() 函数和 ctime() 函数将时间以固定的格式显示出来,两者的返回值都是 char* 型的字符串。返回的时间格式为:
星期几 月份 日期 : : /n/0 
例如: Wed Jan 02 02:03:55 1980/n/0
其中 /n 是一个换行符, /0 是一个空字符,表示字符串结束。其 asctime() 函数是通过 tm 结构来生成具有固定格式的保存时间信息的字符串,而 ctime() 是通过日历时间来生成时间字符串。这样的话, asctime ()函数只是把 tm 结构对象中的各个域填到时间字符串的相应位置就行了,而 ctime ()函数需要先参照本地的时间设置,把日历时间转化为 本地时间,然后再生成格式化后的字符串。在下面,如果 t 是一个非空的 time_t 变量的话,那么: printf(ctime(&t)); 等价于:
struct tm *ptr; 
ptr=localtime(&t); 
printf(asctime(ptr));
那么,下面这个程序的两条 printf 语句输出的结果就是不同的了(除非你将本地时区设为世界标准时间所在的时区):

 

#include <time.h> 
#include "stdio.h" 
int main(void) 
struct tm *ptr; 
time_t lt; 
lt =time(NULL); 
ptr=gmtime(<); 
printf(asctime(ptr)); 
printf(ctime(<)); 
return 0; 
 
运行结果: Sat Jul 30 08:43:03 2005 
Sat Jul 30 16:43:03 2005 
mktime ()函数将用 tm 结构表示的时间转化为日历时间。其函数原型如下 其返回值就是转化后的日历时间。这样我们就可以先制定一个分解时间,然后对这个时间进行操作了,下面的例子可以计算出 1997 7 1 是星期几:

 

#include <time.h> 
#include "stdio.h" 
#include "stdlib.h" 
int main(void) 
struct tm t; 
time_t t_of_day; 
t.tm_year=1997-1900; 
t.tm_mon=6; 
t.tm_mday=1; 
t.tm_hour=0; 
t.tm_min=0; 
t.tm_sec=1; 
t.tm_isdst=0; 
t_of_day=mktime(&t); 
printf(ctime(&t_of_day)); 
return 0; 
运行结果:  
Tue Jul 01 00:00:01 1997 
  现在注意了,有了 mktime() 函数,是不是我们可以操作现在之前的任何时间呢?你可以通过这种办法算出 1945 8 15 号是星期几吗?答案是否定的。因为这个时间在 1970 1 1 之前,所以在大多数编译器中,这样的程序虽然可以编译通过,但运行时会异常终止。
2.2自定义时间格式
我们可以使用 strftime ()函数将时间格式化为我们想要的格式。它的原型如下: size_t strftime( 
char *strDest, 
size_t maxsize, 
const char *format, 
const struct tm *timeptr 
);
我们可以根据 format 指向字符串中格式命令把 timeptr 中保存的时间信息放在 strDest 指向的字符串中,最多向 strDest 中存放 maxsize 个字符。该函数返回向 strDest 指向的字符串中放置的字符数。函 strftime() 的操作有些类似于 sprintf() :识别以百分号 (%) 开始的格式命令集合,格式化输出结果放在一个字符串中。格式化命令说明串 strDest 中各种日期和时间信息的确切表示方法。格式串中的其他字符原样放进串中。格式命令列在下面,它们是区分大小写的。
%a
星期几的简写  
%A 星期几的全称  
%b 月分的简写  
%B 月份的全称  
%c 标准的日期的时间串  
%C 年份的后两位数字  
%d 十进制表示的每月的第几天  
%D / /  
%e 在两字符域中,十进制表示的每月的第几天  
%F - -  
%g 年份的后两位数字,使用基于周的年  
%G 年分,使用基于周的年  
%h 简写的月份名  
%H 24 小时制的小时  
%I 12 小时制的小时  
%j 十进制表示的每年的第几天  
%m 十进制表示的月份  
%M 十时制表示的分钟数  
%n 新行符  
%p 本地的 AM PM 的等价显示  
%r 12 小时的时间  
%R 显示小时和分钟: hh:mm 
%S 十进制的秒数  
%t 水平制表符  
%T 显示时分秒: hh:mm:ss 
%u 每周的第几天,星期一为第一天 (值从 0 6 ,星期一为 0  
%U 第年的第几周,把星期日做为第一天(值从 0 53  
%V 每年的第几周,使用基于周的年  
%w 十进制表示的星期几(值从 0 6 ,星期天为 0  
%W 每年的第几周,把星期一做为第一天(值从 0 53  
%x 标准的日期串  
%X 标准的时间串  
%y 不带世纪的十进制年份(值从 0 99  
%Y 带世纪部分的十进制年份  
%z %Z 时区名称,如果不能得到时区名称则返回空字符。  
%% 百分号  
 
如果想显示现在是几点了,并以 12 小时制显示,就象下面这段程序:  

 

 
#include <time.h> 
#include “stdio.h” 
int main(void) 
struct tm *ptr; 
time_t lt; 
char str[80]; 
lt=time(NULL); 
ptr=localtime(<); 
strftime(str,100,"It is now %I %p",ptr); 
printf(str); 
return 0; 
 
其运行结果为:  
It is now 4PM 
 
而下面的程序则显示当前的完整日期:  
 
#include <stdio.h> 
#include <time.h> 
 
void main( void ) 
struct tm *newtime; 
char tmpbuf[128]; 
time_t lt1; 
time( &lt1 ); 
newtime=localtime(&lt1); 
strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y./n", newtime); 
printf(tmpbuf); 
 
运行结果:  
 
Today is Saturday, day 30 of July in the year 2005.
 
2.3 计算持续时间的长度 
有时候在实际应用中要计算一个事件持续的时间长度,比如计算打字速度。在第 1 节计时部分中,我已经用 clock 函数举了一个例子。 Clock() 函数可以精确到毫秒级。同时,我们也可以使用 difftime() 函数,但它只能精确到秒。该函数的定义如下:
double difftime(time_t time1, time_t time0);
虽然该函数返回的以秒计算的时间间隔是 double 类型的,但这并不说明该时间具有同 double 一样的精确度,这是由它的参数觉得的( time_t 是以秒为单位计算的)。比如下面一段程序:

 

#include <time.h> 
#include "stdio.h" 
#include "stdlib.h" 
int main(void) 
time_t start,end; 
start = time(NULL); 
system("pause"); 
end = time(NULL); 
printf("The pause used %f seconds./n",difftime(end,start));//<- 
 //system("pause"); 
return 0; 
 
运行结果为:  
请按任意键继续 . . . 
The pause used 2.000000 seconds. 
请按任意键继续 . . . 
 
可以想像,暂停的时间并不那么巧是整整 2 秒钟。其实,你将上面程序的带有“ //<- ”注释的一行用下面的一行代码替换:  
 
printf("The pause used %f seconds./n",end-start); 
 
其运行结果是一样的。
 3、延时函数
延时可以采用如下函数:
unsigned int sleep(unsigned int seconds); 
sleep()会使目前程式陷入「冬眠」seconds秒,除非收到「不可抵」的信号。 
如果sleep()没睡饱,它将会返回还需要补眠的时间,否则一般返回零。 
 
void usleep(unsigned long usec); 
usleep与sleep()类同,不同之处在於秒的单位为10E-6秒。 
 
int select(0,NULL,NULL,NULL,struct timeval *tv); 
可以利用select的实作sleep()的功能,它将不会等待任何事件发生。 
 
int nanosleep(struct timespec *req,struct timespec *rem); 
nanosleep会沉睡req所指定的时间,若rem为non-null,而且没睡饱,将会把要补眠的时间放在rem上。 
 
 
4、定时器
4.1、alarm
如果不要求很精确的话,用 alarm() 和 signal() 就够了
     unsigned int alarm(unsigned int seconds)
     专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号, 又称为闹钟时间。进程调用alarm后,任何以前的alarm()调用都将无效。如果参数seconds为零,那么进程内将不再包含任何闹钟时间。如果调 用alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。
 
         示例:

 

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sigalrm_fn(int sig)
{
         /* Do something */
         printf("alarm!/n");
         alarm(2);
         return;
}
 
int main(void)
{
         signal(SIGALRM, sigalrm_fn);
         alarm(2);
 
         /* Do someting */
         while(1) pause();
}
4.2、setitimer
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));
setitimer()比alarm功能强大,支持3种类型的定时器:
 
ITIMER_REAL :    以系统真实的时间来计算,它送出SIGALRM信号。 
ITIMER_VIRTUAL :    以该行程真正有执行的时间来计算,它送出SIGVTALRM信号。 
ITIMER_PROF :    以行程真正有执行及在核心中所费的时间来计算,它送出SIGPROF信号。 
Setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例;第三个参数可不做处理。
Setitimer()调用成功返回0,否则返回-1。
下面是关于setitimer调用的一个简单示范,在该例子中,每隔一秒发出一个SIGALRM,每隔0.5秒发出一个SIGVTALRM信号::

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
 
int sec;
void sigroutine(int signo){
 
     switch (signo){
     case SIGALRM:
         printf("Catch a signal -- SIGALRM /n");
         signal(SIGALRM, sigroutine);
         break;
     case SIGVTALRM:
         printf("Catch a signal -- SIGVTALRM /n");
         signal(SIGVTALRM, sigroutine);
         break;
     }
     return;
}
 
int main()
{
     struct itimerval value, ovalue, value2;
   
     sec = 5;
     printf("process id is %d ", getpid());
     signal(SIGALRM, sigroutine);
      signal(SIGVTALRM, sigroutine);
     value.it_value.tv_sec = 1;
     value.it_value.tv_usec = 0;
     value.it_interval.tv_sec = 1;
     value.it_interval.tv_usec = 0;
     setitimer(ITIMER_REAL, &value, &ovalue);
 
     value2.it_value.tv_sec = 0;
     value2.it_value.tv_usec = 500000;
     value2.it_interval.tv_sec = 0;
     value2.it_interval.tv_usec = 500000;
     setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
     for(;;)
         ;
}
输出:
localhost:~$ ./timer_test
process id is 579
Catch a signal – SIGVTALRM
Catch a signal – SIGALRM
Catch a signal – SIGVTALRM
Catch a signal – SIGVTALRM
Catch a signal – SIGALRM
Catch a signal –GVTALRM
         注意: Linux 信号机制基本上是从 Unix 系统中继承过来的。早期 Unix 系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,因此,把那些建立在早期机制上的信号叫做 " 不可靠信号 " ,信号值小于 SIGRTMIN(Red hat 7.2 中, SIGRTMIN=32 SIGRTMAX=63) 的信号都是不可靠信号。这就是 " 不可靠信号 " 的来源。它的主要问题是:进程每次处理信号后,就将对信号的响应设置为默认动作。在某些情况下,将导致对信号的错误处理;因此,用户如果不希望这样的操作,那么就要在信号处理函数结尾再一次调用 signal() ,重新安装该信号

你可能感兴趣的:(linux,struct,calendar,null,日历,Signal)