Formerly time/time.h.~10~
[kopensolaris-gnu/glibc.git] / time / mktime.c
1 /* Copyright (C) 1991 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB.  If
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, USA.  */
18
19 #include <ansidecl.h>
20 #include <errno.h>
21 #include <limits.h>
22 #include <time.h>
23
24
25 /* Defined in offtime.c.  */
26 extern CONST unsigned short int __mon_lengths[2][12];
27
28
29 #define invalid()       return (time_t) -1
30
31 /* Return the `time_t' representation of TP and normalizes TP.
32    Return (time_t) -1 if TP is not representable as a `time_t'.
33    Note that 31 Dec 1969 23:59:59 is not representable
34    because it is represented as (time_t) -1.  */
35 time_t
36 DEFUN(mktime, (tp), register struct tm *tp)
37 {
38   static struct tm min, max;
39   static char init = 0;
40
41   register time_t result;
42   register time_t t;
43   register int i;
44   register CONST unsigned short *l;
45   register struct tm *new;
46   time_t end;
47
48   if (tp == NULL)
49     {
50       errno = EINVAL;
51       invalid();
52     }
53
54   if (!init)
55     {
56       init = 1;
57       end = (time_t) LONG_MIN;
58       new = gmtime(&end);
59       if (new != NULL)
60         min = *new;
61       else
62         min.tm_sec = min.tm_min = min.tm_hour =
63           min.tm_mday = min.tm_mon = min.tm_year = INT_MIN;
64
65       end = (time_t) LONG_MAX;
66       new = gmtime(&end);
67       if (new != NULL)
68         max = *new;
69       else
70         max.tm_sec = max.tm_min = max.tm_hour =
71           max.tm_mday = max.tm_mon = max.tm_year = INT_MAX;
72     }
73
74   /* Make all the elements of TP that we pay attention to
75      be within the ranges of reasonable values for those things.  */
76 #define normalize(elt, min, max, nextelt) \
77   while (tp->elt < min)                                                       \
78     {                                                                         \
79       --tp->nextelt;                                                          \
80       tp->elt += max + 1;                                                     \
81     }                                                                         \
82   while (tp->elt > max)                                                       \
83     {                                                                         \
84       ++tp->nextelt;                                                          \
85       tp->elt -= max + 1;                                                     \
86     }
87
88   normalize (tm_sec, 0, 59, tm_min);
89   normalize (tm_min, 0, 59, tm_hour);
90   normalize (tm_hour, 0, 24, tm_mday);
91
92   /* Normalize the month first so we can use
93      it to figure the range for the day.  */
94   normalize (tm_mon, 0, 11, tm_year);
95   normalize (tm_mday, 1, __mon_lengths[__isleap (tp->tm_year)][tp->tm_mon],
96              tm_mon);
97
98   /* Normalize the month again, since normalizing
99      the day may have pushed it out of range.  */
100   normalize (tm_mon, 0, 11, tm_year);
101
102   /* Normalize the day again, because normalizing
103      the month may have changed the range.  */
104   normalize (tm_mday, 1, __mon_lengths[__isleap (tp->tm_year)][tp->tm_mon],
105              tm_mon);
106
107   /* Check for out-of-range values.  */
108 #define lowhigh(field, minmax, cmp)     (tp->field cmp minmax.field)
109 #define low(field)                      lowhigh(field, min, <)
110 #define high(field)                     lowhigh(field, max, >)
111 #define oor(field)                      (low(field) || high(field))
112 #define lowbound(field)                 (tp->field == min.field)
113 #define highbound(field)                (tp->field == max.field)
114   if (oor(tm_year))
115     invalid();
116   else if (lowbound(tm_year))
117     {
118       if (low(tm_mon))
119         invalid();
120       else if (lowbound(tm_mon))
121         {
122           if (low(tm_mday))
123             invalid();
124           else if (lowbound(tm_mday))
125             {
126               if (low(tm_hour))
127                 invalid();
128               else if (lowbound(tm_hour))
129                 {
130                   if (low(tm_min))
131                     invalid();
132                   else if (lowbound(tm_min))
133                     {
134                       if (low(tm_sec))
135                         invalid();
136                     }
137                 }
138             }
139         }
140     }
141   else if (highbound(tm_year))
142     {
143       if (high(tm_mon))
144         invalid();
145       else if (highbound(tm_mon))
146         {
147           if (high(tm_mday))
148             invalid();
149           else if (highbound(tm_mday))
150             {
151               if (high(tm_hour))
152                 invalid();
153               else if (highbound(tm_hour))
154                 {
155                   if (high(tm_min))
156                     invalid();
157                   else if (highbound(tm_min))
158                     {
159                       if (high(tm_sec))
160                         invalid();
161                     }
162                 }
163             }
164         }
165     }
166
167   t = 0;
168   for (i = 1970; i > 1900 + tp->tm_year; --i)
169     t -= __isleap(i) ? 366 : 365;
170   for (i = 1970; i < 1900 + tp->tm_year; ++i)
171     t += __isleap(i) ? 366 : 365;
172   l = __mon_lengths[__isleap(1900 + tp->tm_year)];
173   for (i = 0; i < tp->tm_mon; ++i)
174     t += l[i];
175   t += tp->tm_mday - 1;
176   result = ((t * 60 * 60 * 24) +
177             (tp->tm_hour * 60 * 60) +
178             (tp->tm_min * 60) +
179             tp->tm_sec);
180
181   end = result;
182   if (tp->tm_isdst < 0)
183     new = localtime(&end);
184   else
185     new = gmtime(&end);
186   if (new == NULL)
187     invalid();
188   new->tm_isdst = tp->tm_isdst;
189   *tp = *new;
190
191   return result;
192 }