首页 > 编程 > .NET > 正文

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

2024-07-10 13:27:17
字体:
来源:转载
供稿:网友

4、difftime函数:从日历时间t1中减去日历时间t0,返回double类型的差值(秒数)。

 

[cpp] view plaincopy
  1. /* difftime.c:difftime函数的实现 */  
  2. #include <time.h>  
  3. #include <limits.h>  
  4. #include <float.h>  
  5. #include <stdint.h>  
  6. #define TYPE_BITS(type) (sizeof (type) * CHAR_BIT)  
  7. #define TYPE_FLOATING(type) ((type) 0.5 == 0.5)  
  8. #define TYPE_SIGNED(type) ((type) -1 < 0)  
  9. /* 返回两个时间TIME1和TIME0的差值(秒数),这里TIME0<=TIME1,time_t众所周知是 
  10.     一个整数类型 */  
  11. static double  
  12. subtract (time_t time1, time_t time0)  
  13. {  
  14.   if (! TYPE_SIGNED (time_t))  
  15.     return time1 - time0;  
  16.   else  
  17.     {  
  18.       /* 对一般的情况进行优化,这里time_t能无损地转换成uintmax_t */  
  19.       uintmax_t dt = (uintmax_t) time1 - (uintmax_t) time0;  
  20.       double delta = dt;  
  21.       if (UINTMAX_MAX / 2 < INTMAX_MAX)  
  22.     {  
  23.       /* 这是一种罕见的情况,uintmax_t有填充位,当把time_t转换成uintmax_t时可能会丢失信息。 
  24.           通过比较dt/2与(time1/2-time0/2)检查是否有溢出,如果它们有一个小的差值,则发生溢出 
  25.            
  26.           在下面的代码中,"h"前缀表示一半(half),通过范围分析,我们有: 
  27.                   -0.5 <= ht1 - 0.5*time1 <= 0.5 
  28.                   -0.5 <= ht0 - 0.5*time0 <= 0.5 
  29.                   -1.0 <= dht - 0.5*(time1 - time0) <= 1.0 
  30.                 如果溢出没有发生,我们也有: 
  31.                   -0.5 <= hdt - 0.5*(time1 - time0) <= 0 
  32.                   -1.0 <= dht - hdt <= 1.5 
  33.                 并且因为dht-hdt是一个整数,我们有: 
  34.                   -1 <= dht - hdt <= 1 
  35.                 或者等价的有: 
  36.                   0 <= dht - hdt + 1 <= 2 
  37.                 在上面的分析中,所有的运算符都有它们准确的数学语义,而不是C语义。然而, 
  38.              dht-hdt+1在C中是无符号的,因此它无需与0比较  */  
  39.       uintmax_t hdt = dt / 2;  
  40.       time_t ht1 = time1 / 2;  
  41.       time_t ht0 = time0 / 2;  
  42.       time_t dht = ht1 - ht0;  
  43.       if (2 < dht - hdt + 1)  
  44.         {  
  45.           /* 修复这个微小的溢出 
  46.              下面的表达式包含一个秒数的舍入,因此结果可能不是与真正答案最接近的。这种 
  47.              情况只在差值非常大时发生 */  
  48.           delta = dt + 2.0L * (UINTMAX_MAX - UINTMAX_MAX / 2);  
  49.         }  
  50.     }  
  51.       return delta;  
  52.     }  
  53. }  
  54. /* 返回TIME1和TIME0之间的差值(秒数)  */  
  55. double  
  56. __difftime (time_t time1, time_t time0)  
  57. {  
  58.   /* 如果不会导致double的舍入错误,就转换成double类型,然后做减法 */  
  59.   if (TYPE_BITS (time_t) <= DBL_MANT_DIG  
  60.       || (TYPE_FLOATING (time_t) && sizeof (time_t) < sizeof (long double)))  
  61.     return (double) time1 - (double) time0;  
  62.   /* 对long double做同样的事  */  
  63.   if (TYPE_BITS (time_t) <= LDBL_MANT_DIG || TYPE_FLOATING (time_t))  
  64.     return (long double) time1 - (long double) time0;  
  65.   /* 调用substract从大的参数中减去小的整数,把差值转换成double,如果需要还要转换成负数  */  
  66.   return time1 < time0 ? - subtract (time0, time1) : subtract (time1, time0);  
  67. }  
  68. /* 把函数命名为标准的difftime */  
  69. 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来完成工作,如下:

 

