source: trunk/libs/newlib/src/newlib/libc/time/mktime.c @ 450

Last change on this file since 450 was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

File size: 7.3 KB
Line 
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/*
17FUNCTION
18<<mktime>>---convert time to arithmetic representation
19
20INDEX
21        mktime
22
23SYNOPSIS
24        #include <time.h>
25        time_t mktime(struct tm *<[timp]>);
26
27DESCRIPTION
28<<mktime>> assumes the time at <[timp]> is a local time, and converts
29its 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
34RETURNS
35If the contents of the structure at <[timp]> do not form a valid
36calendar time representation, the result is <<-1>>.  Otherwise, the
37result is the time, converted to a <<time_t>> value.
38
39PORTABILITY
40ANSI 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
53static 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
58static 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
64static void 
65validate_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
150time_t 
151mktime (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}
Note: See TracBrowser for help on using the repository browser.