Linux中的日历时间
- time函数和gettimeofday系统调用
早期UNIX上是实现了time的系统调用的,在4.3BSD上补充了更为精确的gettimeofday系统调用(虽然这个函数名字感觉很奇怪),但是这个调用可以提供微秒级别的精度。所以将time作为系统调用就显得有点多余,目前的做法是将time封装到C语言的time.h中,实现为一个调用了gettimeofday的库函数。
测试代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/time.h> #include <time.h> int main() { time_t tt; struct timeval tv; printf("Time Ret: %ld\n",time(NULL)); printf("Time Ret: %ld\n",time(&tt)); printf("Time Arg: %ld\n",tt); int ret=gettimeofday(&tv,NULL); printf("tv.tv_sec: %ld\n",tv.tv_sec); printf("tv.tv_usec: %ld\n",tv.tv_usec); return 0; }
运行效果见下图:
这里要说明几个注意点:
1. time函数,返回自Epoch以来的秒数,这是肯定的,同时可以接受一个time_t*类型的指针作为形参,执行该函数将会把这个秒数放入到指针所指向的位置 ,如果为NULL当然不用管了;
2. time函数和gettimeofday系统调用返回的结果是相同的,都是秒数,但是后者还可以返回微秒数,这两个值都是通过结构体timeval传递出来的,该结构体定义如下:
/* A time value that is accurate to the nearest microsecond but also has a range of years. */ struct timeval { __time_t tv_sec; /* Seconds. */ __suseconds_t tv_usec; /* Microseconds. */ };
3. 有gettimeofday,同样也有对应的settimeofday函数。
有时候我们需要打印程序运行的时间,可以简单写一个函数:
void print_now() { struct timeval tv; int ret=gettimeofday(&tv,NULL); printf("[%ld.%ld]\t",tv.tv_sec,tv.tv_usec); }
- 时间转换
有几种时间转换函数,最简单的是ctime函数,直接给返回一个长达26字节的字符串,包含了标准格式的日期和时间,测试代码如下:
int main() { time_t tt; tt=time(NULL); printf("%s\n",ctime(&tt)); return 0; }
运行效果如下,下方是作为对比的date命令。
- 时间的分解和合成
有时候我们并不需要这么多的时间信息,例如我只想检查一下现在时间是否介于晚上7点到早上6点之间,然后决定是否打开路灯这样的工作,显然这个就需要提取时间中的各个不同要素。
首先是结构体tm(感觉早期写程序语言的人起名字好随意啊~):
/* ISO C `broken-down time' structure. */ struct tm { int tm_sec; /* Seconds. [0-60] (1 leap second) */ int tm_min; /* Minutes. [0-59] */ int tm_hour; /* Hours. [0-23] */ int tm_mday; /* Day. [1-31] */ int tm_mon; /* Month. [0-11] */ int tm_year; /* Year - 1900. */ int tm_wday; /* Day of week. [0-6] */ int tm_yday; /* Days in year.[0-365] */ int tm_isdst; /* DST. [-1/0/1]*/ # ifdef __USE_MISC long int tm_gmtoff; /* Seconds east of UTC. */ const char *tm_zone; /* Timezone abbreviation. */ # else long int __tm_gmtoff; /* Seconds east of UTC. */ const char *__tm_zone; /* Timezone abbreviation. */ # endif };
可以看到这个结构体中包含了最基本的年月日、时分秒、星期,还有DST(夏令时)等,另外还受到宏的影响,可能有其他的一些信息。更有意思的是,秒并不是从0~59,而是从0~60,这是为了考虑闰秒的问题。另外,年并不是直接是年,而是有个1900的偏移量,所以如果今年是2022年的话,这个字段得到的结果是122。
测试代码如下:
int main() { time_t tt; struct tm* ptm; tt=time(NULL); printf("%s",ctime(&tt)); ptm=gmtime(&tt); printf("Hour: %d\n",ptm->tm_hour); return 0; }
运行效果如下:
等等,怎么上面显示的是23点,下面居然是7点?
这个地方存在GMT和LocalTime的问题。首先我们是东八区,比GMT快了8小时,所以时间上是23(周五晚上)+8=7(周六上午)。
GMT可以作为基准,这是很不错的,但是通常大家都是使用的本地时间,这个还有一个和gmtime类似的函数:localtime,把上面的测试代码中gmtime函数名改为localtime就可以得到23了。
在介绍完了tm结构体以后,再引入另一个时间格式化函数asctime,这个函数接收一个tm的结构体,然后输出时间格式的字符串,与ctime类似,但是与ctime函数不同的是,它不会受到本地时间的影响。
事实上,ctime函数的原型是这么注释的:
/* Equivalent to `asctime (localtime (timer))'. */ extern char *ctime (const time_t *__timer) ;
可见时间上是将GMT时间转化为了本地时间以后输出。
时间的合成是时间的分解的逆向操作,所使用的函数是mktime,可以猜到,这个函数的参数是一个tm的结构体(或指针),返回值是time_t类型,函数原型如下:
/* Return the `time_t' representation of TP and normalize TP. */ extern time_t mktime (struct tm *__tp) ;