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