1 /*
2 * mktime.c
3 * Original Author: G. Haley
4 *
5 * Converts the broken-down time, expressed as local time, in the structure
6 * pointed to by tim_p into a calendar time value. The original values of the
7 * tm_wday and tm_yday fields of the structure are ignored, and the original
8 * values of the other fields have no restrictions. On successful completion
9 * the fields of the structure are set to represent the specified calendar
10 * time. Returns the specified calendar time. If the calendar time can not be
11 * represented, returns the value (time_t) -1.
12 */
13
14 /*
15 FUNCTION
16 <<mktime>>---convert time to arithmetic representation
17
18 INDEX
19 mktime
20
21 ANSI_SYNOPSIS
22 #include <time.h>
23 time_t mktime(struct tm *<[timp]>);
24
25 TRAD_SYNOPSIS
26 #include <time.h>
27 time_t mktime(<[timp]>)
28 struct tm *<[timp]>;
29
30 DESCRIPTION
31 <<mktime>> assumes the time at <[timp]> is a local time, and converts
32 its representation from the traditional representation defined by
33 <<struct tm>> into a representation suitable for arithmetic.
34
35 <<localtime>> is the inverse of <<mktime>>.
36
37 RETURNS
38 If the contents of the structure at <[timp]> do not form a valid
39 calendar time representation, the result is <<-1>>. Otherwise, the
40 result is the time, converted to a <<time_t>> value.
41
42 PORTABILITY
43 ANSI C requires <<mktime>>.
44
45 <<mktime>> requires no supporting OS subroutines.
46 */
47
48 #include <stdlib.h>
49 #include <time.h>
50 #include "local.h"
51
52 #define _SEC_IN_MINUTE 60L
53 #define _SEC_IN_HOUR 3600L
54 #define _SEC_IN_DAY 86400L
55
56 static _CONST int DAYS_IN_MONTH[12] =
57 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
58
59 #define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : DAYS_IN_MONTH[x])
60
61 static _CONST int _DAYS_BEFORE_MONTH[12] =
62 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
63
64 #define _ISLEAP(y) (((y) % 4) == 0 && (((y) % 100) != 0 || (((y)+1900) % 400) == 0))
65 #define _DAYS_IN_YEAR(year) (_ISLEAP(year) ? 366 : 365)
66
67 static void
validate_structure(tim_p)68 validate_structure (tim_p)
69 struct tm *tim_p;
70 {
71 div_t res;
72 int days_in_feb = 28;
73
74 /* calculate time & date to account for out of range values */
75 if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59)
76 {
77 res = div (tim_p->tm_sec, 60);
78 tim_p->tm_min += res.quot;
79 if ((tim_p->tm_sec = res.rem) < 0)
80 {
81 tim_p->tm_sec += 60;
82 --tim_p->tm_min;
83 }
84 }
85
86 if (tim_p->tm_min < 0 || tim_p->tm_min > 59)
87 {
88 res = div (tim_p->tm_min, 60);
89 tim_p->tm_hour += res.quot;
90 if ((tim_p->tm_min = res.rem) < 0)
91 {
92 tim_p->tm_min += 60;
93 --tim_p->tm_hour;
94 }
95 }
96
97 if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23)
98 {
99 res = div (tim_p->tm_hour, 24);
100 tim_p->tm_mday += res.quot;
101 if ((tim_p->tm_hour = res.rem) < 0)
102 {
103 tim_p->tm_hour += 24;
104 --tim_p->tm_mday;
105 }
106 }
107
108 if (tim_p->tm_mon > 11)
109 {
110 res = div (tim_p->tm_mon, 12);
111 tim_p->tm_year += res.quot;
112 if ((tim_p->tm_mon = res.rem) < 0)
113 {
114 tim_p->tm_mon += 12;
115 --tim_p->tm_year;
116 }
117 }
118
119 if (_DAYS_IN_YEAR (tim_p->tm_year) == 366)
120 days_in_feb = 29;
121
122 if (tim_p->tm_mday <= 0)
123 {
124 while (tim_p->tm_mday <= 0)
125 {
126 if (--tim_p->tm_mon == -1)
127 {
128 tim_p->tm_year--;
129 tim_p->tm_mon = 11;
130 days_in_feb =
131 ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
132 29 : 28);
133 }
134 tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon);
135 }
136 }
137 else
138 {
139 while (tim_p->tm_mday > _DAYS_IN_MONTH (tim_p->tm_mon))
140 {
141 tim_p->tm_mday -= _DAYS_IN_MONTH (tim_p->tm_mon);
142 if (++tim_p->tm_mon == 12)
143 {
144 tim_p->tm_year++;
145 tim_p->tm_mon = 0;
146 days_in_feb =
147 ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ?
148 29 : 28);
149 }
150 }
151 }
152 }
153
154 time_t
mktime(tim_p)155 mktime (tim_p)
156 struct tm *tim_p;
157 {
158 time_t tim = 0;
159 long days = 0;
160 int year, isdst;
161
162 /* validate structure */
163 validate_structure (tim_p);
164
165 /* compute hours, minutes, seconds */
166 tim += tim_p->tm_sec + (tim_p->tm_min * _SEC_IN_MINUTE) +
167 (tim_p->tm_hour * _SEC_IN_HOUR);
168
169 /* compute days in year */
170 days += tim_p->tm_mday - 1;
171 days += _DAYS_BEFORE_MONTH[tim_p->tm_mon];
172 if (tim_p->tm_mon > 1 && _DAYS_IN_YEAR (tim_p->tm_year) == 366)
173 days++;
174
175 /* compute day of the year */
176 tim_p->tm_yday = days;
177
178 if (tim_p->tm_year > 10000
179 || tim_p->tm_year < -10000)
180 {
181 return (time_t) -1;
182 }
183
184 /* compute days in other years */
185 if (tim_p->tm_year > 70)
186 {
187 for (year = 70; year < tim_p->tm_year; year++)
188 days += _DAYS_IN_YEAR (year);
189 }
190 else if (tim_p->tm_year < 70)
191 {
192 for (year = 69; year > tim_p->tm_year; year--)
193 days -= _DAYS_IN_YEAR (year);
194 days -= _DAYS_IN_YEAR (year);
195 }
196
197 /* compute day of the week */
198 if ((tim_p->tm_wday = (days + 4) % 7) < 0)
199 tim_p->tm_wday += 7;
200
201 /* compute total seconds */
202 tim += (days * _SEC_IN_DAY);
203
204 isdst = tim_p->tm_isdst;
205
206 if (_daylight)
207 {
208 int y = tim_p->tm_year + YEAR_BASE;
209 if (y == __tzyear || __tzcalc_limits (y))
210 {
211 /* calculate start of dst in dst local time and
212 start of std in both std local time and dst local time */
213 time_t startdst_dst = __tzrule[0].change - __tzrule[1].offset;
214 time_t startstd_dst = __tzrule[1].change - __tzrule[1].offset;
215 time_t startstd_std = __tzrule[1].change - __tzrule[0].offset;
216 /* if the time is in the overlap between dst and std local times */
217 if (tim >= startstd_std && tim < startstd_dst)
218 ; /* we let user decide or leave as -1 */
219 else
220 {
221 isdst = (__tznorth
222 ? (tim >= startdst_dst && tim < startstd_std)
223 : (tim >= startdst_dst || tim < startstd_std));
224 /* if user committed and was wrong, perform correction */
225 if ((isdst ^ tim_p->tm_isdst) == 1)
226 {
227 /* we either subtract or add the difference between
228 time zone offsets, depending on which way the user got it wrong */
229 int diff = __tzrule[0].offset - __tzrule[1].offset;
230 if (!isdst)
231 diff = -diff;
232 tim_p->tm_sec += diff;
233 validate_structure (tim_p);
234 tim += diff; /* we also need to correct our current time calculation */
235 }
236 }
237 }
238 }
239
240 /* add appropriate offset to put time in gmt format */
241 if (isdst == 1)
242 tim += __tzrule[1].offset;
243 else /* otherwise assume std time */
244 tim += __tzrule[0].offset;
245
246 /* reset isdst flag to what we have calculated */
247 tim_p->tm_isdst = isdst;
248
249 return tim;
250 }
251