home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / sharutils-4.1-src.tgz / tar.out / fsf / sharutils / mktime.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  14KB  |  504 lines

  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.  
  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 *));
  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.     time_t guess = 0;
  281.     time_t distance = 0;
  282.     time_t last_distance = 0;
  283.  
  284.     times_through_search = 0;
  285.  
  286.     do
  287.       {
  288.     guess += distance;
  289.  
  290.     times_through_search++;     
  291.       
  292.     guess_tm = (*producer) (&guess);
  293.       
  294. #ifdef DEBUG
  295.     if (debugging_enabled)
  296.       {
  297.         printf ("   Guessing time_t == %d\n     ", (int) guess);
  298.         printtm (guess_tm);
  299.         putchar ('\n');
  300.       }
  301. #endif
  302.       
  303.     /* How far is our guess from the desired struct tm? */
  304.     distance = dist_tm (me, guess_tm);
  305.       
  306.     /* Handle periods of time where a period of time is skipped.
  307.        For example, 2:15 3 April 1994 does not exist, because DST
  308.        is in effect.  The distance function will alternately
  309.        return values of 3600 and -3600, because it doesn't know
  310.        that the requested time doesn't exist.  In these situations
  311.        (even if the skip is not exactly an hour) the distances
  312.        returned will be the same, but alternating in sign.  We
  313.        want the later time, so check to see that the distance is
  314.        oscillating and we've chosen the correct of the two
  315.        possibilities.
  316.  
  317.        Useful: 3 Apr 94 765356300, 30 Oct 94 783496000 */
  318.  
  319.     if ((distance == -last_distance) && (distance < last_distance))
  320.       {
  321.         /* If the caller specified that the DST flag was off, it's
  322.                not possible to represent this time. */
  323.         if (me->tm_isdst == 0)
  324.           {
  325. #ifdef DEBUG
  326.         printf ("   Distance is oscillating -- dst flag nixes struct!\n");
  327. #endif
  328.         return BAD_STRUCT_TM;
  329.           }
  330.  
  331. #ifdef DEBUG
  332.         printf ("   Distance is oscillating -- chose the later time.\n");
  333. #endif
  334.         distance = 0;
  335.       }
  336.  
  337.     if ((distance == 0) && (me->tm_isdst != -1)
  338.         && (me->tm_isdst != guess_tm->tm_isdst))
  339.       {
  340.         /* If we're in this code, we've got the right time but the
  341.                wrong daylight savings flag.  We need to move away from
  342.                the time that we have and approach the other time from
  343.                the other direction.  That is, if I've requested the
  344.                non-DST version of a time and I get the DST version
  345.                instead, I want to put us forward in time and search
  346.                backwards to get the other time.  I checked all of the
  347.                configuration files for the tz package -- no entry
  348.                saves more than two hours, so I think we'll be safe by
  349.                moving 24 hours in one direction.  IF THE AMOUNT OF
  350.                TIME SAVED IN THE CONFIGURATION FILES CHANGES, THIS
  351.                VALUE MAY NEED TO BE ADJUSTED.  Luckily, we can never
  352.                have more than one level of overlaps, or this would
  353.                never work. */
  354.  
  355. #define SKIP_VALUE 86400
  356.  
  357.         if (guess_tm->tm_isdst == 0)
  358.           /* we got the later one, but want the earlier one */
  359.           distance = -SKIP_VALUE;
  360.         else
  361.           distance = SKIP_VALUE;
  362.         
  363. #ifdef DEBUG
  364.         printf ("   Got the right time, wrong DST value -- adjusting\n");
  365. #endif
  366.       }
  367.  
  368.     last_distance = distance;
  369.  
  370.       } while (distance != 0);
  371.  
  372.     /* Check to see that the dst flag matches */
  373.  
  374.     if (me->tm_isdst != -1)
  375.       {
  376.     if (me->tm_isdst != guess_tm->tm_isdst)
  377.       {
  378. #ifdef DEBUG
  379.         printf ("   DST flag doesn't match!  FIXME?\n");
  380. #endif
  381.         return BAD_STRUCT_TM;
  382.       }
  383.       }
  384.  
  385.     result = guess;        /* Success! */
  386.  
  387.     /* On successful completion, the values of tm_wday and tm_yday
  388.        have to be set appropriately. */
  389.     
  390.     /* me->tm_yday = guess_tm->tm_yday; 
  391.        me->tm_mday = guess_tm->tm_mday; */
  392.  
  393.     *me = *guess_tm;
  394.   }
  395.  
  396.   /* Update the caller's version of the structure */
  397.  
  398.   *timeptr = *me;
  399.  
  400.   return result;
  401. }
  402.  
  403. time_t
  404. #ifdef DEBUG            /* make it work even if the system's
  405.                    libc has it's own mktime routine */
  406. my_mktime (timeptr)
  407. #else
  408. mktime (timeptr)
  409. #endif
  410.      struct tm *timeptr;
  411. {
  412.   return _mktime_internal (timeptr, localtime);
  413. }
  414.  
  415. #ifdef DEBUG
  416. void
  417. main (argc, argv)
  418.      int argc;
  419.      char *argv[];
  420. {
  421.   int time;
  422.   int result_time;
  423.   struct tm *tmptr;
  424.   
  425.   if (argc == 1)
  426.     {
  427.       long q;
  428.       
  429.       printf ("starting long test...\n");
  430.  
  431.       for (q = 10000000; q < 1000000000; q += 599)
  432.     {
  433.       struct tm *tm = localtime ((time_t *) &q);
  434.       if ((q % 10000) == 0) { printf ("%ld\n", q); fflush (stdout); }
  435.       if (q != my_mktime (tm))
  436.         { printf ("failed for %ld\n", q); fflush (stdout); }
  437.     }
  438.       
  439.       printf ("test finished\n");
  440.  
  441.       exit (0);
  442.     }
  443.   
  444.   if (argc != 2)
  445.     {
  446.       printf ("wrong # of args\n");
  447.       exit (0);
  448.     }
  449.   
  450.   debugging_enabled = 1;    /* We want to see the info */
  451.  
  452.   ++argv;
  453.   time = atoi (*argv);
  454.   
  455.   tmptr = localtime ((time_t *) &time);
  456.   printf ("Localtime tells us that a time_t of %d represents\n     ", time);
  457.   printtm (tmptr);
  458.   putchar ('\n');
  459.  
  460.   printf ("   Given localtime's return val, mktime returns %d which is\n     ",
  461.       (int) my_mktime (tmptr));
  462.   printtm (tmptr);
  463.   putchar ('\n');
  464.  
  465. #if 0
  466.   tmptr->tm_sec -= 20;
  467.   tmptr->tm_min -= 20;
  468.   tmptr->tm_hour -= 20;
  469.   tmptr->tm_mday -= 20;
  470.   tmptr->tm_mon -= 20;
  471.   tmptr->tm_year -= 20;
  472.   tmptr->tm_gmtoff -= 20000;    /* This has no effect! */
  473.   tmptr->tm_zone = NULL;    /* Nor does this! */
  474.   tmptr->tm_isdst = -1;
  475. #endif
  476.   
  477.   tmptr->tm_hour += 1;
  478.   tmptr->tm_isdst = -1;
  479.  
  480.   printf ("\n\nchanged ranges: ");
  481.   printtm (tmptr);
  482.   putchar ('\n');
  483.  
  484.   result_time = my_mktime (tmptr);
  485.   printf ("\nmktime: %d\n", result_time);
  486.  
  487.   tmptr->tm_isdst = 0;
  488.  
  489.   printf ("\n\nchanged ranges: ");
  490.   printtm (tmptr);
  491.   putchar ('\n');
  492.  
  493.   result_time = my_mktime (tmptr);
  494.   printf ("\nmktime: %d\n", result_time);
  495. }
  496. #endif /* DEBUG */
  497.  
  498.  
  499. /*
  500. Local Variables:
  501. compile-command: "gcc -g mktime.c -o mktime -DDEBUG"
  502. End:
  503. */
  504.