C标准库源码解剖(8):日期与时间函数time.h(续)
2016-07-29 00:00
363 查看
4、difftime函数:从日历时间t1中减去日历时间t0,返回double类型的差值(秒数)。
由于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来完成工作,如下:
6、asctime,ctime:asctime返回tp时间(struct tm结构)的可打印格式,格式为"%a %b %d %H:%M:%S %Y/n",ctime(time_t *tp)等价于asctime(localtime(tp))。
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来完成工作。
/* 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)
相关文章推荐
- C++ Primer学习系列(1):快速入门/变量和基本类型/标准库类型
- 搭建Eclipse C/C++开发环境
- C标准库源码解剖(1):类型相关的定义
- Hadoop在百度的应用--4000个节点的分布式集群
- zlib库剖析(1):实现概览
- 分支限界法
- Java集合框架官方教程(5):集合类的同步包装器/不可变包装器
- 我的C++实践(11):存放异类对象的元组类型
- 数据库集群技术漫谈
- 数值概率算法
- 服务器设计系列:线程
- 我的C实践(1):宏的应用
- Hadoop在Facebook的使用--广告分析
- libxml2剖析(1):功能特性
- 服务器设计系列:高性能服务器本质论
- Martin Fowler的《持续集成》
- dbm数据库源代码分析(4):头文件部分(续)
- C标准库源码解剖(14):通用函数stdlib.h
- C标准库源码解剖(3):字符处理函数ctype.h和wctype.h
- RPM软件包的制作