[cpp] view plaincopy
  1. /* gmtime.c:gmtime函数的实现,把`time_t'转换成UTC中的`struct tm'  */  
  2. #include <time.h>  
  3. /* 返回TIMER的struct tm表示(为格林尼治标准时间UTC), 
  4.     使用TP来存放结果 */  
  5. struct tm *  
  6. __gmtime_r (t, tp)  
  7.      const time_t *t;  
  8.      struct tm *tp;  
  9. {  
  10.   return __tz_convert (t, 0, tp);  
  11. }  
  12. libc_hidden_def (__gmtime_r)  
  13. weak_alias (__gmtime_r, gmtime_r)  
  14. /* 返回TIMER的struct tm表示(为格林尼治标准时间UTC) */  
  15. struct tm *  
  16. gmtime (t)  
  17.      const time_t *t;  
  18. {  
  19.   return __tz_convert (t, 0, &_tmbuf);  
  20. }  

 

 

[cpp] view plaincopy
  1. /* localtime.c:localtime函数的实现,把`time_t'转换成本时间区域中的`struct tm'  */  
  2. #include <time.h>  
  3. /* C标准指出localtime和gmtime返回同样的指针  */  
  4. struct tm _tmbuf;  
  5. /* 返回TIMER的struct tm表示(为本地时间),使用TP来存放结果 */  
  6. struct tm *  
  7. __localtime_r (t, tp)  
  8.      const time_t *t;  
  9.      struct tm *tp;  
  10. {  
  11.   return __tz_convert (t, 1, tp);  
  12. }  
  13. weak_alias (__localtime_r, localtime_r)  
  14.   
  15. /* 返回T的struct tm表示(为本地时间) */  
  16. struct tm *  
  17. localtime (t)  
  18.      const time_t *t;  
  19. {  
  20.   return __tz_convert (t, 1, &_tmbuf);  
  21. }  
  22. 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))。

 

