6.10 时间和日期例程
UNIX内核提供的基本时间服务是计算处国际标准时间公元1970年1月1日00:00:00以来经过的秒数,它是用数据类型time_t表示的,我们称其为日历时间:包括日期和时间。Unix在这方面与其它操作系统的区别是(a)以国际标准时而非本地时间计时,(b)可自动进行转换,例如变换到夏日制,(c)将时间和日期作为一个量值保存。
time函数可以返回当前时间和日期:
#includetime_t time(time_t *calptr);
与time函数相比,gettimeofday提供了更高的分辨率(最高为微秒级):
#includeint gettimeofday(struct timeval *restrict tp, void *restrict tzp);
其中,结构timeval定义如下:
struct timeval{ time_t tv_sec; long tv_usec; /*microseconds*/ };
一旦获得这种以秒计的整型时间值后,通常要调用另一个时间函数将其转换为人们可读的时间和日期,下图说明了各个函数之间的关系:(下述函数中localtime、mktime、ctime和strftime都收到环境变量TZ的影响)
两个函数localtime和gmtime将日历时间转换成以年、月、日、时、分、秒、周日表示的时间,并将这些存放在一个tm结构,tm定义如下(注意每个成员的范围):
struct tm{ /*a broken-down time*/ int tm_sec; //seconds after the minute:[0-60] int tm_min; //minutes after the hour:[0-59] int tm_hour; /*hours after midnight:[0-23]*/ int tm_mday; /*day of the month[1-31]*/ int tm_mon; /*months since January[0-11]*/ int tm_year; /*years since 1900*/ int tm_wday; /*days since Sunday:[0-6]*/ int tm_yday; /*days since January 1:[0-365]*/ int tm_isdst; /*datlight saving time flag:<0,0,>0*/ };
秒可以超过59的理由是可以表示润秒。注意,除了月日字段,其它字段的值都以0开始。如果夏日制生效,则夏日制标志值为正,如果已非夏日制时间则为零,如果此信息不可用,则为负。
#includestruct tm *gmtime(const time*_t calptr) ; struct tm *localtime(const time_t calptr*) ;
两个函数返回:指向t m结构的指针
localtime和gmtime之间的区别是:localtime将日历时间变换成本地时间(考虑到本地时区和夏日制标志),而gmtime则将日历时间变换成国际标准时的年、月、日、时、分、秒、周日。
函数mktime以本地时间的年、月、日等作为参数,将其变换成time_t值。
#includetime_t mktime(struct tm *tmptr);//返回:若成功为日历时间,出错为-1
asctime和ctime函数产生下列形式的26字节字符串,这与date(1)命令的系统默认输出形式类似:
Tue Jan 14 17:49:03 1992\n\0
#includechar *asctime(const struct *tm tmptr) ; char *ctime(const time_t *calptr) ;
两个函数返回:指向n u l l结尾的字符串
asctime的参数是指向年、月、日等字符串的指针,而ctime的参数则是指向日历时间的指针。
最后一个时间函数是strftime,它是非常复杂的printf类的时间值函数。
#includesize_t strftime(char *buf,size_t maxsize,const char *format,const struct tm *tmptr); // 返回:若有空间为存入数组的字符数,否则为0
最后一个参数是要格式化的时间值,说明为指向一个年、月、日、时、分、秒、周日时间值的指针。格式化结果存放在一个长度为maxsize个字符的buf数组中,如果buf长度是以存放格式化结果及一个null络止符,则该函数返回在buf中存放的字符数(不包括null终止符)。否则该函数返回0。
format参数控制时间值的格式。如同printf函数一样,变换说明的形式是百分号之后跟一个特定字符。在format中的其它字符则按原择输出。两个连续的百分号在输出中产生一个百分号。与printf函数的不同之处是,每个变换说明产生一个定长输出字符串,在format字符串中没有宽字段修饰符,表中列出了37种ISO C规定的变换说明。
格式 | 说明 | 实例 |
%a | 缩写的星期名 | Thu |
%A | 全星期名 | Thursday |
%b | 缩写的月名 | Aug |
%B | 全月名 | August |
%c | 日期和时间 | Thu Aug 23 14:55:02 2001 |
%C | 年/100 [00,99] | 20 |
%d | 十进制表示的每月的第几天 [01,31] | 16 |
%D | 月/天/年 [MM/DD/YY] | 06/16/12 |
%e | 十进制表示的每月的第几天 ,一位数前加空格[1,31] | 10 |
%F | 年-月-日 [YYYY-MM-DD] | 2012-06-16 |
%g | ISO 8601使用基于周的年的后两位数字 | 12 |
%G | ISO 8601使用基于周的年 | 2012 |
%h | 简写的月份名 ,与%b相同 | Aug |
%H | 24小时制的小时 [00,23] | 14 |
%I | 12小时制的小时 [01,12] | 02 |
%j | 每年的第几天 [001,366] | 235 |
%m | 十进制的月 [01,12] | 08 |
%M | 分钟 [00,59] | 55 |
%n | 换行符 | |
%p | AM/PM | PM |
%r | 本地时间:(12时制) | 11:01:23 AM |
%R | 与“%H:%M”相同 | 11:01 |
%S | 秒 [00,60] | 02 |
%t | 水平制表符 | |
%T | 与“%H:%M:%S”相同 | 11:01:23 |
%u | ISO 8601的星期,星期一为1,[1,7] | 2 |
%U | 周数 ,以周日为一周开始 [00,53] | 33 |
%V | ISO 8601周数 [01,53] | 07 |
%w | 星期,星期天为0. [0,6] | 4 |
%W | 周数,以星期一为一周开始 [00,53] | 34 |
%x | 标准日期 | 06/16/12 |
%X | 标准时间 | 14:55:02 |
%y | 年份的后两位数字 [00,99] | 12 |
%Y | 年 | 2012 |
%z | ISO 8601格式的UTC偏移量 | -0500 |
%Z | 时区名 | EST |
%% | 百分号 | % |
图中的大多数格式说明的意义是很明显的。需要略作解释的是%U和%W。%U是相应日期在该年中所属周数,包含该年中第一个星期日的周是第一周。%W也是相应日期在该年中所属的周数,不同的是包含第一个星期一的周为第一周。而%V说明符则与上述两者有较大区别。若某周包含了1月1日,而且至少包含了其后的另外3天,那么改周是一年中的第一周,否则改周被认为是上一年的最后一周。在这两种情况下,周一都被视为每周的第一天。