1 | /* Test program from Paul Eggert and Tony Leneis. */ |
2 | |
3 | #include <array_length.h> |
4 | #include <errno.h> |
5 | #include <limits.h> |
6 | #include <stdio.h> |
7 | #include <stdlib.h> |
8 | #include <support/check.h> |
9 | #include <time.h> |
10 | #include <unistd.h> |
11 | |
12 | /* True if the arithmetic type T is signed. */ |
13 | #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) |
14 | |
15 | /* The maximum and minimum values for the integer type T. These |
16 | macros have undefined behavior if T is signed and has padding bits. |
17 | If this is a problem for you, please let us know how to fix it for |
18 | your host. */ |
19 | #define TYPE_MINIMUM(t) \ |
20 | ((t) (! TYPE_SIGNED (t) \ |
21 | ? (t) 0 \ |
22 | : ~ TYPE_MAXIMUM (t))) |
23 | #define TYPE_MAXIMUM(t) \ |
24 | ((t) (! TYPE_SIGNED (t) \ |
25 | ? (t) -1 \ |
26 | : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1))) |
27 | |
28 | #ifndef TIME_T_MIN |
29 | # define TIME_T_MIN TYPE_MINIMUM (time_t) |
30 | #endif |
31 | #ifndef TIME_T_MAX |
32 | # define TIME_T_MAX TYPE_MAXIMUM (time_t) |
33 | #endif |
34 | |
35 | /* Values we'll use to set the TZ environment variable. */ |
36 | static const char *tz_strings[] = |
37 | { |
38 | (const char *) 0, "GMT0" , "JST-9" , |
39 | "EST+3EDT+2,M10.1.0/00:00:00,M2.3.0/00:00:00" |
40 | }; |
41 | |
42 | static void |
43 | set_timezone (const char *tz) |
44 | { |
45 | printf (format: "info: setting TZ=%s\n" , tz); |
46 | if (setenv (name: "TZ" , value: tz, replace: 1) != 0) |
47 | FAIL_EXIT1 ("setenv: %m" ); |
48 | } |
49 | |
50 | /* Fail if mktime fails to convert a date in the spring-forward gap. |
51 | Based on a problem report from Andreas Jaeger. */ |
52 | static void |
53 | spring_forward_gap (void) |
54 | { |
55 | /* glibc (up to about 1998-10-07) failed this test. */ |
56 | struct tm tm; |
57 | |
58 | /* Use the portable POSIX.1 specification "TZ=PST8PDT,M4.1.0,M10.5.0" |
59 | instead of "TZ=America/Vancouver" in order to detect the bug even |
60 | on systems that don't support the Olson extension, or don't have the |
61 | full zoneinfo tables installed. */ |
62 | set_timezone ("PST8PDT,M4.1.0,M10.5.0" ); |
63 | |
64 | tm.tm_year = 98; |
65 | tm.tm_mon = 3; |
66 | tm.tm_mday = 5; |
67 | tm.tm_hour = 2; |
68 | tm.tm_min = 0; |
69 | tm.tm_sec = 0; |
70 | tm.tm_isdst = -1; |
71 | if (mktime (&tm) == (time_t)-1) |
72 | FAIL_EXIT1 ("mktime: %m" ); |
73 | } |
74 | |
75 | static void |
76 | mktime_test1 (time_t now) |
77 | { |
78 | struct tm *lt = localtime (&now); |
79 | if (lt == NULL) |
80 | { |
81 | /* For extreme input values, it is expected that localtime fails |
82 | with EOVERFLOW. */ |
83 | printf (format: "info: localtime (%lld) failed: %m\n" , (long long int) now); |
84 | TEST_COMPARE (errno, EOVERFLOW); |
85 | return; |
86 | } |
87 | TEST_COMPARE (mktime (lt), now); |
88 | } |
89 | |
90 | static void |
91 | mktime_test (time_t now) |
92 | { |
93 | mktime_test1 (now); |
94 | mktime_test1 (now: (time_t) (TIME_T_MAX - now)); |
95 | mktime_test1 (now: (time_t) (TIME_T_MIN + now)); |
96 | } |
97 | |
98 | static void |
99 | irix_6_4_bug (void) |
100 | { |
101 | /* Based on code from Ariel Faigon. */ |
102 | struct tm tm; |
103 | tm.tm_year = 96; |
104 | tm.tm_mon = 3; |
105 | tm.tm_mday = 0; |
106 | tm.tm_hour = 0; |
107 | tm.tm_min = 0; |
108 | tm.tm_sec = 0; |
109 | tm.tm_isdst = -1; |
110 | mktime (&tm); |
111 | TEST_COMPARE (tm.tm_mon, 2); |
112 | TEST_COMPARE (tm.tm_mday, 31); |
113 | } |
114 | |
115 | static void |
116 | bigtime_test (int j) |
117 | { |
118 | struct tm tm; |
119 | time_t now; |
120 | tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_hour = tm.tm_min = tm.tm_sec = j; |
121 | tm.tm_isdst = -1; |
122 | now = mktime (&tm); |
123 | if (now != (time_t) -1) |
124 | { |
125 | struct tm *lt = localtime (&now); |
126 | TEST_COMPARE (lt->tm_year, tm.tm_year); |
127 | TEST_COMPARE (lt->tm_mon, tm.tm_mon); |
128 | TEST_COMPARE (lt->tm_mday, tm.tm_mday); |
129 | TEST_COMPARE (lt->tm_hour, tm.tm_hour); |
130 | TEST_COMPARE (lt->tm_min, tm.tm_min); |
131 | TEST_COMPARE (lt->tm_sec, tm.tm_sec); |
132 | TEST_COMPARE (lt->tm_yday, tm.tm_yday); |
133 | TEST_COMPARE (lt->tm_wday, tm.tm_wday); |
134 | TEST_COMPARE (lt->tm_isdst < 0 ? -1 : 0 < lt->tm_isdst, |
135 | tm.tm_isdst < 0 ? -1 : 0 < tm.tm_isdst); |
136 | } |
137 | } |
138 | |
139 | static int |
140 | do_test (void) |
141 | { |
142 | time_t t, delta; |
143 | int i; |
144 | unsigned int j; |
145 | |
146 | set_timezone ("America/Sao_Paulo" ); |
147 | |
148 | delta = TIME_T_MAX / 997; /* a suitable prime number */ |
149 | for (i = 0; i < array_length (tz_strings); i++) |
150 | { |
151 | if (tz_strings[i] != NULL) |
152 | set_timezone (tz_strings[i]); |
153 | |
154 | for (t = 0; t <= TIME_T_MAX - delta; t += delta) |
155 | mktime_test (now: t); |
156 | mktime_test (now: (time_t) 1); |
157 | mktime_test (now: (time_t) (60 * 60)); |
158 | mktime_test (now: (time_t) (60 * 60 * 24)); |
159 | |
160 | for (j = 1; j <= INT_MAX; j *= 2) |
161 | bigtime_test (j); |
162 | bigtime_test (j: j - 1); |
163 | } |
164 | irix_6_4_bug (); |
165 | spring_forward_gap (); |
166 | return 0; |
167 | } |
168 | |
169 | #include <support/test-driver.c> |
170 | |