[cpp] view plaincopy
  1. /* asctime.c:asctime函数的实现 */  
  2. #include "../locale/localeinfo.h"  
  3. #include <errno.h>  
  4. #include <limits.h>  
  5. #include <stdio.h>  
  6. #include <time.h>  
  7. /* 这里的函数在GNU libc的locale/C-time.c中定义  */  
  8. extern const struct locale_data _nl_C_LC_TIME attribute_hidden;  
  9. #define ab_day_name(DAY) (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)+(DAY)].string)  
  10. #define ab_month_name(MON) (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)+(MON)].string)  
  11. static const char format[] = "%.3s %.3s%3d %.2d:%.2d:%.2d %d/n";  
  12. static char result[          3+1+ 3+1+20+1+20+1+20+1+20+1+20+1 + 1];  
  13. /* 输出日历时间的核心函数 */  
  14. static char *  
  15. asctime_internal (const struct tm *tp, char *buf, size_t buflen)  
  16. {  
  17.   if (tp == NULL)  
  18.     {  
  19.       __set_errno (EINVAL); /* 参数无效的错误 */  
  20.       return NULL;  
  21.     }  
  22.   /* 我们限制打印的年份大小。使用%d格式说明符表示年份后两位(即要加上1900),这可能溢出 
  23.        使得一个负值被打印出来。对一些体系结构而言,我们在理论上能使用%ld或者更大的整数格 
  24.        式,但这将意味着输出需要更大的空间。如果asctime_r接口比较健壮,并且传递给它一个 
  25.        缓冲区,那么这将不再是问题 */  
  26.   if (__builtin_expect (tp->tm_year > INT_MAX - 1900, 0)) /* 使用了内部的一个断言 */  
  27.     {  
  28.     eoverflow:  
  29.       __set_errno (EOVERFLOW); /* 设置溢出错误 */  
  30.       return NULL;  
  31.     }  
  32.   /* 按格式"Day Mon dd hh:mm:ss yyyy/n"打印给定的日历时间 */  
  33.   int n = __snprintf (buf, buflen, format,  
  34.               (tp->tm_wday < 0 || tp->tm_wday >= 7 ?  
  35.                "???" : ab_day_name (tp->tm_wday)),  
  36.               (tp->tm_mon < 0 || tp->tm_mon >= 12 ?  
  37.                "???" : ab_month_name (tp->tm_mon)),  
  38.               tp->tm_mday, tp->tm_hour, tp->tm_min,  
  39.               tp->tm_sec, 1900 + tp->tm_year);  
  40.   if (n < 0)  
  41.     return NULL;  
  42.   if (n >= buflen)  
  43.     goto eoverflow; /* 转身上面的eoverflow标号处 */  
  44.   return buf;  
  45. }  
  46. /* 与asctime类似,但把结果写入到显式提供的缓冲区中。这个缓冲区只保证使用前26字节 
  47.     的长度 */  
  48. char *  
  49. __asctime_r (const struct tm *tp, char *buf)  
  50. {  
  51.   return asctime_internal (tp, buf, 26);  
  52. }  
  53. weak_alias (__asctime_r, asctime_r)  
  54.   
  55. /* 返回表示TP的可打印日期与时间字符串,格式为"Day Mon dd hh:mm:ss yyyy/n" */  
  56. char *  
  57. asctime (const struct tm *tp)  
  58. {  
  59.   return asctime_internal (tp, result, sizeof (result));  
  60. }  
  61. libc_hidden_def (asctime)  

 

 

[cpp] view plaincopy
  1. /* ctime.c:ctime函数的实现  */  
  2. #undef  __OPTIMIZE__    /* 避免内联ctime函数  */  
  3. #include <time.h>  
  4. #undef  ctime  
  5. /* 返回像asctime中一样的表示时间T的字符串,等价于asctime(localtime (t)) */  
  6. char *  
  7. ctime (const time_t *t)  
  8. {  
  9.   /* C标准指出ctime(t)等价于asctime(localtime(t)),特别地,ctime和asctime必须 
  10.       生成同样的指针 */  
  11.   return asctime (localtime (t));  
  12. }  

 

    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来完成工作。

 

[cpp] view plaincopy
  1. /* strftime.c:strftime函数的实现  */  
  2. #include <time.h>  
  3. #include <locale/localeinfo.h>  
  4. /* 根据控制串FORMAT对TP进行格式化,并保存到字符串S中,最多写入MAXSIZE个字符到S中, 
  5.     并返回写入的字符数,如果字符串长超过MAXSIZE,则返回0 */  
  6. size_t  
  7. strftime (char *s, size_t maxsize, const char *format, const struct tm *tp)  
  8. {  
  9.   return __strftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE);  
  10. }  
  11. libc_hidden_def (strftime)  

 

 

[cpp] view plaincopy
  1. /* wcsftime.c:wcsftime函数的实现  */  
  2. #include <wchar.h>  
  3. #include <locale/localeinfo.h>  
  4. /* 根据控制串FORMAT对TP进行格式化,并保存到宽字符串S中,最多写入MAXSIZE个字符到S中, 
  5.     并返回写入的字符数,如果字符串长超过MAXSIZE,则返回0 */  
  6. size_t  
  7. wcsftime (wchar_t *s, size_t maxsize, const wchar_t *format,  
  8.       const struct tm *tp)  
  9. {  
  10.   return __wcsftime_l (s, maxsize, format, tp, _NL_CURRENT_LOCALE);  
  11. }  
  12. libc_hidden_def (wcsftime)  

 

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表