1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright © 2021 R. Diez
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * 3. Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #define _DEFAULT_SOURCE
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <time.h>
40
41
42 #define TIME_TM_YEAR_BASE 1900
43
44
45 static void
init_struct_tm(struct tm * stm)46 init_struct_tm ( struct tm * stm )
47 {
48 if ( sizeof( struct tm ) != 9 * sizeof( int ) )
49 {
50 puts("Error: struct tm has changed, so these tests probably need adjusting.");
51 exit(1);
52 }
53
54 stm->tm_sec = 0;
55 stm->tm_min = 0;
56 stm->tm_hour = 0;
57 stm->tm_mday = 0;
58 stm->tm_mon = 0;
59 stm->tm_year = 0;
60 stm->tm_wday = 0;
61 stm->tm_yday = 0;
62 stm->tm_isdst = 0;
63 }
64
65
66 int
main(void)67 main(void)
68 {
69 // Time zone ART3 is America/Buenos_Aires.
70 // There is no daylight saving time (aka sommer time).
71
72 if ( 0 != setenv( "TZ", "ART3", 1 ) )
73 {
74 puts("Error calling setenv().");
75 exit(1);
76 }
77
78 tzset();
79
80
81 struct tm dtForTimegm1;
82 init_struct_tm( &dtForTimegm1 );
83 // This is some arbitrary point in time.
84 dtForTimegm1.tm_mday = 3; // 3rd
85 dtForTimegm1.tm_mon = 1; // of February
86 dtForTimegm1.tm_year = 1970 - TIME_TM_YEAR_BASE;
87
88 // Make a copy, because mktime() and friends modify the fields of the tm structure passed as an argument.
89 struct tm dtForMktime2 = dtForTimegm1;
90
91 // timegm() should be not affected by the time zone.
92
93 time_t t1 = timegm( &dtForTimegm1 );
94
95 if ( t1 != 2851200 ||
96 dtForTimegm1.tm_wday != 2 ) // 2 means Tuesday.
97 {
98 puts("Test t1 failed.");
99 exit(1);
100 }
101
102 // mktime() is affected by the time zone. Check that the offset is the expected one.
103
104 time_t t2 = mktime( &dtForMktime2 );
105
106 if ( t2 != t1 + 3 * 60 * 60 || // The chosen time zone has a positive 3-hour difference.
107 dtForMktime2.tm_wday != 2 )
108 {
109 puts("Test t2 failed.");
110 exit(1);
111 }
112
113
114 // The European Central Time goes the other direction (negative)
115 // and has a daylight saving time (aka sommer time).
116
117 if ( 0 != setenv( "TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1 ) )
118 {
119 puts("Error calling setenv().");
120 exit(1);
121 }
122
123 tzset();
124
125
126 struct tm dtForTimegm3;
127 init_struct_tm( &dtForTimegm3 );
128
129 // This is some arbitrary point in time with daylight saving time.
130 dtForTimegm3.tm_mday = 1; // 1st
131 dtForTimegm3.tm_mon = 4; // of May
132 dtForTimegm3.tm_year = 2021 - TIME_TM_YEAR_BASE;
133 dtForTimegm3.tm_isdst = 1; // Daylight saving time is in effect. That only affects local time.
134
135 struct tm dtForMktime4 = dtForTimegm3;
136
137 time_t t3 = timegm( &dtForTimegm3 );
138
139 if ( t3 != 1619827200 ||
140 dtForTimegm3.tm_wday != 6 || // 6 means Saturday.
141 dtForTimegm3.tm_isdst != 0 ) // timegm() should reset the daylight saving time flag.
142 {
143 puts("Test t3 failed.");
144 exit(1);
145 }
146
147 time_t t4 = mktime( &dtForMktime4 );
148
149 // The offset is -2 hours: 1 hour because of the time zone, and 1 hour because of the sommer time.
150 if ( t4 != t3 - 2 * 60 * 60 ||
151 dtForMktime4.tm_wday != 6 || // 6 means Saturday.
152 dtForMktime4.tm_isdst != 1 ) // mktime() should leave the daylight saving time flag set.
153 {
154 puts("Test t4 failed.");
155 exit(1);
156 }
157
158
159 // Test the first non-negative time_t value of 0,
160 // which is the "UNIX Epoch time" of 1st Januar 1970 00:00:00.
161
162 struct tm dtForTimegm5;
163 init_struct_tm( &dtForTimegm5 );
164
165 dtForTimegm5.tm_mday = 1; // 1st day of the month.
166 dtForTimegm5.tm_year = 1970 - TIME_TM_YEAR_BASE;
167 dtForTimegm5.tm_isdst = -123; // A negative value makes mktime() calculate this flag,
168 // but timegm() should unconditionally reset it.
169
170 time_t t5 = timegm( &dtForTimegm5 );
171
172 if ( t5 != 0 ||
173 dtForTimegm5.tm_wday != 4 || // 4 means Thursday.
174 dtForTimegm5.tm_isdst != 0 )
175 {
176 puts("Test t5 failed.");
177 exit(1);
178 }
179
180
181 // Test the last time_t value of 0x7FFFFFFF before the 32-bit signed integer overflow,
182 // aka "year 2038 problem". That corresponds to 2038-01-19 03:14:07.
183
184 struct tm dtForTimegm6;
185 init_struct_tm( &dtForTimegm6 );
186
187 dtForTimegm6.tm_sec = 7;
188 dtForTimegm6.tm_min = 14;
189 dtForTimegm6.tm_hour = 3,
190 dtForTimegm6.tm_mon = 0; // January.
191 dtForTimegm6.tm_mday = 19;
192 dtForTimegm6.tm_year = 2038 - TIME_TM_YEAR_BASE;
193 dtForTimegm6.tm_isdst = 123; // Some value that should be reset.
194
195 struct tm dtForTimegm7 = dtForTimegm6; // Reuse the date for the next test.
196
197 time_t t6 = timegm( &dtForTimegm6 );
198
199 if ( t6 != 0x7FFFFFFF ||
200 dtForTimegm6.tm_wday != 2 || // 2 means Tuesday.
201 dtForTimegm6.tm_yday != 18 ||
202 dtForTimegm6.tm_isdst != 0 )
203 {
204 puts("Test t6 failed.");
205 exit(1);
206 }
207
208
209 // Test the next time_t value of 0x80000000 right after
210 // the 32-bit signed integer overflow, aka "year 2038 problem".
211
212 dtForTimegm7.tm_sec++;
213
214 time_t t7 = timegm( &dtForTimegm7 );
215
216 if ( t7 != 0x80000000 ||
217 dtForTimegm7.tm_wday != 2 || // 2 means Tuesday.
218 dtForTimegm7.tm_yday != 18 ||
219 dtForTimegm7.tm_isdst != 0 )
220 {
221 puts("Test t7 failed.");
222 exit(1);
223 }
224
225
226 // Test the 29th of February in a leap year far in the future.
227
228 struct tm dtForTimegm8;
229 init_struct_tm( &dtForTimegm8 );
230
231 dtForTimegm8.tm_mon = 1; // February.
232 dtForTimegm8.tm_mday = 29;
233 dtForTimegm8.tm_year = 2104 - TIME_TM_YEAR_BASE;
234
235 time_t t8 = timegm( &dtForTimegm8 );
236
237 if ( t8 != 4233686400 || // That is much higher than INT32_MAX.
238 dtForTimegm8.tm_wday != 5 || // 5 means Friday.
239 dtForTimegm8.tm_yday != 59 )
240 {
241 puts("Test t8 failed.");
242 exit(1);
243 }
244
245
246 return 0;
247 }
248