1. linux文件的时间
linux文件的时间分为三种,访问时间(access time)修改时间(modify time)和创造时间(create time)。其中:
文件的 Access time,atime 是在读取文件或者执行文件时更改的。
文件的 Modified time,mtime 是在写入文件时随文件内容的更改而更改的
文件的 Create time,ctime 是在写入文件、更改所有者、权限或链接设置时随Inode 的内容更改而更改的。
文件的 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 秒 。下面是个例子:
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 星期几的简写
%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( <1 );
newtime=localtime(<1);
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
|