原文出自:http://blog.csdn.net/zhoudaxia/article/details/4647150
4、difftime函数:从日历时间t1中减去日历时间t0,返回double类型的差值(秒数)。
- /* difftime.c:difftime函数的实现 */
- #include <time.h>
- #include <limits.h>
- #include <float.h>
- #include <stdint.h>
- #define TYPE_BITS(type) (sizeof (type) * CHAR_BIT)
- #define TYPE_FLOATING(type) ((type) 0.5 == 0.5)
- #define TYPE_SIGNED(type) ((type) -1 < 0)
- /* 返回两个时间TIME1和TIME0的差值(秒数),这里TIME0<=TIME1,time_t众所周知是
- 一个整数类型 */
- static double
- subtract (time_t time1, time_t time0)
- {
- if (! TYPE_SIGNED (time_t))
- return time1 - time0;
- else
- {
- /* 对一般的情况进行优化,这里time_t能无损地转换成uintmax_t */
- uintmax_t dt = (uintmax_t) time1 - (uintmax_t) time0;
- double delta = dt;
- if (UINTMAX_MAX / 2 < INTMAX_MAX)
- {
- /* 这是一种罕见的情况,uintmax_t有填充位,当把time_t转换成uintmax_t时可能会丢失信息。
- 通过比较dt/2与(time1/2-time0/2)检查是否有溢出,如果它们有一个小的差值,则发生溢出
- 在下面的代码中,"h"前缀表示一半(half),通过范围分析,我们有:
- -0.5 <= ht1 - 0.5*time1 <= 0.5
- -0.5 <= ht0 - 0.5*time0 <= 0.5
- -1.0 <= dht - 0.5*(time1 - time0) <= 1.0
- 如果溢出没有发生,我们也有:
- -0.5 <= hdt - 0.5*(time1 - time0) <= 0
- -1.0 <= dht - hdt <= 1.5
- 并且因为dht-hdt是一个整数,我们有:
- -1 <= dht - hdt <= 1
- 或者等价的有:
- 0 <= dht - hdt + 1 <= 2
- 在上面的分析中,所有的运算符都有它们准确的数学语义,而不是C语义。然而,
- dht-hdt+1在C中是无符号的,因此它无需与0比较 */
- uintmax_t hdt = dt / 2;
- time_t ht1 = time1 / 2;
- time_t ht0 = time0 / 2;
- time_t dht = ht1 - ht0;
- if (2 < dht - hdt + 1)
- {
- /* 修复这个微小的溢出
- 下面的表达式包含一个秒数的舍入,因此结果可能不是与真正答案最接近的。这种
- 情况只在差值非常大时发生 */
- delta = dt + 2.0L * (UINTMAX_MAX - UINTMAX_MAX / 2);
- }
- }
- return delta;
- }
- }
- /* 返回TIME1和TIME0之间的差值(秒数) */
- double
- __difftime (time_t time1, time_t time0)
- {
- /* 如果不会导致double的舍入错误,就转换成double类型,然后做减法 */
- if (TYPE_BITS (time_t) <= DBL_MANT_DIG
- || (TYPE_FLOATING (time_t) && sizeof (time_t) < sizeof (long double)))
- return (double) time1 - (double) time0;
- /* 对long double做同样的事 */
- if (TYPE_BITS (time_t) <= LDBL_MANT_DIG || TYPE_FLOATING (time_t))
- return (long double) time1 - (long double) time0;
- /* 调用substract从大的参数中减去小的整数,把差值转换成double,如果需要还要转换成负数 */
- return time1 < time0 ? - subtract (time0, time1) : subtract (time1, time0);
- }
- /* 把函数命名为标准的difftime */
- strong_alias (__difftime, difftime)
由于time_t是整型,因此代码中如果不会导致double的舍入错误,就转换成double类型,然后做减法。如果有可能导致double的舍入错误,则调用substract做减法。substract中,把两个time_t参数转换成最大长度的整数类型uintmax_t,一般都能实现无损的转换,然后做减法,返回结果值。有一种罕见的情况即UINTMAX_MAX / 2 < INTMAX_MAX时,会导致转换有微小的溢出,因此对这种情况要进行特殊地处理,这时获得的结果可能是真正答案的近似值(有一个细微的舍入)。
5、gmtime,localtime,mktime:gmtime把time_t类型时间值转换成UTC中的struct tm表示,localtime把time_t类型时间值转换成本地时间区域中的struct tm表示,mktime根据struct tm结构指定的本地时间构造time_t类型的值。mktime由于要对tm结构的各个成员值进行分析,然后合并成time_t类型的值返回,比较复杂,代码就不解剖了。而且不同Linux/Unix平台上的实现有差异,需要兼容不同的平台。核心的函数是__mktime_internal,在mktime中只是直接调用这个函数。gmtime和localtime的实现比较简单,都是直接调用内置函数__tz_convert来完成工作,如下:
- /* gmtime.c:gmtime函数的实现,把`time_t'转换成UTC中的`struct tm' */
- #include <time.h>
- /* 返回TIMER的struct tm表示(为格林尼治标准时间UTC),
- 使用TP来存放结果 */
- struct tm *
- __gmtime_r (t, tp)
- const time_t *t;
- struct tm *tp;
- {
- return __tz_convert (t, 0, tp);
- }
- libc_hidden_def (__gmtime_r)
- weak_alias (__gmtime_r, gmtime_r)
- /* 返回TIMER的struct tm表示(为格林尼治标准时间UTC) */
- struct tm *
- gmtime (t)
- const time_t *t;
- {
- return __tz_convert (t, 0, &_tmbuf);
- }
- /* localtime.c:localtime函数的实现,把`time_t'转换成本时间区域中的`struct tm' */
- #include <time.h>
- /* C标准指出localtime和gmtime返回同样的指针 */
- struct tm _tmbuf;
- /* 返回TIMER的struct tm表示(为本地时间),使用TP来存放结果 */
- struct tm *
- __localtime_r (t, tp)
- const time_t *t;
- struct tm *tp;
- {
- return __tz_convert (t, 1, tp);
- }
- weak_alias (__localtime_r, localtime_r)
- /* 返回T的struct tm表示(为本地时间) */
- struct tm *
- localtime (t)
- const time_t *t;
- {
- return __tz_convert (t, 1, &_tmbuf);
- }
- libc_hidden_def (localtime)
6、asctime,ctime:asctime返回tp时间(struct tm结构)的可打印格式,格式为"%a %b %d %H:%M:%S %Y/n",ctime(time_t *tp)等价于asctime(localtime(tp))。
- /* asctime.c:asctime函数的实现 */
- #include "../locale/localeinfo.h"
- #include <errno.h>
- #include <limits.h>
- #include <stdio.h>
- #include <time.h>
- /* 这里的函数在GNU libc的locale/C-time.c中定义 */
- extern const struct locale_data _nl_C_LC_TIME attribute_hidden;
- #define ab_day_name(DAY) (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)+(DAY)].string)
- #define ab_month_name(MON) (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)+(MON)].string)
- static const char format[] = "%.3s %.3s%3d %.2d:%.2d:%.2d %d/n";
- static char result[ 3+1+ 3+1+20+1+20+1+20+1+20+1+20+1 + 1];
- /* 输出日历时间的核心函数 */
- static char *
- asctime_internal (const struct tm *tp, char *buf, size_t buflen)
- {
- if (tp == NULL)
- {
- __set_errno (EINVAL); /* 参数无效的错误 */
- return NULL;
- }
- /* 我们限制打印的年份大小。使用%d格式说明符表示年份后两位(即要加上1900),这可能溢出
- 使得一个负值被打印出来。对一些体系结构而言,我们在理论上能使用%ld或者更大的整数格
- 式,但这将意味着输出需要更大的空间。如果asctime_r接口比较健壮,并且传递给它一个
- 缓冲区,那么这将不再是问题 */
- if (__builtin_expect (tp->tm_year > INT_MAX - 1900, 0)) /* 使用了内部的一个断言 */
- {
- eoverflow:
- __set_errno (EOVERFLOW); /* 设置溢出错误 */
- return NULL;
- }
- /* 按格式"Day Mon dd hh:mm:ss yyyy/n"打印给定的日历时间 */
- int n = __snprintf (buf, buflen, format,
- (tp->tm_wday < 0 || tp->tm_wday >= 7 ?
- "???" : ab_day_name (tp->tm_wday)),
- (tp->tm_mon < 0 || tp->tm_mon >= 12 ?
- "???" : ab_month_name (tp->tm_mon)),
- tp->tm_mday, tp->tm_hour, tp->tm_min,
- tp->tm_sec, 1900 + tp->tm_year);
- if (n < 0)
- return NULL;
- if (n >= buflen)
- goto eoverflow; /* 转身上面的eoverflow标号处 */
- return buf;
- }
- /* 与asctime类似,但把结果写入到显式提供的缓冲区中。这个缓冲区只保证使用前26字节
- 的长度 */
- char *
- __asctime_r (const struct tm *tp, char *buf)
- {
- return asctime_internal (tp, buf, 26);
- }
- weak_alias (__asctime_r, asctime_r)
- /* 返回表示TP的可打印日期与时间字符串,格式为"Day Mon dd hh:mm:ss yyyy/n" */
- char *
- asctime (const struct tm *tp)
- {
- return asctime_internal (tp, result, sizeof (result));
- }
- libc_hidden_def (asctime)
- /* ctime.c:ctime函数的实现 */
- #undef __OPTIMIZE__ /* 避免内联ctime函数 */
- #include <time.h>
- #undef ctime
- /* 返回像asctime中一样的表示时间T的字符串,等价于asctime(localtime (t)) */
- char *
- ctime (const time_t *t)
- {
- /* C标准指出ctime(t)等价于asctime(localtime(t)),特别地,ctime和asctime必须
- 生成同样的指针 */
- return asctime (localtime (t));
- }
asctime_internal是实现日历时间输出的核心函数。这里限制了打印的年份大小,以防年份太大而溢出,然后调用内部函数__snprintf来把时间输出到全局的字符串result中,并返回这个字符串,格式控制字符串保存在全局的format串中。
7、strftime,wcsftime:对struct tm结构的时间进行格式化输出,保存到字符串中。strftime函数根据控制串FORMAT对TP进行格式化,并保存到字符串S中,最多写入MAXSIZE个字符到S中,并返回写入的字符数,如果字符串长超过MAXSIZE,则返回0。wcsftime是对应的宽字符版本,时间会被格式化成宽字符串,在wchar.h中声明。它们都直接调用内部函数__strftime_l和__wcsftime_l来完成工作。
- /* strftime.c:strftime函数的实现 */
- #include <time.h>
- #include <locale/localeinfo.h>
- /* 根据控制串FORMAT对TP进行格式化,并保存到字符串S中,最多写入MAXSIZE个字符到S中,
- 并返回写入的字符数,如果字符串长超过MAXSIZE,则返回0 */
- size_t
- strftime (char *s, size_t maxsize, const char *format, const struct tm *tp)
- {
- return __strftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE);
- }
- libc_hidden_def (strftime)
- /* wcsftime.c:wcsftime函数的实现 */
- #include <wchar.h>
- #include <locale/localeinfo.h>
- /* 根据控制串FORMAT对TP进行格式化,并保存到宽字符串S中,最多写入MAXSIZE个字符到S中,
- 并返回写入的字符数,如果字符串长超过MAXSIZE,则返回0 */
- size_t
- wcsftime (wchar_t *s, size_t maxsize, const wchar_t *format,
- const struct tm *tp)
- {
- return __wcsftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE);
- }
- libc_hidden_def (wcsftime)