home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / rcs567s.zip / rcs / src / maketime.c < prev    next >
C/C++ Source or Header  |  1993-11-09  |  8KB  |  327 lines

  1. /* Convert struct partime into time_t.  */
  2.  
  3. /* Copyright 1992, 1993 Paul Eggert
  4.    Distributed under license by the Free Software Foundation, Inc.
  5.  
  6. This file is part of RCS.
  7.  
  8. RCS is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2, or (at your option)
  11. any later version.
  12.  
  13. RCS 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
  16. GNU General Public License for more details.
  17.  
  18. You should have received a copy of the GNU General Public License
  19. along with RCS; see the file COPYING.  If not, write to
  20. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22. Report problems and direct all questions to:
  23.  
  24.     rcs-bugs@cs.purdue.edu
  25.  
  26. */
  27.  
  28. #if has_conf_h
  29. #    include "conf.h"
  30. #else
  31. #    ifdef __STDC__
  32. #        define P(x) x
  33. #    else
  34. #        define const
  35. #        define P(x) ()
  36. #    endif
  37. #    include <stdlib.h>
  38. #    include <time.h>
  39. #endif
  40.  
  41. #include "partime.h"
  42. #include "maketime.h"
  43.  
  44. char const maketId[]
  45.   = "$Id: maketime.c,v 5.9 1993/11/09 17:40:15 eggert Exp $";
  46.  
  47. static int isleap P((int));
  48. static int month_days P((struct tm const*));
  49. static time_t maketime P((struct partime const*,time_t));
  50.  
  51. /*
  52. * For maximum portability, use only localtime and gmtime.
  53. * Make no assumptions about the time_t epoch or the range of time_t values.
  54. * Avoid mktime because it's not universal and because there's no easy,
  55. * portable way for mktime to yield the inverse of gmtime.
  56. */
  57.  
  58. #define TM_YEAR_ORIGIN 1900
  59.  
  60.     static int
  61. isleap(y)
  62.     int y;
  63. {
  64.     return (y&3) == 0  &&  (y%100 != 0 || y%400 == 0);
  65. }
  66.  
  67. static int const month_yday[] = {
  68.     /* days in year before start of months 0-12 */
  69.     0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
  70. };
  71.  
  72. /* Yield the number of days in TM's month.  */
  73.     static int
  74. month_days(tm)
  75.     struct tm const *tm;
  76. {
  77.     int m = tm->tm_mon;
  78.     return month_yday[m+1] - month_yday[m]
  79.         + (m==1 && isleap(tm->tm_year + TM_YEAR_ORIGIN));
  80. }
  81.  
  82. /*
  83. * Convert UNIXTIME to struct tm form.
  84. * Use gmtime if available and if !LOCALZONE, localtime otherwise.
  85. */
  86.     struct tm *
  87. time2tm(unixtime, localzone)
  88.     time_t unixtime;
  89.     int localzone;
  90. {
  91.     struct tm *tm;
  92. #    if TZ_must_be_set
  93.         static char const *TZ;
  94.         if (!TZ  &&  !(TZ = getenv("TZ")))
  95.             faterror("The TZ environment variable is not set; please set it to your timezone");
  96. #    endif
  97.     if (localzone  ||  !(tm = gmtime(&unixtime)))
  98.         tm = localtime(&unixtime);
  99.     return tm;
  100. }
  101.  
  102. /* Yield A - B, measured in seconds.  */
  103.     time_t
  104. difftm(a, b)
  105.     struct tm const *a, *b;
  106. {
  107.     int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
  108.     int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
  109.     return
  110.         (
  111.             (
  112.                 (
  113.                     /* difference in day of year */
  114.                        a->tm_yday - b->tm_yday
  115.                     /* + intervening leap days */
  116.                     +  ((ay >> 2) - (by >> 2))
  117.                     -  (ay/100 - by/100)
  118.                     +  ((ay/100 >> 2) - (by/100 >> 2))
  119.                     /* + difference in years * 365 */
  120.                     +  (time_t)(ay-by) * 365
  121.                 )*24 + (a->tm_hour - b->tm_hour)
  122.             )*60 + (a->tm_min - b->tm_min)
  123.         )*60 + (a->tm_sec - b->tm_sec);
  124. }
  125.  
  126. /*
  127. * Adjust T by adding MINUTES.  MINUTES must be at most 24 hours' worth.
  128. * Adjust only T's year, mon, mday, hour and min members;
  129. * plus adjust wday if it is defined.
  130. */
  131.     void
  132. adjzone(t, minutes)
  133.     register struct tm *t;
  134.     int minutes;
  135. {
  136.     if ((t->tm_min += minutes) < 0) {
  137.         if ((t->tm_hour -= (59-t->tm_min)/60) < 0) {
  138.         t->tm_hour += 24;
  139.         if (TM_DEFINED(t->tm_wday)  &&  --t->tm_wday < 0)
  140.             t->tm_wday = 6;
  141.         if (--t->tm_mday <= 0) {
  142.             if (--t->tm_mon < 0) {
  143.             --t->tm_year;
  144.             t->tm_mon = 11;
  145.             }
  146.             t->tm_mday = month_days(t);
  147.         }
  148.         }
  149.         t->tm_min += 24*60;
  150.     } else
  151.         if (24 <= (t->tm_hour += t->tm_min/60)) {
  152.         t->tm_hour -= 24;
  153.         if (TM_DEFINED(t->tm_wday)  &&  ++t->tm_wday == 7)
  154.             t->tm_wday = 0;
  155.         if (month_days(t) < ++t->tm_mday) {
  156.             if (11 < ++t->tm_mon) {
  157.             ++t->tm_year;
  158.             t->tm_mon = 0;
  159.             }
  160.             t->tm_mday = 1;
  161.         }
  162.         }
  163.     t->tm_min %= 60;
  164. }
  165.  
  166. /*
  167. * Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise.
  168. * Use only TM's year, mon, mday, hour, min, and sec members.
  169. * Ignore TM's old tm_yday and tm_wday, but fill in their correct values.
  170. * Yield -1 on failure (e.g. a member out of range).
  171. * Posix 1003.1-1990 doesn't allow leap seconds, but some implementations
  172. * have them anyway, so allow them if localtime/gmtime does.
  173. */
  174.     time_t
  175. tm2time(tm, localzone)
  176.     struct tm *tm;
  177.     int localzone;
  178. {
  179.     /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime.  */
  180.     static time_t t_cache[2];
  181.     static struct tm tm_cache[2];
  182.  
  183.     time_t d, gt;
  184.     struct tm const *gtm;
  185.     /*
  186.     * The maximum number of iterations should be enough to handle any
  187.     * combinations of leap seconds, time zone rule changes, and solar time.
  188.     * 4 is probably enough; we use a bigger number just to be safe.
  189.     */
  190.     int remaining_tries = 8;
  191.  
  192.     /* Avoid subscript errors.  */
  193.     if (12 <= (unsigned)tm->tm_mon)
  194.         return -1;
  195.  
  196.     tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday
  197.         -  (tm->tm_mon<2  ||  ! isleap(tm->tm_year + TM_YEAR_ORIGIN));
  198.  
  199.     /* Make a first guess.  */
  200.     gt = t_cache[localzone];
  201.     gtm = gt ? &tm_cache[localzone] : time2tm(gt,localzone);
  202.  
  203.     /* Repeatedly use the error from the guess to improve the guess.  */
  204.     while ((d = difftm(tm, gtm)) != 0) {
  205.         if (--remaining_tries == 0)
  206.             return -1;
  207.         gt += d;
  208.         gtm = time2tm(gt,localzone);
  209.     }
  210.     t_cache[localzone] = gt;
  211.     tm_cache[localzone] = *gtm;
  212.  
  213.     /*
  214.     * Check that the guess actually matches;
  215.     * overflow can cause difftm to yield 0 even on differing times,
  216.     * or tm may have members out of range (e.g. bad leap seconds).
  217.     */
  218.     if (   (tm->tm_year ^ gtm->tm_year)
  219.         |  (tm->tm_mon  ^ gtm->tm_mon)
  220.         |  (tm->tm_mday ^ gtm->tm_mday)
  221.         |  (tm->tm_hour ^ gtm->tm_hour)
  222.         |  (tm->tm_min  ^ gtm->tm_min)
  223.         |  (tm->tm_sec  ^ gtm->tm_sec))
  224.         return -1;
  225.  
  226.     tm->tm_wday = gtm->tm_wday;
  227.     return gt;
  228. }
  229.  
  230. /*
  231. * Check *PT and convert it to time_t.
  232. * If it is incompletely specified, use DEFAULT_TIME to fill it out.
  233. * Use localtime if PT->zone is the special value TM_LOCAL_ZONE.
  234. * Yield -1 on failure.
  235. * ISO 8601 day-of-year and week numbers are not yet supported.
  236. */
  237.     static time_t
  238. maketime(pt, default_time)
  239.     struct partime const *pt;
  240.     time_t default_time;
  241. {
  242.     int localzone, wday;
  243.     struct tm tm;
  244.     struct tm *tm0 = 0;
  245.     time_t r;
  246.  
  247.     tm0 = 0; /* Keep gcc -Wall happy.  */
  248.     localzone = pt->zone==TM_LOCAL_ZONE;
  249.  
  250.     tm = pt->tm;
  251.  
  252.     if (TM_DEFINED(pt->ymodulus) || !TM_DEFINED(tm.tm_year)) {
  253.         /* Get tm corresponding to current time.  */
  254.         tm0 = time2tm(default_time, localzone);
  255.         if (!localzone)
  256.         adjzone(tm0, pt->zone);
  257.     }
  258.  
  259.     if (TM_DEFINED(pt->ymodulus))
  260.         tm.tm_year +=
  261.         (tm0->tm_year + TM_YEAR_ORIGIN)/pt->ymodulus * pt->ymodulus;
  262.     else if (!TM_DEFINED(tm.tm_year)) {
  263.         /* Set default year, month, day from current time.  */
  264.         tm.tm_year = tm0->tm_year + TM_YEAR_ORIGIN;
  265.         if (!TM_DEFINED(tm.tm_mon)) {
  266.         tm.tm_mon = tm0->tm_mon;
  267.         if (!TM_DEFINED(tm.tm_mday))
  268.             tm.tm_mday = tm0->tm_mday;
  269.         }
  270.     }
  271.  
  272.     /* Convert from partime year (Gregorian) to Posix year.  */
  273.     tm.tm_year -= TM_YEAR_ORIGIN;
  274.  
  275.     /* Set remaining default fields to be their minimum values.  */
  276.     if (!TM_DEFINED(tm.tm_mon)) tm.tm_mon = 0;
  277.     if (!TM_DEFINED(tm.tm_mday)) tm.tm_mday = 1;
  278.     if (!TM_DEFINED(tm.tm_hour)) tm.tm_hour = 0;
  279.     if (!TM_DEFINED(tm.tm_min)) tm.tm_min = 0;
  280.     if (!TM_DEFINED(tm.tm_sec)) tm.tm_sec = 0;
  281.  
  282.     if (!localzone)
  283.         adjzone(&tm, -pt->zone);
  284.     wday = tm.tm_wday;
  285.  
  286.     /* Convert and fill in the rest of the tm.  */
  287.     r = tm2time(&tm, localzone);
  288.  
  289.     /* Check weekday.  */
  290.     if (r != -1  &&  TM_DEFINED(wday)  &&  wday != tm.tm_wday)
  291.         return -1;
  292.  
  293.     return r;
  294. }
  295.  
  296. /* Parse a free-format date in SOURCE, yielding a Unix format time.  */
  297.     time_t
  298. str2time(source, default_time, default_zone)
  299.     char const *source;
  300.     time_t default_time;
  301.     int default_zone;
  302. {
  303.     struct partime pt;
  304.  
  305.     if (*partime(source, &pt))
  306.         return -1;
  307.     if (pt.zone == TM_UNDEFINED_ZONE)
  308.         pt.zone = default_zone;
  309.     return maketime(&pt, default_time);
  310. }
  311.  
  312. #if TEST
  313. #include <stdio.h>
  314.     int
  315. main(argc, argv) int argc; char **argv;
  316. {
  317.     time_t default_time = time((time_t *)0);
  318.     int default_zone = argv[1] ? atoi(argv[1]) : 0;
  319.     char buf[1000];
  320.     while (gets(buf)) {
  321.         time_t t = str2time(buf, default_time, default_zone);
  322.         printf("%s", asctime(gmtime(&t)));
  323.     }
  324.     return 0;
  325. }
  326. #endif
  327.