Remove errant comment end sequence.
[kopensolaris-gnu/glibc.git] / time / mktime.c
1 /* Copyright (C) 1993, 1994 Free Software Foundation, Inc.
2    Contributed by Noel Cragg (noel@cs.oberlin.edu), with fixes by
3    Michael E. Calwas (calwas@ttd.teradyne.com) and
4    Wade Hampton (tasi029@tmn.com).
5
6 This file is part of the GNU C Library.
7
8 The GNU C Library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
12
13 The GNU C Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public
19 License along with the GNU C Library; see the file COPYING.LIB.  If
20 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
21 Cambridge, MA 02139, USA.  */
22
23 /* Define this to have a standalone program to test this implementation of
24    mktime.  */
25 /* #define DEBUG */
26
27 #ifdef HAVE_CONFIG_H
28 #if defined (CONFIG_BROKETS)
29 /* We use <config.h> instead of "config.h" so that a compilation
30    using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
31    (which it would do because it found this file in $srcdir).  */
32 #include <config.h>
33 #else
34 #include "config.h"
35 #endif
36 #endif
37
38 #include <sys/types.h>          /* Some systems define `time_t' here.  */
39 #include <time.h>
40
41
42 #ifndef __isleap
43 /* Nonzero if YEAR is a leap year (every 4 years,
44    except every 100th isn't, and every 400th is).  */
45 #define __isleap(year)  \
46   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
47 #endif
48
49 #ifndef __P
50 #if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
51 #define __P(args) args
52 #else
53 #define __P(args) ()
54 #endif  /* GCC.  */
55 #endif  /* Not __P.  */
56
57 /* How many days are in each month.  */
58 const unsigned short int __mon_lengths[2][12] =
59   {
60     /* Normal years.  */
61     { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
62     /* Leap years.  */
63     { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
64   };
65
66
67 static int times_through_search; /* This library routine should never
68                                     hang -- make sure we always return
69                                     when we're searching for a value */
70
71
72 #ifdef DEBUG
73
74 #include <stdio.h>
75 #include <ctype.h>
76
77 int debugging_enabled = 0;
78
79 /* Print the values in a `struct tm'. */
80 static void
81 printtm (it)
82      struct tm *it;
83 {
84   printf ("%02d/%02d/%04d %02d:%02d:%02d (%s) yday:%03d dst:%d gmtoffset:%ld",
85           it->tm_mon + 1,
86           it->tm_mday,
87           it->tm_year + 1900,
88           it->tm_hour,
89           it->tm_min,
90           it->tm_sec,
91           it->tm_zone,
92           it->tm_yday,
93           it->tm_isdst,
94           it->tm_gmtoff);
95 }
96 #endif
97
98
99 static time_t
100 dist_tm (t1, t2)
101      struct tm *t1;
102      struct tm *t2;
103 {
104   time_t distance = 0;
105   unsigned long int v1, v2;
106   int diff_flag = 0;
107
108   v1 = v2 = 0;
109
110 #define doit(x, secs)                                                         \
111   v1 += t1->x * secs;                                                         \
112   v2 += t2->x * secs;                                                         \
113   if (!diff_flag)                                                             \
114     {                                                                         \
115       if (t1->x < t2->x)                                                      \
116         diff_flag = -1;                                                       \
117       else if (t1->x > t2->x)                                                 \
118         diff_flag = 1;                                                        \
119     }
120   
121   doit (tm_year, 31536000);     /* Okay, not all years have 365 days. */
122   doit (tm_mon, 2592000);       /* Okay, not all months have 30 days. */
123   doit (tm_mday, 86400);
124   doit (tm_hour, 3600);
125   doit (tm_min, 60);
126   doit (tm_sec, 1);
127   
128 #undef doit
129   
130   /* We should also make sure that the sign of DISTANCE is correct -- if
131      DIFF_FLAG is positive, the distance should be positive and vice versa. */
132   
133   distance = (v1 > v2) ? (v1 - v2) : (v2 - v1);
134   if (diff_flag < 0)
135     distance = -distance;
136
137   if (times_through_search > 20) /* Arbitrary # of calls, but makes sure we
138                                     never hang if there's a problem with
139                                     this algorithm.  */
140     {
141       distance = diff_flag;
142     }
143
144   /* We need this DIFF_FLAG business because it is forseeable that the
145      distance may be zero when, in actuality, the two structures are
146      different.  This is usually the case when the dates are 366 days apart
147      and one of the years is a leap year.  */
148
149   if (distance == 0 && diff_flag)
150     distance = 86400 * diff_flag;
151
152   return distance;
153 }
154       
155
156 /* MKTIME converts the values in a struct tm to a time_t.  The values
157    in tm_wday and tm_yday are ignored; other values can be put outside
158    of legal ranges since they will be normalized.  This routine takes
159    care of that normalization. */
160
161 void
162 do_normalization (tmptr)
163      struct tm *tmptr;
164 {
165
166 #define normalize(foo,x,y,bar); \
167   while (tmptr->foo < x) \
168     { \
169       tmptr->bar--; \
170       tmptr->foo = (y - (x - tmptr->foo) + 1); \
171     } \
172   while (tmptr->foo > y) \
173     { \
174       tmptr->foo = (x + (tmptr->foo - y) - 1); \
175       tmptr->bar++; \
176     }
177   
178   normalize (tm_sec, 0, 59, tm_min);
179   normalize (tm_min, 0, 59, tm_hour);
180   normalize (tm_hour, 0, 23, tm_mday);
181   
182   /* Do the month first, so day range can be found. */
183   normalize (tm_mon, 0, 11, tm_year);
184
185   /* Since the day range modifies the month, we should be careful how
186      we reference the array of month lengths -- it is possible that
187      the month will go negative, hence the modulo...
188
189      Also, tm_year is the year - 1900, so we have to 1900 to have it
190      work correctly. */
191
192   normalize (tm_mday, 1,
193              __mon_lengths[__isleap (tmptr->tm_year + 1900)]
194                           [((tmptr->tm_mon < 0)
195                             ? (12 + (tmptr->tm_mon % 12))
196                             : (tmptr->tm_mon % 12)) ],
197              tm_mon);
198
199   /* Do the month again, because the day may have pushed it out of range. */
200   normalize (tm_mon, 0, 11, tm_year);
201
202   /* Do the day again, because the month may have changed the range. */
203   normalize (tm_mday, 1,
204              __mon_lengths[__isleap (tmptr->tm_year + 1900)]
205                           [((tmptr->tm_mon < 0)
206                             ? (12 + (tmptr->tm_mon % 12))
207                             : (tmptr->tm_mon % 12)) ],
208              tm_mon);
209   
210 #ifdef DEBUG
211   if (debugging_enabled)
212     {
213       printf ("   After normalizing:\n     ");
214       printtm (tmptr);
215       putchar ('\n');
216     }
217 #endif
218
219 }
220
221
222 /* Here's where the work gets done. */
223
224 #define BAD_STRUCT_TM ((time_t) -1)
225
226 time_t
227 _mktime_internal (timeptr, producer)
228      struct tm *timeptr;
229      struct tm *(*producer) __P ((const time_t *));
230 {
231   struct tm our_tm;             /* our working space */
232   struct tm *me = &our_tm;      /* a pointer to the above */
233   time_t result;                /* the value we return */
234
235   *me = *timeptr;               /* copy the struct tm that was passed
236                                    in by the caller */
237
238
239   /***************************/
240   /* Normalize the structure */
241   /***************************/
242
243   do_normalization (me);
244
245   /* Get out of here if it's not possible to represent this struct.
246      If any of the values in the normalized struct tm are negative,
247      our algorithms won't work.  Luckily, we only need to check the
248      year at this point; normalization guarantees that all values will
249      be in correct ranges EXCEPT the year. */
250
251   if (me->tm_year < 0)
252     return BAD_STRUCT_TM;
253
254   /*************************************************/
255   /* Find the appropriate time_t for the structure */
256   /*************************************************/
257
258   /* Modified b-search -- make intelligent guesses as to where the
259      time might lie along the timeline, assuming that our target time
260      lies a linear distance (w/o considering time jumps of a
261      particular region).
262
263      Assume that time does not fluctuate at all along the timeline --
264      e.g., assume that a day will always take 86400 seconds, etc. --
265      and come up with a hypothetical value for the time_t
266      representation of the struct tm TARGET, in relation to the guess
267      variable -- it should be pretty close!
268
269      After testing this, the maximum number of iterations that I had
270      on any number that I tried was 3!  Not bad.
271
272      The reason this is not a subroutine is that we will modify some
273      fields in the struct tm (yday and mday).  I've never felt good
274      about side-effects when writing structured code... */
275
276   {
277     struct tm *guess_tm;
278     time_t guess = 0;
279     time_t distance = 0;
280     time_t last_distance = 0;
281
282     times_through_search = 0;
283
284     do
285       {
286         guess += distance;
287
288         times_through_search++;     
289       
290         guess_tm = (*producer) (&guess);
291       
292 #ifdef DEBUG
293         if (debugging_enabled)
294           {
295             printf ("   Guessing time_t == %d\n     ", (int) guess);
296             printtm (guess_tm);
297             putchar ('\n');
298           }
299 #endif
300       
301         /* How far is our guess from the desired struct tm? */
302         distance = dist_tm (me, guess_tm);
303       
304         /* Handle periods of time where a period of time is skipped.
305            For example, 2:15 3 April 1994 does not exist, because DST
306            is in effect.  The distance function will alternately
307            return values of 3600 and -3600, because it doesn't know
308            that the requested time doesn't exist.  In these situations
309            (even if the skip is not exactly an hour) the distances
310            returned will be the same, but alternating in sign.  We
311            want the later time, so check to see that the distance is
312            oscillating and we've chosen the correct of the two
313            possibilities.
314
315            Useful: 3 Apr 94 765356300, 30 Oct 94 783496000 */
316
317         if ((distance == -last_distance) && (distance < last_distance))
318           {
319             /* If the caller specified that the DST flag was off, it's
320                not possible to represent this time. */
321             if (me->tm_isdst == 0)
322               {
323 #ifdef DEBUG
324             printf ("   Distance is oscillating -- dst flag nixes struct!\n");
325 #endif
326                 return BAD_STRUCT_TM;
327               }
328
329 #ifdef DEBUG
330             printf ("   Distance is oscillating -- chose the later time.\n");
331 #endif
332             distance = 0;
333           }
334
335         if ((distance == 0) && (me->tm_isdst != -1)
336             && (me->tm_isdst != guess_tm->tm_isdst))
337           {
338             /* If we're in this code, we've got the right time but the
339                wrong daylight savings flag.  We need to move away from
340                the time that we have and approach the other time from
341                the other direction.  That is, if I've requested the
342                non-DST version of a time and I get the DST version
343                instead, I want to put us forward in time and search
344                backwards to get the other time.  I checked all of the
345                configuration files for the tz package -- no entry
346                saves more than two hours, so I think we'll be safe by
347                moving 24 hours in one direction.  IF THE AMOUNT OF
348                TIME SAVED IN THE CONFIGURATION FILES CHANGES, THIS
349                VALUE MAY NEED TO BE ADJUSTED.  Luckily, we can never
350                have more than one level of overlaps, or this would
351                never work. */
352
353 #define SKIP_VALUE 86400
354
355             if (guess_tm->tm_isdst == 0)
356               /* we got the later one, but want the earlier one */
357               distance = -SKIP_VALUE;
358             else
359               distance = SKIP_VALUE;
360             
361 #ifdef DEBUG
362             printf ("   Got the right time, wrong DST value -- adjusting\n");
363 #endif
364           }
365
366         last_distance = distance;
367
368       } while (distance != 0);
369
370     /* Check to see that the dst flag matches */
371
372     if (me->tm_isdst != -1)
373       {
374         if (me->tm_isdst != guess_tm->tm_isdst)
375           {
376 #ifdef DEBUG
377             printf ("   DST flag doesn't match!  FIXME?\n");
378 #endif
379             return BAD_STRUCT_TM;
380           }
381       }
382
383     result = guess;             /* Success! */
384
385     /* On successful completion, the values of tm_wday and tm_yday
386        have to be set appropriately. */
387     
388     /* me->tm_yday = guess_tm->tm_yday; 
389        me->tm_mday = guess_tm->tm_mday; */
390
391     *me = *guess_tm;
392   }
393
394   /* Update the caller's version of the structure */
395
396   *timeptr = *me;
397
398   return result;
399 }
400
401 time_t
402 #ifdef DEBUG                    /* make it work even if the system's
403                                    libc has it's own mktime routine */
404 my_mktime (timeptr)
405 #else
406 mktime (timeptr)
407 #endif
408      struct tm *timeptr;
409 {
410   return _mktime_internal (timeptr, localtime);
411 }
412 \f
413 #ifdef DEBUG
414 void
415 main (argc, argv)
416      int argc;
417      char *argv[];
418 {
419   int time;
420   int result_time;
421   struct tm *tmptr;
422   
423   if (argc == 1)
424     {
425       long q;
426       
427       printf ("starting long test...\n");
428
429       for (q = 10000000; q < 1000000000; q += 599)
430         {
431           struct tm *tm = localtime ((time_t *) &q);
432           if ((q % 10000) == 0) { printf ("%ld\n", q); fflush (stdout); }
433           if (q != mktime (tm))
434             { printf ("failed for %ld\n", q); fflush (stdout); }
435         }
436       
437       printf ("test finished\n");
438
439       exit (0);
440     }
441   
442   if (argc != 2)
443     {
444       printf ("wrong # of args\n");
445       exit (0);
446     }
447   
448   debugging_enabled = 1;        /* We want to see the info */
449
450   ++argv;
451   time = atoi (*argv);
452   
453   tmptr = localtime ((time_t *) &time);
454   printf ("Localtime tells us that a time_t of %d represents\n     ", time);
455   printtm (tmptr);
456   putchar ('\n');
457
458   printf ("   Given localtime's return val, mktime returns %d which is\n     ",
459           (int) my_mktime (tmptr));
460   printtm (tmptr);
461   putchar ('\n');
462
463 #if 0
464   tmptr->tm_sec -= 20;
465   tmptr->tm_min -= 20;
466   tmptr->tm_hour -= 20;
467   tmptr->tm_mday -= 20;
468   tmptr->tm_mon -= 20;
469   tmptr->tm_year -= 20;
470   tmptr->tm_gmtoff -= 20000;    /* This has no effect! */
471   tmptr->tm_zone = NULL;        /* Nor does this! */
472   tmptr->tm_isdst = -1;
473 #endif
474   
475   tmptr->tm_hour += 1;
476   tmptr->tm_isdst = -1;
477
478   printf ("\n\nchanged ranges: ");
479   printtm (tmptr);
480   putchar ('\n');
481
482   result_time = my_mktime (tmptr);
483   printf ("\nmktime: %d\n", result_time);
484
485   tmptr->tm_isdst = 0;
486
487   printf ("\n\nchanged ranges: ");
488   printtm (tmptr);
489   putchar ('\n');
490
491   result_time = my_mktime (tmptr);
492   printf ("\nmktime: %d\n", result_time);
493 }
494 #endif /* DEBUG */
495
496 \f
497 /*
498 Local Variables:
499 compile-command: "gcc -g mktime.c -o mktime -DDEBUG"
500 End:
501 */