全部博文(842)
分类: 系统运维
2012-05-14 17:07:39
由UNIX内核提供的基本时间服务计算自从Epoch:统一全球时间(Coordinated Universal Time,
UTC)1970-1-1
00:00:00至今经过的秒数。在1.10节,我们说过这些秒数用time_t数据类型表示,而我们称它们为日历时间。这些日历时间同时表示时间和日
期。UNIX系统总是和其它操作系统有所区别:a、保留UTC时间而不是本地时间;b、自动完成转换,比如夏令时时间;c、把时间和日期作为单一量存储。
time函数返回当前的时间和日期。
这个时间值总是作为函数的值返回。如果参数是一个非空值,时间值也被存储在calptr指向的地点里。
我们没有说过内核如何初始化当前时间。历史上,在从System V继承下来的实现上,stime函数被调用,而基于BSD的系统使用settimeofday。
SUS没有指定系统如何设置它的当前时间。
gettimeofday函数提供了比time函数更好的精度(精确到微秒)。这对于一些应用程序很重要。
这个函数定义在SUS的XSI扩展里。tzp的唯一合法值是NULL。其它值会导致未指定的行为。一些平台通过使用tzp支持一个时区的指定,然而这是系统指定的而不是SUS定义的。
gettimeofday函数把以Epoch衡量的当前时间存储在由tp指向的内存里。这个时间被表示为一个timeval结构里,它存储秒数和微秒数:
struct timeval {
time_t tv_sec; /* seconds */
long tv_usec; /* microseconds */
}:
一但我们有了这个计算从Epoch至今的秒数的整型值,我们可以调用其它函数来把它转换成人可读的时间和日期。
localtime和gmtime这两个函数把日历时间转换了一个被称为分解时间(broken-down time)的一个结构体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; /* daylight saving time flag: <0, 0, >0 */
};
秒数能大于59的原因是为了允许闰秒(leap second)。注意除了月份日之外的所有域都是基于0的。夏令时标志为正表示夏令时有效,0表示无效,负值表示些信息不可用。
在SUS的前一个版本,双闰秒(double leap seconds)被允许。这样,tm_sec成员的合法范围是0-61。UTC的格式定义不允许双闰秒,所以秒的合法值现在被定义为0-60。
localtime和gmtime的区别在于第一个把日历时间转换为本地时间,根据时区和夏令时标志,而后者把日历时间转换成一个表示为UTC的分解时间。
函数mktime接受一个表示为本地时间的分解时间,并把它转换成一个time_t值。
asctime和ctime函数生产熟悉的26字节字符串,它与date命令的默认输出类似:
2012年 02月 27日 星期一 17:15:51 CST
asctime的参数是一个指向分解时间字符串的指针,而ctime的参数是一个指向日历时间的指针。
最后一个时间函数,strftime,是最复杂的。它是一个对于时间值类似于printf的函数。
最后的参数是格式所需的时间值,由一个分解时间值的指针指定。格式化的结构存储在尺寸为maxsize的buf数组里。如果包括终止null的结果的尺寸,可以放入这个缓冲,那么函数返回在buf里存储的字符数,不包括终止null。否则,该函数返回0。
参数format控制这个时间值的格式化。和printf函数相似,转换指示符由一个百分号接着一个特殊的字符给定。格式化字符串的所有其它字符被拷贝到 输出中。一行内的两个百分号在输出中产生单个百分号。不像printf函数,每个指定的转换产生一个不同的固定大小的输出字符串--在格式化字符串里没有 域长度。下表描述了37个ISO C的转换指示符。该表的第三列是Linux下的strftime输出,对应时间日期为Tue Feb 10 18:27:38 EST 2004。
格式 | 描述 | 例子 |
%a | 缩写的星期名 | Tue |
%A | 完全星期名 | Tuesday |
%b | 缩写的月名 | Feb |
%B | 完全月名 | February |
%c | 日期与时间 | Tue Feb 10 18:27:38 2004 |
%C | 年份除以100: [00-99] | 20 |
%d | 月内日期:[01-31] | 10 |
%D | 日期[MM/DD/YY] | 02/10/04 |
%e | 月内日期(单个数字用空格开头)[1-31] | 10 |
%F | ISO 8601日期格式[YYYY-MM-DD] | 2004-02-10 |
%g | ISO 8601基于星期的年份的后两个数字[00-99] | 04 |
%G | ISO 8601基于星期的年份 | 2004 |
%h | 与%b相同 | Feb |
%H | 一天内的小时(24小时格式):[00-23] | 18 |
%I | 一天内的小时(12小时格式):[01-12] | 06 |
%j | 一年内的天数[001-366] | 041 |
%m | 月份:[01-12] | 02 |
%M | 分:[00-59] | 27 |
%n | 换行符 | |
%p | AM/PM | PM |
%r | 本地时间(12小时格式) | 06:27:38 PM |
%R | 和“%H:%M”相同 | 18:27 |
%S | 秒:[00-60] | 38 |
%t | 水平制表符 | |
%T | 和“%H:%M:%S”相同 | 18:27:38 |
%u | ISO 8601星期[周一=1,1-7] | 2 |
%U | 周日星期数:[00-53] | 06 |
%V | ISO 8601星期数:[01-53] | 07 |
%w | 星期:[0=周天,0-6] | 2 |
%W | 周一星期数:[00-53] | 06 |
%x | 日期 | 02/10/04 |
%X | 时间 | 18:27:38 |
%y | 年份后两位:[00-99] | 04 |
%Y | 年份 | 2004 |
%z | UTC以ISO 8601格式的偏移量 | -500 |
%Z | 时间区 | EST |
%% | 翻译为一个百分号 | % |
不明朗的指示符只有%U、%V和%W。%U指示符表示年内的星期数,包含第一个星期天的星期被作为第一个星期。%W指示符表示年内的星期数,包含第一个周
一的是第一个星期。%V指示符不同。如果包含新年一月的第一天的星期有四天或更多天,这个星期被作为第一个星期。不然,它作为去年的最后一个星期。在两种
情况下,周一都作为星期的第一天。
和printf一样,strftime支持了一些转换指示符的修改符。E和O修改符可以用来产生替代格式,如果本地化被支持的话。
一些系统在strftime的格式化字符串时支持额外的,非标准的扩展。
localtime、mktime、ctime和strftime被TZ环境变量影响。如果定义的话,这个环境变量的值被这些函数使用代替默认的时区。如 果变量被定义为空字符串,比如TZ=,那么UTC通常被使用。这个环境变量的值通常是像TZ=EST5EDT的东西,但POSIX.1允许细节地多的指 定。参考SUS的环境变量那一样来得到TZ变量的所有细节。
本节描述的所有的时间和日期函数,除了gettimeofday,都定义在ISO C标准里。然而,POSIX.1加入了TZ环境变量。在FreeBSD 5.2.1、Linux 2.4.2和Mac OS X 10.3上,TZ变量的更多信息可以在tzset手册页上找到。在Solaris 9,这个信息在environ手册页里。