Annotation of embedaddon/lrzsz/lib/mktime.c, revision 1.1
1.1 ! misho 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>