1 /*
2 Copyright (c) 1994 Cygnus Support.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms are permitted
6 provided that the above copyright notice and this paragraph are
7 duplicated in all such forms and that any documentation,
8 and/or other materials related to such
9 distribution and use acknowledge that the software was developed
10 at Cygnus Support, Inc. Cygnus Support, Inc. may not be used to
11 endorse or promote products derived from this software without
12 specific prior written permission.
13 THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17 /*
18 * mktime.c
19 * Original Author: G. Haley
20 *
21 * Converts the broken-down time, expressed as local time, in the structure
22 * pointed to by tim_p into a calendar time value. The original values of the
23 * tm_wday and tm_yday fields of the structure are ignored, and the original
24 * values of the other fields have no restrictions. On successful completion
25 * the fields of the structure are set to represent the specified calendar
26 * time. Returns the specified calendar time. If the calendar time can not be
27 * represented, returns the value (time_t) -1.
28 *
29 * Modifications: Fixed tm_isdst usage - 27 August 2008 Craig Howland.
30 * Added timegm - 15 May 2021 R. Diez.
31 */
32
33 /*
34 FUNCTION
35 <<mktime>>, <<timegm>>---convert time to arithmetic representation
36
37 INDEX
38 mktime
39 INDEX
40 timegm
41
42 SYNOPSIS
43 #include <time.h>
44 time_t mktime(struct tm *<[timp]>);
45 time_t timegm(struct tm *<[timp]>);
46
47 DESCRIPTION
48 <<mktime>> assumes the time at <[timp]> is a local time, and converts
49 its representation from the traditional representation defined by
50 <<struct tm>> into a representation suitable for arithmetic.
51
52 <<localtime>> is the inverse of <<mktime>>.
53
54 <<timegm>> is similar to <<mktime>>, but assumes that the time at
55 <[timp]> is Coordinated Universal Time (UTC).
56
57 <<timegm>> could be emulated by setting the TZ environment variable to UTC,
58 calling <<mktime>> and restoring the value of TZ. However, other concurrent
59 threads could be affected by the temporary change to TZ.
60
61 <<timegm>> is the inverse of <<gmtime>>.
62
63 <<timegm>> is available if _BSD_SOURCE || _SVID_SOURCE || _DEFAULT_SOURCE.
64
65 RETURNS
66 If the contents of the structure at <[timp]> do not form a valid
67 calendar time representation, the result is <<-1>>. Otherwise, the
68 result is the time, converted to a <<time_t>> value.
69
70 PORTABILITY
71 ANSI C requires <<mktime>>.
72
73 <<timegm>> is a nonstandard GNU extension that is also present on the BSDs.
74
75 <<mktime>> and <<timegm>> require no supporting OS subroutines.
76 */
77
78 #define _DEFAULT_SOURCE
79 #include <stdlib.h>
80 #include <time.h>
81 #include "local.h"
82
83
84 #define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : __month_lengths[0][x])
85
86 static const int16_t _DAYS_BEFORE_MONTH[12] =
87 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
88
89 #define _DAYS_IN_YEAR(year) (isleap(year+YEAR_BASE) ? 366 : 365)
90
91 static void
set_tm_wday(long days,struct tm * tim_p)92 set_tm_wday (long days, struct tm *tim_p)
93 {
94 if ((tim_p->tm_wday = (days + 4) % 7) < 0)
95 tim_p->tm_wday += 7;
96 }
97
98 static void
validate_structure(struct tm * tim_p)99 validate_structure (struct tm *tim_p)
100 {
101 div_t res;
102 int days_in_feb = 28;
103
104 /* calculate time & date to account for out of range values */
105 if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59)
106 {
107 res = div (tim_p->tm_sec, 60);
108 tim_p->tm_min += res.quot;
109 if ((tim_p->tm_sec = res.rem) < 0)
110 {
111 tim_p->tm_sec += 60;
112 --tim_p->tm_min;
113 }
114 }
115
116 if (tim_p->tm_min < 0 || tim_p->tm_min > 59)
117 {
118 res = div (tim_p->tm_min, 60);
119 tim_p->tm_hour += res.quot;
120 if ((tim_p->tm_min = res.rem) < 0)
121 {
122 tim_p->tm_min += 60;
123 --tim_p->tm_hour;
124 }
125 }
126
127 if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23)
128 {
129 res = div (tim_p->tm_hour, 24);
130 tim_p->tm_mday += res.quot;
131 if ((tim_p->tm_hour = res.rem) < 0)
132 {
133 tim_p->tm_hour += 24;
134 --tim_p->tm_mday;
135 }
136 }
137
138 if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11)
139 {
140 res = div (tim_p->tm_mon, 12);
141 tim_p->tm_year += res.quot;
142 if ((tim_p->tm_mon = res.rem) < 0)
143 {
144 tim_p->tm_mon += 12;
145 --tim_p->tm_year;
146 }
147 }
148
149 if (isleap (tim_p->tm_year+YEAR_BASE))
150 days_in_feb = 29;
151
152 if (tim_p->tm_mday <= 0)
153 {
154 while (tim_p->tm_mday <= 0)
155 {
156 if (--tim_p->tm_mon == -1)
157 {
158 tim_p->tm_year--;
159 tim_p->tm_mon = 11;
160 days_in_feb =
161 (isleap (tim_p->tm_year+YEAR_BASE) ?
162 29 : 28);
163 }
164 tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon);
165 }
166 }
167 else
168 {
169 while (tim_p->tm_mday > _DAYS_IN_MONTH (tim_p->tm_mon))
170 {
171 tim_p->tm_mday -= _DAYS_IN_MONTH (tim_p->tm_mon);
172 if (++tim_p->tm_mon == 12)
173 {
174 tim_p->tm_year++;
175 tim_p->tm_mon = 0;
176 days_in_feb =
177 (isleap (tim_p->tm_year+YEAR_BASE) ?
178 29 : 28);
179 }
180 }
181 }
182 }
183
184 static time_t
mktime_utc(struct tm * tim_p,long * days_p)185 mktime_utc (struct tm *tim_p, long *days_p)
186 {
187 time_t tim = 0;
188 long days = 0;
189 int year;
190
191 /* validate structure */
192 validate_structure (tim_p);
193
194 /* compute hours, minutes, seconds */
195 tim += tim_p->tm_sec + (tim_p->tm_min * SECSPERMIN) +
196 (tim_p->tm_hour * SECSPERHOUR);
197
198 /* compute days in year */
199 days += tim_p->tm_mday - 1;
200 days += _DAYS_BEFORE_MONTH[tim_p->tm_mon];
201 if (tim_p->tm_mon > 1 && isleap (tim_p->tm_year+YEAR_BASE))
202 days++;
203
204 /* compute day of the year */
205 tim_p->tm_yday = days;
206
207 if (tim_p->tm_year > 10000 || tim_p->tm_year < -10000)
208 return (time_t) -1;
209
210 /* compute days in other years */
211 if ((year = tim_p->tm_year) > 70)
212 {
213 for (year = 70; year < tim_p->tm_year; year++)
214 days += _DAYS_IN_YEAR (year);
215 }
216 else if (year < 70)
217 {
218 for (year = 69; year > tim_p->tm_year; year--)
219 days -= _DAYS_IN_YEAR (year);
220 days -= _DAYS_IN_YEAR (year);
221 }
222
223 /* compute total seconds */
224 tim += (time_t)days * SECSPERDAY;
225
226 *days_p = days;
227 return tim;
228 }
229
230 time_t
mktime(struct tm * tim_p)231 mktime (struct tm *tim_p)
232 {
233 long days;
234 time_t tim;
235 int year;
236 int isdst=0;
237 __tzinfo_type *tz;
238
239 tim = mktime_utc (tim_p, &days);
240
241 if (tim == (time_t) -1)
242 return tim;
243
244 year = tim_p->tm_year;
245
246 tz = __gettzinfo ();
247
248 TZ_LOCK;
249
250 _tzset_unlocked ();
251
252 if (_daylight)
253 {
254 int tm_isdst;
255 int y = tim_p->tm_year + YEAR_BASE;
256 /* Convert user positive into 1 */
257 tm_isdst = tim_p->tm_isdst > 0 ? 1 : tim_p->tm_isdst;
258 isdst = tm_isdst;
259
260 if (y == tz->__tzyear || __tzcalc_limits (y))
261 {
262 /* calculate start of dst in dst local time and
263 start of std in both std local time and dst local time */
264 time_t startdst_dst = tz->__tzrule[0].change
265 - (time_t) tz->__tzrule[1].offset;
266 time_t startstd_dst = tz->__tzrule[1].change
267 - (time_t) tz->__tzrule[1].offset;
268 time_t startstd_std = tz->__tzrule[1].change
269 - (time_t) tz->__tzrule[0].offset;
270 /* if the time is in the overlap between dst and std local times */
271 if (tim >= startstd_std && tim < startstd_dst)
272 ; /* we let user decide or leave as -1 */
273 else
274 {
275 isdst = (tz->__tznorth
276 ? (tim >= startdst_dst && tim < startstd_std)
277 : (tim >= startdst_dst || tim < startstd_std));
278 /* if user committed and was wrong, perform correction, but not
279 * if the user has given a negative value (which
280 * asks mktime() to determine if DST is in effect or not) */
281 if (tm_isdst >= 0 && (isdst ^ tm_isdst) == 1)
282 {
283 /* we either subtract or add the difference between
284 time zone offsets, depending on which way the user got it
285 wrong. The diff is typically one hour, or 3600 seconds,
286 and should fit in a 16-bit int, even though offset
287 is a long to accomodate 12 hours. */
288 int diff = (int) (tz->__tzrule[0].offset
289 - tz->__tzrule[1].offset);
290 if (!isdst)
291 diff = -diff;
292 tim_p->tm_sec += diff;
293 tim += diff; /* we also need to correct our current time calculation */
294 int mday = tim_p->tm_mday;
295 validate_structure (tim_p);
296 mday = tim_p->tm_mday - mday;
297 /* roll over occurred */
298 if (mday) {
299 /* compensate for month roll overs */
300 if (mday > 1)
301 mday = -1;
302 else if (mday < -1)
303 mday = 1;
304 /* update days for wday calculation */
305 days += mday;
306 /* handle yday */
307 if ((tim_p->tm_yday += mday) < 0) {
308 --year;
309 tim_p->tm_yday = _DAYS_IN_YEAR(year) - 1;
310 } else {
311 mday = _DAYS_IN_YEAR(year);
312 if (tim_p->tm_yday > (mday - 1))
313 tim_p->tm_yday -= mday;
314 }
315 }
316 }
317 }
318 }
319 }
320
321 /* add appropriate offset to put time in gmt format */
322 if (isdst == 1)
323 tim += (time_t) tz->__tzrule[1].offset;
324 else /* otherwise assume std time */
325 tim += (time_t) tz->__tzrule[0].offset;
326
327 TZ_UNLOCK;
328
329 /* reset isdst flag to what we have calculated */
330 tim_p->tm_isdst = isdst;
331
332 set_tm_wday(days, tim_p);
333
334 return tim;
335 }
336
337 time_t
timegm(struct tm * tim_p)338 timegm (struct tm *tim_p)
339 {
340 long days;
341 time_t tim = mktime_utc (tim_p, &days);
342
343 if (tim == (time_t) -1)
344 return tim;
345
346 /* The time is always UTC, so there is no daylight saving time. */
347 tim_p->tm_isdst = 0;
348
349 set_tm_wday(days, tim_p);
350
351 return tim;
352 }
353