日历时间
无论地理位置如何,UNIX系统内部对时间的表示方式均是以Epoch以来的秒数来度量的,Epoch亦即通用协调时间(UTC,以前也称为格林威治标准时间,或GMT)的1970年1月1日早晨零点。这也是UNIX系统问世的大致日期。日历时间存储于类型为time_t的表两种,此类型是由SUSv3定义的整数类型。
获取日历时间函数
gettimeofday
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
参数tv是指向如下数据结构的一个指针:
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
tv_sec为Epoch以来的秒数。虽然tv_usec字段能提供微秒级精度,但其返回值的准确性则由依赖于架构的具体实现来决定。参数tz是个历史产物。早期的UNIX实现用其来获取系统的时区信息,目前已遭废弃,应始终将其置为NULL。
time
#include <time.h>
time_t time(time_t *tloc);
time()系统调用返回自Epoch以来的秒数(和函数gettimeofday()所返回的tv参数中的tv_sec字段的数值相同)。如果tloc参数不为NULL,那么还会将Epoch以来的秒数置于tloc所指的位置。由于time()会以两种方式返回相同的值,而使用时唯一可能出错的地方是赋予tloc参数一个无效的地址(EFAULT),因此往往会简单地采用如下调用(不做错误检查):
t = time(NULL);
时间转换函数
ctime
#include <time.h>
char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);
把一个指向time_t的指针作为timep参数传入函数ctime(),将返回一个长达26字节的字符串,内含标准格式的日期和时间,如下例所示:
Wed Jun 8 14:22:34 2011
该字符串包含换行符和终止空字节各一。ctime()函数在进行转换时,会自动对本地时区和DST(夏令时)设置加以考虑。返回的字符串经由静态分配,下一次对ctime的调用会将其覆盖。
SUSv3规定,调用ctime()、gmtime()、localtime()或asctime()中的任一函数,都可能会覆盖由其他函数返回,且经静态分配的数据结构。换言之,这些函数可以共享返回的字符数组和tm结构体。如果有意在对这些函数的多次调用间维护返回的信息,那么必须将其保存在本地副本中。
ctime_r()是ctime()的可重入版本。该函数允许调用者额外指定一个指针参数,所指向的缓冲区用于返回时间字符串。其他函数的可重入版本,其操作方式与之类似。
gmtime和localtime
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);
函数gmtime()能够把日历时间转换为一个对应于UTC的分解时间。(字母GM源于格林威治标准时间)。相形之下,函数localtime()需要考虑时区和夏令时设置,返回对应于系统本地时间的一个分解时间。
gmtime_r()和localtime_r()分别是这些函数的可重入版本。
在这些函数所返回的tm结构中,日期和时间被分解为多个独立字段,其形式如下:
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
将字段tm_sec的上线设为60以考虑闰秒,偶尔会用其将人类日历调整至精确的天文年(所谓的回归年)。
mktime
time_t mktime(struct tm *tm);
函数mktime()将一个本地时区的分解时间翻译为time_t值,并将其作为函数结果返回。调用者将分解时间置于一个tm结构,再以tm指针指向该结构,这一转换会忽略输入tm结构的tm_wday和tm_yday字段。
函数mktime()可能会修改tm所指向的结构体,至少会确保对tm_wday和tm_yday字段值的设置,会与其他输入字段的值能对应起来。
asctime
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
在参数tm中提供一个指向分解时间结构的指针,asctime()则会返回一指针,指向经由静态分配的字符串,内含时间,格式则与ctime()相同。
ctime() = asctime(localtime())
strftime
#include <time.h>
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
当把一个分解时间转换成打印格式时,函数strftime()可以提供更为精确的控制。令tm指向分解时间,strftime()会将以null结尾、由日期和时间组成的相应字符串置于s所指向的缓冲区。s中返回的字符串按照format参数定义的格式做了格式化。max参数指定s的最大长度。不同于ctime()和asctime(),strftime()不会在字符串的结尾包括换行符(除非format中定义有换行符)。
如果成功,strftime()返回s所指缓冲区的字节长度,且不包括终止空字节。如果结果字符串的总长度,含终止空字节,超过了max参数,那么strftime()会返回0以示出错,且暂时无法确定s的内容。
strftime()的format参数是一字符串,与赋予printf()的参数类似。冠以百分号的字符序列是对转换的定义,函数会将百分号后的说明符字符一一替换为日期和时间的组成部分。这是一套相当丰富的转换说明符。
说明符 | 描述 | 例子 |
%% | 百分号(%)字符 | % |
%a | 星期几的缩写 | Tue |
%A | 星期几的全称 | Tuesday |
%b,%h | 月份名称的缩写 | Feb |
%B | 月份全称 | February |
%c | 日期和时间 | Tue Feb 1 21:39:46 2011 |
%d | 一个月的一天(2位数字,01至31) |
01 |
%D | 美国日期格式(与%m%d%y相同) | 02/01/11 |
%e | 一个月中的一天(2个字符) | 1 |
%F | ISO日期格式(与%Y-%m-%d相同) | 2011-02-01 |
%H | 小时(24小时制,2位数) | 21 |
%I | 小时(12小时制,2位数) | 09 |
%j | 一年中的一天(3位数字,从001到366) | 032 |
%m | 十进制月(2位,01到12) | 02 |
%M | 分(2位数) | 39 |
%p | AM/PM | PM |
%P | am/pm(GNU扩展) | pm |
%R | 24小时制的时间(和%H:%M格式相同)
| 21:39 |
%S | 秒(00至60) | 46 |
%T | 时间(和%H:%M:%S格式相同) | 21:39:46 |
%u | 星期几编号(1至7,星期一=1) | 2 |
%U | 以周日计算、一年中的周数(00到53) | 05 |
%w | 星期几编号(0至6,星期日=0) | 2 |
%W | 以周一计算,一年中的周数(00到53) | 05 |
%x | 日期(本地化) | 02/01/11 |
%X | 时间(本地化) | 21:39:46 |
%y | 2为数字年份 | 11 |
%Y | 4为数字年份 | 2011 |
%Z | 时区名称 | CET |
strptime
#define _XOPEN_SOURCE /* See feature_test_macros(7) */
#include <time.h>
char *strptime(const char *s, const char *format, struct tm *tm);
函数strptime()是strftime()的逆向函日期和时间的数,将包含时间和日期的字符串转换成一个分解时间。
函数strptime()按照参数format的格式要求,对由日期和时间组成的字符串s加以分析,并将转换后的分解时间置于tm所指向的结构体中。如果成功,strptime()返回一个指针,指向s中下一个未经处理的字符。如果无法匹配整个格式字符串,strptime()返回NULL。
转换说明类似于之前为strftime()给出的列表说明。主要的区别在于,此处的说明符更为通用。例如,不拘于星期名称的全称和简称,%a和%A都可接受,而且%d和%e均可用于读取月中的各位天数,无论该数字前面是否有0.此外,不区分大小写,例如,May和MA相同的月份名称。