]> granicus.if.org Git - postgresql/blob - src/timezone/difftime.c
Add Olson's public domain timezone library to src/timezone.
[postgresql] / src / timezone / difftime.c
1 /*\r
2 ** This file is in the public domain, so clarified as of\r
3 ** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).\r
4 */\r
5 \r
6 #ifndef lint\r
7 #ifndef NOID\r
8 static char     elsieid[] = "@(#)difftime.c     7.9";\r
9 #endif /* !defined NOID */\r
10 #endif /* !defined lint */\r
11 \r
12 /*LINTLIBRARY*/\r
13 \r
14 #include "private.h"\r
15 \r
16 /*\r
17 ** Algorithm courtesy Paul Eggert (eggert@twinsun.com).\r
18 */\r
19 \r
20 #ifdef HAVE_LONG_DOUBLE\r
21 #define long_double     long double\r
22 #endif /* defined HAVE_LONG_DOUBLE */\r
23 #ifndef HAVE_LONG_DOUBLE\r
24 #define long_double     double\r
25 #endif /* !defined HAVE_LONG_DOUBLE */\r
26 \r
27 double\r
28 difftime(time1, time0)\r
29 const time_t    time1;\r
30 const time_t    time0;\r
31 {\r
32         time_t  delta;\r
33         time_t  hibit;\r
34 \r
35         {\r
36                 time_t          tt;\r
37                 double          d;\r
38                 long_double     ld;\r
39 \r
40                 if (sizeof tt < sizeof d)\r
41                         return (double) time1 - (double) time0;\r
42                 if (sizeof tt < sizeof ld)\r
43                         return (long_double) time1 - (long_double) time0;\r
44         }\r
45         if (time1 < time0)\r
46                 return -difftime(time0, time1);\r
47         /*\r
48         ** As much as possible, avoid loss of precision\r
49         ** by computing the difference before converting to double.\r
50         */\r
51         delta = time1 - time0;\r
52         if (delta >= 0)\r
53                 return delta;\r
54         /*\r
55         ** Repair delta overflow.\r
56         */\r
57         hibit = (~ (time_t) 0) << (TYPE_BIT(time_t) - 1);\r
58         /*\r
59         ** The following expression rounds twice, which means\r
60         ** the result may not be the closest to the true answer.\r
61         ** For example, suppose time_t is 64-bit signed int,\r
62         ** long_double is IEEE 754 double with default rounding,\r
63         ** time1 = 9223372036854775807 and time0 = -1536.\r
64         ** Then the true difference is 9223372036854777343,\r
65         ** which rounds to 9223372036854777856\r
66         ** with a total error of 513.\r
67         ** But delta overflows to -9223372036854774273,\r
68         ** which rounds to -9223372036854774784, and correcting\r
69         ** this by subtracting 2 * (long_double) hibit\r
70         ** (i.e. by adding 2**64 = 18446744073709551616)\r
71         ** yields 9223372036854776832, which\r
72         ** rounds to 9223372036854775808\r
73         ** with a total error of 1535 instead.\r
74         ** This problem occurs only with very large differences.\r
75         ** It's too painful to fix this portably.\r
76         ** We are not alone in this problem;\r
77         ** some C compilers round twice when converting\r
78         ** large unsigned types to small floating types,\r
79         ** so if time_t is unsigned the "return delta" above\r
80         ** has the same double-rounding problem with those compilers.\r
81         */\r
82         return delta - 2 * (long_double) hibit;\r
83 }\r