您的位置:首页 > 其它

C标准库源码解剖(8):日期与时间函数time.h(续)

2016-07-29 00:00 363 查看
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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: