Cygwin: mmap: allow remapping part of an existing anonymous mapping
[newlib-cygwin.git] / newlib / libc / time / mktime.c
blob02032599a0a322b30a362f90ec1454185f4cb132
1 /*
2 * mktime.c
3 * Original Author: G. Haley
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.
13 * Modifications: Fixed tm_isdst usage - 27 August 2008 Craig Howland.
17 FUNCTION
18 <<mktime>>---convert time to arithmetic representation
20 INDEX
21 mktime
23 SYNOPSIS
24 #include <time.h>
25 time_t mktime(struct tm *<[timp]>);
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.
32 <<localtime>> is the inverse of <<mktime>>.
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.
39 PORTABILITY
40 ANSI C requires <<mktime>>.
42 <<mktime>> requires no supporting OS subroutines.
45 #include <stdlib.h>
46 #include <time.h>
47 #include "local.h"
49 #define _SEC_IN_MINUTE 60L
50 #define _SEC_IN_HOUR 3600L
51 #define _SEC_IN_DAY 86400L
53 static const int DAYS_IN_MONTH[12] =
54 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
56 #define _DAYS_IN_MONTH(x) ((x == 1) ? days_in_feb : DAYS_IN_MONTH[x])
58 static const int _DAYS_BEFORE_MONTH[12] =
59 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
61 #define _ISLEAP(y) (((y) % 4) == 0 && (((y) % 100) != 0 || (((y)+1900) % 400) == 0))
62 #define _DAYS_IN_YEAR(year) (_ISLEAP(year) ? 366 : 365)
64 static void
65 validate_structure (struct tm *tim_p)
67 div_t res;
68 int days_in_feb = 28;
70 /* calculate time & date to account for out of range values */
71 if (tim_p->tm_sec < 0 || tim_p->tm_sec > 59)
73 res = div (tim_p->tm_sec, 60);
74 tim_p->tm_min += res.quot;
75 if ((tim_p->tm_sec = res.rem) < 0)
77 tim_p->tm_sec += 60;
78 --tim_p->tm_min;
82 if (tim_p->tm_min < 0 || tim_p->tm_min > 59)
84 res = div (tim_p->tm_min, 60);
85 tim_p->tm_hour += res.quot;
86 if ((tim_p->tm_min = res.rem) < 0)
88 tim_p->tm_min += 60;
89 --tim_p->tm_hour;
93 if (tim_p->tm_hour < 0 || tim_p->tm_hour > 23)
95 res = div (tim_p->tm_hour, 24);
96 tim_p->tm_mday += res.quot;
97 if ((tim_p->tm_hour = res.rem) < 0)
99 tim_p->tm_hour += 24;
100 --tim_p->tm_mday;
104 if (tim_p->tm_mon < 0 || tim_p->tm_mon > 11)
106 res = div (tim_p->tm_mon, 12);
107 tim_p->tm_year += res.quot;
108 if ((tim_p->tm_mon = res.rem) < 0)
110 tim_p->tm_mon += 12;
111 --tim_p->tm_year;
115 if (_DAYS_IN_YEAR (tim_p->tm_year) == 366)
116 days_in_feb = 29;
118 if (tim_p->tm_mday <= 0)
120 while (tim_p->tm_mday <= 0)
122 if (--tim_p->tm_mon == -1)
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);
130 tim_p->tm_mday += _DAYS_IN_MONTH (tim_p->tm_mon);
133 else
135 while (tim_p->tm_mday > _DAYS_IN_MONTH (tim_p->tm_mon))
137 tim_p->tm_mday -= _DAYS_IN_MONTH (tim_p->tm_mon);
138 if (++tim_p->tm_mon == 12)
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);
150 time_t
151 mktime (struct tm *tim_p)
153 time_t tim = 0;
154 long days = 0;
155 int year, isdst=0;
156 __tzinfo_type *tz = __gettzinfo ();
158 /* validate structure */
159 validate_structure (tim_p);
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);
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++;
171 /* compute day of the year */
172 tim_p->tm_yday = days;
174 if (tim_p->tm_year > 10000 || tim_p->tm_year < -10000)
175 return (time_t) -1;
177 /* compute days in other years */
178 if ((year = tim_p->tm_year) > 70)
180 for (year = 70; year < tim_p->tm_year; year++)
181 days += _DAYS_IN_YEAR (year);
183 else if (year < 70)
185 for (year = 69; year > tim_p->tm_year; year--)
186 days -= _DAYS_IN_YEAR (year);
187 days -= _DAYS_IN_YEAR (year);
190 /* compute total seconds */
191 tim += (time_t)days * _SEC_IN_DAY;
193 TZ_LOCK;
195 _tzset_unlocked ();
197 if (_daylight)
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;
205 if (y == tz->__tzyear || __tzcalc_limits (y))
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
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)
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;
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;
272 TZ_UNLOCK;
274 /* reset isdst flag to what we have calculated */
275 tim_p->tm_isdst = isdst;
277 /* compute day of the week */
278 if ((tim_p->tm_wday = (days + 4) % 7) < 0)
279 tim_p->tm_wday += 7;
281 return tim;