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