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 | * Modifications: Fixed tm_isdst usage - 27 August 2008 Craig Howland. |
---|
14 | */ |
---|
15 | |
---|
16 | /* |
---|
17 | FUNCTION |
---|
18 | <<mktime>>---convert time to arithmetic representation |
---|
19 | |
---|
20 | INDEX |
---|
21 | mktime |
---|
22 | |
---|
23 | SYNOPSIS |
---|
24 | #include <time.h> |
---|
25 | time_t mktime(struct tm *<[timp]>); |
---|
26 | |
---|
27 | DESCRIPTION |
---|
28 | <<mktime>> assumes the time at <[timp]> is a local time, and converts |
---|
29 | its representation from the traditional representation defined by |
---|
30 | <<struct tm>> into a representation suitable for arithmetic. |
---|
31 | |
---|
32 | <<localtime>> is the inverse of <<mktime>>. |
---|
33 | |
---|
34 | RETURNS |
---|
35 | If the contents of the structure at <[timp]> do not form a valid |
---|
36 | calendar time representation, the result is <<-1>>. Otherwise, the |
---|
37 | result is the time, converted to a <<time_t>> value. |
---|
38 | |
---|
39 | PORTABILITY |
---|
40 | ANSI C requires <<mktime>>. |
---|
41 | |
---|
42 | <<mktime>> requires no supporting OS subroutines. |
---|
43 | */ |
---|
44 | |
---|
45 | #include <stdlib.h> |
---|
46 | #include <time.h> |
---|
47 | #include "local.h" |
---|
48 | |
---|
49 | #define _SEC_IN_MINUTE 60L |
---|
50 | #define _SEC_IN_HOUR 3600L |
---|
51 | #define _SEC_IN_DAY 86400L |
---|
52 | |
---|
53 | static const int DAYS_IN_MONTH[12] = |
---|
54 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
---|
55 | |
---|
56 | #define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : DAYS_IN_MONTH[x]) |
---|
57 | |
---|
58 | static const int _DAYS_BEFORE_MONTH[12] = |
---|
59 | {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; |
---|
60 | |
---|
61 | #define _ISLEAP(y) (((y) % 4) == 0 && (((y) % 100) != 0 || (((y)+1900) % 400) == 0)) |
---|
62 | #define _DAYS_IN_YEAR(year) (_ISLEAP(year) ? 366 : 365) |
---|
63 | |
---|
64 | static void |
---|
65 | validate_structure (struct tm *tim_p) |
---|
66 | { |
---|
67 | div_t res; |
---|
68 | int days_in_feb = 28; |
---|
69 | |
---|
70 | /* calculate time & date to account for out of range values */ |
---|
71 | if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59) |
---|
72 | { |
---|
73 | res = div (tim_p->tm_sec, 60); |
---|
74 | tim_p->tm_min += res.quot; |
---|
75 | if ((tim_p->tm_sec = res.rem) < 0) |
---|
76 | { |
---|
77 | tim_p->tm_sec += 60; |
---|
78 | --tim_p->tm_min; |
---|
79 | } |
---|
80 | } |
---|
81 | |
---|
82 | if (tim_p->tm_min < 0 || tim_p->tm_min > 59) |
---|
83 | { |
---|
84 | res = div (tim_p->tm_min, 60); |
---|
85 | tim_p->tm_hour += res.quot; |
---|
86 | if ((tim_p->tm_min = res.rem) < 0) |
---|
87 | { |
---|
88 | tim_p->tm_min += 60; |
---|
89 | --tim_p->tm_hour; |
---|
90 | } |
---|
91 | } |
---|
92 | |
---|
93 | if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23) |
---|
94 | { |
---|
95 | res = div (tim_p->tm_hour, 24); |
---|
96 | tim_p->tm_mday += res.quot; |
---|
97 | if ((tim_p->tm_hour = res.rem) < 0) |
---|
98 | { |
---|
99 | tim_p->tm_hour += 24; |
---|
100 | --tim_p->tm_mday; |
---|
101 | } |
---|
102 | } |
---|
103 | |
---|
104 | if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11) |
---|
105 | { |
---|
106 | res = div (tim_p->tm_mon, 12); |
---|
107 | tim_p->tm_year += res.quot; |
---|
108 | if ((tim_p->tm_mon = res.rem) < 0) |
---|
109 | { |
---|
110 | tim_p->tm_mon += 12; |
---|
111 | --tim_p->tm_year; |
---|
112 | } |
---|
113 | } |
---|
114 | |
---|
115 | if (_DAYS_IN_YEAR (tim_p->tm_year) == 366) |
---|
116 | days_in_feb = 29; |
---|
117 | |
---|
118 | if (tim_p->tm_mday <= 0) |
---|
119 | { |
---|
120 | while (tim_p->tm_mday <= 0) |
---|
121 | { |
---|
122 | if (--tim_p->tm_mon == -1) |
---|
123 | { |
---|
124 | tim_p->tm_year--; |
---|
125 | tim_p->tm_mon = 11; |
---|
126 | days_in_feb = |
---|
127 | ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ? |
---|
128 | 29 : 28); |
---|
129 | } |
---|
130 | tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon); |
---|
131 | } |
---|
132 | } |
---|
133 | else |
---|
134 | { |
---|
135 | while (tim_p->tm_mday > _DAYS_IN_MONTH (tim_p->tm_mon)) |
---|
136 | { |
---|
137 | tim_p->tm_mday -= _DAYS_IN_MONTH (tim_p->tm_mon); |
---|
138 | if (++tim_p->tm_mon == 12) |
---|
139 | { |
---|
140 | tim_p->tm_year++; |
---|
141 | tim_p->tm_mon = 0; |
---|
142 | days_in_feb = |
---|
143 | ((_DAYS_IN_YEAR (tim_p->tm_year) == 366) ? |
---|
144 | 29 : 28); |
---|
145 | } |
---|
146 | } |
---|
147 | } |
---|
148 | } |
---|
149 | |
---|
150 | time_t |
---|
151 | mktime (struct tm *tim_p) |
---|
152 | { |
---|
153 | time_t tim = 0; |
---|
154 | long days = 0; |
---|
155 | int year, isdst=0; |
---|
156 | __tzinfo_type *tz = __gettzinfo (); |
---|
157 | |
---|
158 | /* validate structure */ |
---|
159 | validate_structure (tim_p); |
---|
160 | |
---|
161 | /* compute hours, minutes, seconds */ |
---|
162 | tim += tim_p->tm_sec + (tim_p->tm_min * _SEC_IN_MINUTE) + |
---|
163 | (tim_p->tm_hour * _SEC_IN_HOUR); |
---|
164 | |
---|
165 | /* compute days in year */ |
---|
166 | days += tim_p->tm_mday - 1; |
---|
167 | days += _DAYS_BEFORE_MONTH[tim_p->tm_mon]; |
---|
168 | if (tim_p->tm_mon > 1 && _DAYS_IN_YEAR (tim_p->tm_year) == 366) |
---|
169 | days++; |
---|
170 | |
---|
171 | /* compute day of the year */ |
---|
172 | tim_p->tm_yday = days; |
---|
173 | |
---|
174 | if (tim_p->tm_year > 10000 || tim_p->tm_year < -10000) |
---|
175 | return (time_t) -1; |
---|
176 | |
---|
177 | /* compute days in other years */ |
---|
178 | if ((year = tim_p->tm_year) > 70) |
---|
179 | { |
---|
180 | for (year = 70; year < tim_p->tm_year; year++) |
---|
181 | days += _DAYS_IN_YEAR (year); |
---|
182 | } |
---|
183 | else if (year < 70) |
---|
184 | { |
---|
185 | for (year = 69; year > tim_p->tm_year; year--) |
---|
186 | days -= _DAYS_IN_YEAR (year); |
---|
187 | days -= _DAYS_IN_YEAR (year); |
---|
188 | } |
---|
189 | |
---|
190 | /* compute total seconds */ |
---|
191 | tim += (days * _SEC_IN_DAY); |
---|
192 | |
---|
193 | TZ_LOCK; |
---|
194 | |
---|
195 | _tzset_unlocked (); |
---|
196 | |
---|
197 | if (_daylight) |
---|
198 | { |
---|
199 | int tm_isdst; |
---|
200 | int y = tim_p->tm_year + YEAR_BASE; |
---|
201 | /* Convert user positive into 1 */ |
---|
202 | tm_isdst = tim_p->tm_isdst > 0 ? 1 : tim_p->tm_isdst; |
---|
203 | isdst = tm_isdst; |
---|
204 | |
---|
205 | if (y == tz->__tzyear || __tzcalc_limits (y)) |
---|
206 | { |
---|
207 | /* calculate start of dst in dst local time and |
---|
208 | start of std in both std local time and dst local time */ |
---|
209 | time_t startdst_dst = tz->__tzrule[0].change |
---|
210 | - (time_t) tz->__tzrule[1].offset; |
---|
211 | time_t startstd_dst = tz->__tzrule[1].change |
---|
212 | - (time_t) tz->__tzrule[1].offset; |
---|
213 | time_t startstd_std = tz->__tzrule[1].change |
---|
214 | - (time_t) tz->__tzrule[0].offset; |
---|
215 | /* if the time is in the overlap between dst and std local times */ |
---|
216 | if (tim >= startstd_std && tim < startstd_dst) |
---|
217 | ; /* we let user decide or leave as -1 */ |
---|
218 | else |
---|
219 | { |
---|
220 | isdst = (tz->__tznorth |
---|
221 | ? (tim >= startdst_dst && tim < startstd_std) |
---|
222 | : (tim >= startdst_dst || tim < startstd_std)); |
---|
223 | /* if user committed and was wrong, perform correction, but not |
---|
224 | * if the user has given a negative value (which |
---|
225 | * asks mktime() to determine if DST is in effect or not) */ |
---|
226 | if (tm_isdst >= 0 && (isdst ^ tm_isdst) == 1) |
---|
227 | { |
---|
228 | /* we either subtract or add the difference between |
---|
229 | time zone offsets, depending on which way the user got it |
---|
230 | wrong. The diff is typically one hour, or 3600 seconds, |
---|
231 | and should fit in a 16-bit int, even though offset |
---|
232 | is a long to accomodate 12 hours. */ |
---|
233 | int diff = (int) (tz->__tzrule[0].offset |
---|
234 | - tz->__tzrule[1].offset); |
---|
235 | if (!isdst) |
---|
236 | diff = -diff; |
---|
237 | tim_p->tm_sec += diff; |
---|
238 | tim += diff; /* we also need to correct our current time calculation */ |
---|
239 | int mday = tim_p->tm_mday; |
---|
240 | validate_structure (tim_p); |
---|
241 | mday = tim_p->tm_mday - mday; |
---|
242 | /* roll over occurred */ |
---|
243 | if (mday) { |
---|
244 | /* compensate for month roll overs */ |
---|
245 | if (mday > 1) |
---|
246 | mday = -1; |
---|
247 | else if (mday < -1) |
---|
248 | mday = 1; |
---|
249 | /* update days for wday calculation */ |
---|
250 | days += mday; |
---|
251 | /* handle yday */ |
---|
252 | if ((tim_p->tm_yday += mday) < 0) { |
---|
253 | --year; |
---|
254 | tim_p->tm_yday = _DAYS_IN_YEAR(year) - 1; |
---|
255 | } else { |
---|
256 | mday = _DAYS_IN_YEAR(year); |
---|
257 | if (tim_p->tm_yday > (mday - 1)) |
---|
258 | tim_p->tm_yday -= mday; |
---|
259 | } |
---|
260 | } |
---|
261 | } |
---|
262 | } |
---|
263 | } |
---|
264 | } |
---|
265 | |
---|
266 | /* add appropriate offset to put time in gmt format */ |
---|
267 | if (isdst == 1) |
---|
268 | tim += (time_t) tz->__tzrule[1].offset; |
---|
269 | else /* otherwise assume std time */ |
---|
270 | tim += (time_t) tz->__tzrule[0].offset; |
---|
271 | |
---|
272 | TZ_UNLOCK; |
---|
273 | |
---|
274 | /* reset isdst flag to what we have calculated */ |
---|
275 | tim_p->tm_isdst = isdst; |
---|
276 | |
---|
277 | /* compute day of the week */ |
---|
278 | if ((tim_p->tm_wday = (days + 4) % 7) < 0) |
---|
279 | tim_p->tm_wday += 7; |
---|
280 | |
---|
281 | return tim; |
---|
282 | } |
---|