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>