一、日历时间(Calendar Time)
1. 系统调用 gettimeofday(),可于 tv 指向的缓冲区中返回日历时间。
#include <sys/time.h>
/*
*@tz:早期的 UNIX 实现用其来获取系统的时区信息,目前已遭废弃,应始终将其置为 NULL。
*/
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone *tz);
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
2. time()系统调用返回自 Epoch 以来的秒数(和函数 gettimeofday()所返回的 tv 参数中 tv_sec 字段的数值相同)。
#include <time.h>
//如果 timep 参数不为 NULL,那么还会将自 Epoch 以来的秒数置于tloc所指向的位置。
time_t time(time_t *tloc);
由于 time()会以两种方式返回相同的值,而使用时唯一可能出错的地方是赋予 timep 参数 一个无效地址(EFAULT),因此往往会简单地采用如下调用(不做错误检查):
t = time(NULL);
二、时间转换函数 1. 下图所示为用于在 time_t 值和其他时间格式之间相互转换的函数,其中包括打印输出。
2. time_t 转换为可打印格式
-
为了将 time_t 转换为可打印格式,ctime()函数提供了一个简单方法。
#include <time.h>
char *ctime(const time_t *timep);
3. ctime_r()是 ctime()的可重入版本。该函数允许调用者额外指定 一个指针参数,所指向的缓冲区(由调用者提供)用于返回时间字符串。本章所论及的其 他函数的可重入版,其操作方式与之类似
#include <time.h> //可重入的版本,指定输出的位置,而不是内核随机位置输出
char *ctime_r(const time_t *timep, char *buf);
4. time_t 和分解时间之间的转换
-
函数 gmtime()和 localtime()可将一 time_t 值转换为一个所谓的分解时间(broken-down time)。分解时间被置于一个经由静态分配的结构中,其地址则作为函数结果返回。
#include <time.h>
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);
struct tm *localtime_r(const time_t *timep, struct tm *result);
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 */
};
-
函数 gmtime()能够把日历时间转换为一个对应于 UTC 的分解时间。(字母 GM 源于格林 威治标准时间)。相形之下,函数 localtime()需要考虑时区和夏令时设置,返回对应于系统本 地时间的一个分解时间。
5. 函数 mktime() 将一个本地时区的分解时间翻译为 time_t 值,并将其作为函数结果返回。调用者将分解时间置于一个 tm 结构,再以 timeptr 指针指向该结构。这一转换会忽略输入 tm 结构中的 tm_wday 和 tm_yday 字段。
#include <time.h> time_t mktime(struct tm *tm);
-
mktime()在进行转换时会对时区进行设置。此外,DST 设置的使用与否取决于输入字段 tm_isdst 的值。
-
若 tm_isdst 为 0,则将这一时间视为标准间(即,忽略夏令时,即使实际上每年的这 一时刻处于夏令时阶段)。
-
若 tm_isdst 大于 0,则将这一时间视为夏令时(即,夏令时生效,即使每年的此时不 处于夏令时阶段)。
-
若 tm_isdst 小于 0,则试图判定 DTS 在每年的这一时间是否生效。这往往是众望所归 的设置。
三、分解时间和打印格式之间的转换
1. 从分解时间转换为打印格式 -asctime
#include <time.h>
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
2. 当把一个分解时间转换成打印格式时,函数 strftime()可以提供更为精确的控制
#include <time.h>
/*
*@s:返回的字符串按照 format 参数定义的格式做了格式化。
*@max:指定 outstr 的最大长度
*@format:是一字符串,与赋予 printf()的参数相类似。可见诸于strftime(3)手册页
*/
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
3. strftime()的转换说明符选集
说明符 | 描述 | 例子 |
%% | 百分号(%)字符 | % |
%a | 星期几的缩写 | Tuesday |
%b,%h | 月份名称的缩写 | Feb |
%B | 月份全称 | February |
%c | 日期和时间 | Tue Feb 1 |
%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 | 上午/下午(GNU 扩展) | pm |
%R | 24 小时制的时间(和%H:%M 格式相同) 2 | 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 |
4. 将打印格式时间转换为分解时间
-
函数 strptime()是 strftime()的逆向函数,将包含日期和时间的字符串转换成一分解时间
#define _XOPEN_SOURCE /* See feature_test_macros(7) */
#include <time.h>
char *strptime(const char *s, const char *format, struct tm *tm);
四、为程序指定时区 1. 为运行中的程序指定一个时区,需要将 TZ 环境变量设置为由一冒号(:)和时区名称组成的 字符串,其中时区名称定义于/usr/share/zoneinfo 中。设置时区会自动影响到函数 ctime()、 localtime()、mktime()和 strftime()
添加图片注释,不超过 140 字(可选)
五、地区(Locale) 1. 地区定义
-
地区信息维护于/usr/share/local(在一些发行版本中为/usr/lib/local)之下的目录层次结构 中。该目录下的每个子目录都包含一特定地区的信息。这些目录的命名约定如下:
添加图片注释,不超过 140 字(可选)
-
language 是双字母的 ISO 语言代码。
-
territory 是双字母的 ISO 国家代码。
-
codeset 表示字符 编码集。
-
modifier 则提供了一种方法,用以区分多个地区目录下 language、territory 和 codeset 均相同的状况。
de_DE.utf-8@euro |
2. 当在程序中指定要使用的地区时,实际上是指定了/usr /share/locale 下某个子目录的名称。
3. 如果程序指定地区不与任何子目录名称相匹配,那么 C 语言函数库将按如下顺序将各部分从 指定地区(locale)中剥离,以寻求匹配:
codeset normalized codeset territory modifier
五、更新系统时钟
1. 我们现在来看两个更新系统时钟的接口:settimeofday()和 adjtime()。这些接口都很少被应 用程序使用,因为系统时间通常是由工具软件维护,如网络时间协议(Network Time Protocol) 守护进程,并且它们需要调用者已被授权(CAP_SYS_TIME)。
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone *tz);
2. settimeofday()调用所造成的那种系统时间的突然变化,可能会对依赖于系统时钟单调递增 的应用造成有害的影响。出于这个原因,当对时间做微小调整时(几秒钟误差),通常是推荐使用库函数 adjtime(), 它将系统时钟逐步调整到正确的时间。
#include <sys/time.h>
int adjtime(const struct timeval *delta, struct timeval *olddelta);
-
delta 参数指向一个 timeval 结构体,指定需要改变时间的秒和微秒数。如果这个值是正数, 那么每秒系统时间都会额外拨快一点点,直到增加完所需的时间。如果 delta 值为负,时钟以类似的方式减慢。
六、进程时间
-
进程时间是进程创建后使用的 CPU 时间数量。出于记录的目的,内核把 CPU 时间分成以 下两部分:
-
用户 CPU 时间是在用户模式下执行所花费的时间数量。有时也称为虚拟时间(virtual time),这对于程序来说,是它已经得到 CPU 的时间。
-
系统 CPU 时间是在内核模式中执行所花费的时间数量。这是内核用于执行系统调用 或代表程序执行的其他任务(例如,服务页错误)的时间。
-
当我们运行一个 shell 程序,我们可以使用的 time(1)命令,同时获得这两个部分的时间值, 以及运行程序所需的实际时间。
-
系统调用 times(),检索进程时间信息,并把结果通过 buf 指向的结构体返回。
-
tms 结构体的前两个字段返回调用进程到目前为止使用的用户和系统组件的 CPU 时间。 最后两个字段返回的信息是:父进程(比如,times()的调用者)执行了系统调用 wait()的所有 已经终止的子进程使用的 CPU 时间。
-
数据类型 clock_t 是用时钟计时单元(clock tick)为单位度量时间的整型值,习惯用于计 算 tms 结构体的 4 个字段。我们可以调用 sysconf(_SC_CLK_TCK)来获得每秒包含的时钟计时 单元数,然后用这个数字除以 clock_t 转换为秒。
-
函数 clock()提供了一个简单的接口用于取得进程时间。它返回一个值描述了调用进程使 用的总的 CPU 时间(包括用户和系统)。
#include <time.h>
clock_t clock(void);