实验:gmtime函数
下面这个程序gmtime.c利用tm结构和gmtime函数打印出当前时间和日期:


实验解析
程序调用time函数得到底层的时间值,然后调用 gmtime将该值转换为一个包含更有用的时间和日期值的结构。程序用printf将这些信息打印出来。严格来说,不应该用这种方法打印原始时间值,因为 我们并不能保证它在所有系统上都是long类型的值。我们在gmtime程序后立即运行date命令,以比较它们的输出。
不过,这儿有个小问题。如果你在格林尼治标准时间 (GMT)之外的时区运行这个程序,或者如果你所在的地方采用了夏令时,你会发现时间(可能还有日期)是不对的。因为gmtime按GMT返回时间(现在 GMT被称为世界标准时间,或UTC)。Linux和UNIX这样做是为了同步全球各地的所有程序和系统。不同时区同一时刻创建的文件就会有相同的创建时 间。要看当地时间,我们需要使用localtime函数。
struct tm *localtime(const time_t timeval);
localtime函数和gmtime一样,除了它返回的结构中包含的值已根据当地时区和是否采用夏令时做了调整。如果你把上面程序中gmtime换成localtime再编译运行一次,你就会看到正确的时间和日期了。
要把已分解出来的tm结构再转换为原始的time_t时间值,可以使用mktime函数:
time_t mktime(struct tm *timeptr);
如果tm结构不能表示为time_t值,mktime将返回-1。
为了得到更“友好”的时间和日期表示,像date命令输出的那样,我们可以使用asctime函数和ctime函数:
char *asctime(const struct tm *timeptr);
char *ctime(const time_t timeval);
asctime函数返回一个字符串,表示由tm结构timeptr所给出的时间和日期。这个返回的字符串有类似下面的格式:
Sun Jun 6 12:30:34/n/0
它总是这种长度为26个字符的固定格式。ctime函数等效于调用
asctime(localtime(timeval));
它以原始时间值为参数,并将它转换为可读的本地时间。
实验:ctime函数
我们用下面的代码来看看ctime函数的用法。



实验解析
ctime.c程序调用time函数得到底层时间值,让ctime做所有的艰巨工作,把时间值转换成可读的字符串,然后打印它。
为了对时间和日期字符串的格式有更多控制,Linux和现代的类UNIX系统提供了strftime函数。它很像是一个针对时间和日期的sprintf函数,工作方式也很类似:
size_t strftime(char *s,size_t maxsize,const char *format,struct tm *timeptr);
strftime函数格式化timeptr指针指向 的tm结构所表示的时间和日期,并将结果放在字符串s中。字符串被指定(至少)maxsize个字符。format字符串用于控制写入字符串s的字符。与 printf一样,它包含将被传给字符串的普通字符和用于格式化时间和日期元素的转换控制符。转换控制符见下表。
转换控制符 | 说 明 | 转换控制符 | 说 明 |
%a | 星期几的缩写 | %S | 秒,00~61 |
%A | 星期几的全称 | %u | 星期几,1~7(周一为1) |
%b | 月份的缩写 | %U | 一年中的第几周,01~53(周日是一周的第一天) |
%B | 月份的全称 | %V | 一年中的第几周,01~53(周一是一周的第一天) |
%c | 日期和时间 | %w | 星期几,0~6(周日为0) |
%d | 月份中的日期,01~31 | %x | 本地格式的日期 |
%H | 小时,00~23 | %X | 本地格式的时间 |
%I | 12进制的小时,01~12 | %y | 年份减去1900 |
%j | 年份中的日期,001~366 | %Y | 年份 |
%m | 年份中的月份,01~12 | %Z | 时区名 |
%M | 分钟,00~59 | %% | 字符% |
%p | a.m.(上午)或p.m(下午) |
因此,date命令输出的普通日期就相当于strftime格式字符串中的:
"%a %b %d %H:%M:%S %Y"
为了帮助读取日期,我们可以使用strptime函数,该函数以一个代表日期和时间的字符串为参数,并创建表示同一日期和时间的tm结构:
char *strptime(const char *buff,const char *format,struct tm *timeptr);
format字符串的构建方式和strftime的 format字符串完全一样。strptime在字符串扫描方面类似于sscanf函数,也是查找匹配数据字段,并把它们写入对应的变量中。只是这里是根 据format字符串填充tm结构的成员。不过,strptime的转换控制符与strftime的相比,限制要稍微松一些,因为strptime中的星 期几和月份用缩写和全称都行,两者都匹配strptime中的%a控制符,此外,strftime使用小于10的数字总以0开头,而strptime则把 它看作是可选的。
strptime返回一个指针,指向转换过程处理的最后一个字符后面的那个字符。如果碰到不能转换的字符,转换过程就在该点停下来。调用程序需要检查是否已从传递的字符串中读入了足够多的内容,以确保tm结构中写入了有意义的值。
实验:strftime函数和strptime函数
请留意下面这个程序中选用的转换控制符:

strftime程序通过调用time和 localtime得到当前的本地时间。然后,它通过调用带有合适的格式参数的strftime将时间转换成可读的格式。为演示strptime的用法, 程序构建了一个包含日期和时间的字符串,然后调用strptime将原始时间和日期值分解并打印出来。转换控制符%R是strptime中对%H:%M的 缩写形式。
注意:要成功地扫描日期,strptime需要一个精确的格式字符串。这一点非常重要。一般来说,该函数不会精确扫描读自用户的日期,除非用户输入的格式非常严格。
编译strftime.c时,你可能会看到编译器有一个警告信息。因为GNU库在默认情况下并未声明strptime函数。要解决这个问题,在包含time.h头文件的语句之前加上如下一行即可,它的作用是明确请求使用X/Open标准的功能:
#define _XOPEN_SOURCE