1 /*
2  * mktm_r.c
3  * Original Author:	Adapted from tzcode maintained by Arthur David Olson.
4  * Modifications:       Changed to mktm_r and added __tzcalc_limits - 04/10/02, Jeff Johnston
5  *
6  * Converts the calendar time pointed to by tim_p into a broken-down time
7  * expressed as local time. Returns a pointer to a structure containing the
8  * broken-down time.
9  */
10 
11 #include <stdlib.h>
12 #include <time.h>
13 #include "local.h"
14 
15 static _CONST int mon_lengths[2][MONSPERYEAR] = {
16   {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
17   {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
18 } ;
19 
20 static _CONST int year_lengths[2] = {
21   365,
22   366
23 } ;
24 
25 struct tm *
26 _DEFUN (_mktm_r, (tim_p, res, is_gmtime),
27 	_CONST time_t * tim_p _AND
28 	struct tm *res _AND
29 	int is_gmtime)
30 {
31   long days, rem;
32   time_t lcltime;
33   int i;
34   int y;
35   int yleap;
36   _CONST int *ip;
37 
38   /* base decision about std/dst time on current time */
39   lcltime = *tim_p;
40 
41   days = ((long)lcltime) / SECSPERDAY;
42   rem = ((long)lcltime) % SECSPERDAY;
43   while (rem < 0)
44     {
45       rem += SECSPERDAY;
46       --days;
47     }
48   while (rem >= SECSPERDAY)
49     {
50       rem -= SECSPERDAY;
51       ++days;
52     }
53 
54   /* compute hour, min, and sec */
55   res->tm_hour = (int) (rem / SECSPERHOUR);
56   rem %= SECSPERHOUR;
57   res->tm_min = (int) (rem / SECSPERMIN);
58   res->tm_sec = (int) (rem % SECSPERMIN);
59 
60   /* compute day of week */
61   if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
62     res->tm_wday += DAYSPERWEEK;
63 
64   /* compute year & day of year */
65   y = EPOCH_YEAR;
66   if (days >= 0)
67     {
68       for (;;)
69 	{
70 	  yleap = isleap(y);
71 	  if (days < year_lengths[yleap])
72 	    break;
73 	  y++;
74 	  days -= year_lengths[yleap];
75 	}
76     }
77   else
78     {
79       do
80 	{
81 	  --y;
82 	  yleap = isleap(y);
83 	  days += year_lengths[yleap];
84 	} while (days < 0);
85     }
86 
87   res->tm_year = y - YEAR_BASE;
88   res->tm_yday = days;
89   ip = mon_lengths[yleap];
90   for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon)
91     days -= ip[res->tm_mon];
92   res->tm_mday = days + 1;
93 
94   if (!is_gmtime)
95     {
96       int offset;
97       int hours, mins, secs;
98 
99       TZ_LOCK;
100       if (_daylight)
101 	{
102 	  if (y == __tzyear || __tzcalc_limits (y))
103 	    res->tm_isdst = (__tznorth
104 			     ? (*tim_p >= __tzrule[0].change && *tim_p < __tzrule[1].change)
105 			     : (*tim_p >= __tzrule[0].change || *tim_p < __tzrule[1].change));
106 	  else
107 	    res->tm_isdst = -1;
108 	}
109       else
110 	res->tm_isdst = 0;
111 
112       offset = (res->tm_isdst == 1 ? __tzrule[1].offset : __tzrule[0].offset);
113 
114       hours = offset / SECSPERHOUR;
115       offset = offset % SECSPERHOUR;
116 
117       mins = offset / SECSPERMIN;
118       secs = offset % SECSPERMIN;
119 
120       res->tm_sec -= secs;
121       res->tm_min -= mins;
122       res->tm_hour -= hours;
123 
124       if (res->tm_sec >= SECSPERMIN)
125 	{
126 	  res->tm_min += 1;
127 	  res->tm_sec -= SECSPERMIN;
128 	}
129       else if (res->tm_sec < 0)
130 	{
131 	  res->tm_min -= 1;
132 	  res->tm_sec += SECSPERMIN;
133 	}
134       if (res->tm_min >= MINSPERHOUR)
135 	{
136 	  res->tm_hour += 1;
137 	  res->tm_min -= MINSPERHOUR;
138 	}
139       else if (res->tm_min < 0)
140 	{
141 	  res->tm_hour -= 1;
142 	  res->tm_min += MINSPERHOUR;
143 	}
144       if (res->tm_hour >= HOURSPERDAY)
145 	{
146 	  ++res->tm_yday;
147 	  ++res->tm_wday;
148 	  if (res->tm_wday > 6)
149 	    res->tm_wday = 0;
150 	  ++res->tm_mday;
151 	  res->tm_hour -= HOURSPERDAY;
152 	  if (res->tm_mday >= ip[res->tm_mon])
153 	    {
154 	      res->tm_mday -= ip[res->tm_mon] - 1;
155 	      res->tm_mon += 1;
156 	      if (res->tm_mon == 12)
157 		{
158 		  res->tm_mon = 0;
159 		  res->tm_year += 1;
160 		  res->tm_yday = 0;
161 		}
162 	    }
163 	}
164        else if (res->tm_hour < 0)
165 	{
166 	  res->tm_yday -= 1;
167 	  res->tm_wday -= 1;
168 	  if (res->tm_wday < 0)
169 	    res->tm_wday = 6;
170 	  res->tm_mday -= 1;
171 	  res->tm_hour += 24;
172 	  if (res->tm_mday == 0)
173 	    {
174 	      res->tm_mon -= 1;
175 	      if (res->tm_mon < 0)
176 		{
177 		  res->tm_mon = 11;
178 		  res->tm_year -= 1;
179 		  res->tm_yday = 365 + isleap(res->tm_year);
180 		}
181 	      res->tm_mday = ip[res->tm_mon];
182 	    }
183 	}
184       TZ_UNLOCK;
185     }
186   else
187     res->tm_isdst = 0;
188 
189   return (res);
190 }
191 
192 int
193 _DEFUN (__tzcalc_limits, (year),
194 	int year)
195 {
196   int days, year_days, years;
197   int i, j;
198 
199   if (year < EPOCH_YEAR)
200     return 0;
201 
202   __tzyear = year;
203 
204   years = (year - EPOCH_YEAR);
205 
206   year_days = years * 365 +
207     (years - 1 + EPOCH_YEARS_SINCE_LEAP) / 4 - (years - 1 + EPOCH_YEARS_SINCE_CENTURY) / 100 +
208     (years - 1 + EPOCH_YEARS_SINCE_LEAP_CENTURY) / 400;
209 
210   for (i = 0; i < 2; ++i)
211     {
212       if (__tzrule[i].ch == 'J')
213 	days = year_days + __tzrule[i].d + (isleap(year) && __tzrule[i].d >= 60);
214       else if (__tzrule[i].ch == 'D')
215 	days = year_days + __tzrule[i].d;
216       else
217 	{
218 	  int yleap = isleap(year);
219 	  int m_day, m_wday, wday_diff;
220 	  _CONST int *ip = mon_lengths[yleap];
221 
222 	  days = year_days;
223 
224 	  for (j = 1; j < __tzrule[i].m; ++j)
225 	    days += ip[j-1];
226 
227 	  m_wday = (EPOCH_WDAY + days) % DAYSPERWEEK;
228 
229 	  wday_diff = __tzrule[i].d - m_wday;
230 	  if (wday_diff < 0)
231 	    wday_diff += DAYSPERWEEK;
232 	  m_day = (__tzrule[i].n - 1) * DAYSPERWEEK + wday_diff;
233 
234 	  while (m_day >= ip[j])
235 	    m_day -= DAYSPERWEEK;
236 
237 	  days += m_day;
238 	}
239 
240       /* store the change-over time in GMT form by adding offset */
241       __tzrule[i].change = days * SECSPERDAY + __tzrule[i].s + __tzrule[i].offset;
242     }
243 
244   __tznorth = (__tzrule[0].change < __tzrule[1].change);
245 
246   return 1;
247 }
248 
249