home *** CD-ROM | disk | FTP | other *** search
- /* maketime - yield time_t from struct tm yielded by partime */
-
- /* Copyright 1992 by Paul Eggert
- Distributed under license by the Free Software Foundation, Inc.
-
- This file is part of RCS.
-
- RCS is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- RCS is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with RCS; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- Report problems and direct all questions to:
-
- rcs-bugs@cs.purdue.edu
-
- */
-
- #include "rcsbase.h"
-
- libId(maketId, "$Id: maketime.c,v 5.7 1992/05/31 08:29:18 eggert Exp $")
-
- /*
- * For maximum portability, use only localtime and gmtime.
- * Make no assumptions about the epoch or the range of time_t values.
- * Avoid mktime because it's not universal and because there's no easy,
- * portable way for mktime to yield the inverse of gmtime.
- */
-
- #define TM_YEAR_ORIGIN 1900
-
- #define LOCAL_TIME (48*60)
-
- static int
- isleap(y)
- int y;
- {
- return (y&3) == 0 && (y%100 != 0 || y%400 == 0);
- }
-
- static int const month_yday[] = {
- /* days in year before start of months 0-12 */
- 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
- };
-
- /* Yield the number of days in TM's month. */
- static int
- month_days(tm)
- struct tm const *tm;
- {
- int m = tm->tm_mon;
- return month_yday[m+1] - month_yday[m]
- + (m==1 && isleap(tm->tm_year + TM_YEAR_ORIGIN));
- }
-
- /*
- * Convert UNIXTIME to struct tm form.
- * Use gmtime if available and if !LOCALZONE, localtime otherwise.
- */
- static struct tm *
- time2tm(unixtime, localzone)
- time_t unixtime;
- int localzone;
- {
- struct tm *tm;
- # if TZ_must_be_set
- static char const *TZ;
- if (!TZ && !(TZ = getenv("TZ")))
- faterror("TZ is not set");
- # endif
- if (localzone || !(tm = gmtime(&unixtime)))
- tm = localtime(&unixtime);
- return tm;
- }
-
- /* Yield A - B, measured in seconds. */
- static time_t
- difftm(a, b)
- struct tm const *a, *b;
- {
- int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
- int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
- return
- (
- (
- (
- /* difference in day of year */
- a->tm_yday - b->tm_yday
- /* + intervening leap days */
- + ((ay >> 2) - (by >> 2))
- - (ay/100 - by/100)
- + ((ay/100 >> 2) - (by/100 >> 2))
- /* + difference in years * 365 */
- + (time_t)(ay-by) * 365
- )*24 + (a->tm_hour - b->tm_hour)
- )*60 + (a->tm_min - b->tm_min)
- )*60 + (a->tm_sec - b->tm_sec);
- }
-
- /*
- * Adjust T by adding MINUTES. MINUTES must be at most 24 hours' worth.
- * Adjust only T's year, mon, mday, hour and min members;
- * plus adjust wday if it is not negative.
- */
- static void
- adjzone(t, minutes)
- register struct tm *t;
- int minutes;
- {
- if ((t->tm_min += minutes) < 0) {
- if ((t->tm_hour -= (59-t->tm_min)/60) < 0) {
- t->tm_hour += 24;
- if (0 <= t->tm_wday && --t->tm_wday < 0)
- t->tm_wday = 6;
- if (--t->tm_mday <= 0) {
- if (--t->tm_mon < 0) {
- --t->tm_year;
- t->tm_mon = 11;
- }
- t->tm_mday = month_days(t);
- }
- }
- t->tm_min += 24*60;
- } else
- if (24 <= (t->tm_hour += t->tm_min/60)) {
- t->tm_hour -= 24;
- if (0 <= t->tm_wday && ++t->tm_wday == 7)
- t->tm_wday = 0;
- if (month_days(t) < ++t->tm_mday) {
- if (11 < ++t->tm_mon) {
- ++t->tm_year;
- t->tm_mon = 0;
- }
- t->tm_mday = 1;
- }
- }
- t->tm_min %= 60;
- }
-
- /*
- * Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise.
- * Use only TM's year, mon, mday, hour, min, and sec members.
- * Ignore TM's old tm_yday and tm_wday, but fill in their correct values.
- * Yield -1 on failure (e.g. a member out of range).
- * Posix 1003.1-1990 doesn't allow leap seconds, but some implementations
- * have them anyway, so allow them if localtime/gmtime does.
- */
- static time_t
- tm2time(tm, localzone)
- struct tm *tm;
- int localzone;
- {
- /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime. */
- static time_t t_cache[2];
- static struct tm tm_cache[2];
-
- time_t d, gt;
- struct tm const *gtm;
- /*
- * The maximum number of iterations should be enough to handle any
- * combinations of leap seconds, time zone rule changes, and solar time.
- * 4 is probably enough; we use a bigger number just to be safe.
- */
- int remaining_tries = 8;
-
- /* Avoid subscript errors. */
- if (12 <= (unsigned)tm->tm_mon)
- return -1;
-
- tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday
- - (tm->tm_mon<2 || ! isleap(tm->tm_year + TM_YEAR_ORIGIN));
-
- /* Make a first guess. */
- gt = t_cache[localzone];
- gtm = gt ? &tm_cache[localzone] : time2tm(gt,localzone);
-
- /* Repeatedly use the error from the guess to improve the guess. */
- while ((d = difftm(tm, gtm)) != 0) {
- if (--remaining_tries == 0)
- return -1;
- gt += d;
- gtm = time2tm(gt,localzone);
- }
- t_cache[localzone] = gt;
- tm_cache[localzone] = *gtm;
-
- /*
- * Check that the guess actually matches;
- * overflow can cause difftm to yield 0 even on differing times,
- * or tm may have members out of range (e.g. bad leap seconds).
- */
- if ( tm->tm_year ^ gtm->tm_year
- | tm->tm_mon ^ gtm->tm_mon
- | tm->tm_mday ^ gtm->tm_mday
- | tm->tm_hour ^ gtm->tm_hour
- | tm->tm_min ^ gtm->tm_min
- | tm->tm_sec ^ gtm->tm_sec)
- return -1;
-
- tm->tm_wday = gtm->tm_wday;
- return gt;
- }
-
- /*
- * Check TM0 and convert it to time_t, assuming ZONE minutes west of gmtime.
- * Use localtime if ZONE is the special value LOCAL_TIME.
- * Ignore TM0->tm_yday; assume default values for any other negative inputs.
- * Yield -1 on failure.
- */
- static time_t
- maketime(tm0, zone)
- struct tm const *tm0;
- int zone;
- {
- int localzone, wday;
- struct tm tm;
- time_t r;
-
- localzone = zone==LOCAL_TIME;
-
- tm = *tm0;
- if (tm.tm_year < 0) {
- /* Set default year, month, day from current time. */
- register struct tm *d = time2tm(now(), localzone);
- if (!localzone)
- adjzone(d, -zone);
- tm.tm_year = d->tm_year;
- if (tm.tm_mon < 0) {
- tm.tm_mon = d->tm_mon;
- if (tm.tm_mday < 0)
- tm.tm_mday = d->tm_mday;
- }
- }
-
- /* Set remaining default fields to be their minimum values. */
- if (tm.tm_mon < 0) tm.tm_mon = 0;
- if (tm.tm_mday < 0) tm.tm_mday = 1;
- if (tm.tm_hour < 0) tm.tm_hour = 0;
- if (tm.tm_min < 0) tm.tm_min = 0;
- if (tm.tm_sec < 0) tm.tm_sec = 0;
-
- if (!localzone)
- adjzone(&tm, zone);
- wday = tm.tm_wday;
-
- /* Convert and fill in the rest of the tm. */
- r = tm2time(&tm, localzone);
-
- /* Check weekday. */
- if (r != -1 && 0 <= wday && wday != tm.tm_wday)
- return -1;
-
- return r;
- }
-
- /*
- * Convert Unix time to RCS format.
- * For compatibility with older versions of RCS,
- * dates before AD 2000 are stored without the leading "19".
- */
- void
- time2date(unixtime,date)
- time_t unixtime;
- char date[datesize];
- {
- register struct tm const *tm = time2tm(unixtime, RCSversion<VERSION(5));
- VOID sprintf(date, DATEFORM,
- tm->tm_year + (tm->tm_year<100 ? 0 : TM_YEAR_ORIGIN),
- tm->tm_mon+1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec
- );
- }
-
- /* Parse a free-format date in SOURCE, yielding a Unix format time. */
- static time_t
- str2time(source)
- char const *source;
- {
- int zone;
- time_t unixtime;
- struct tm parseddate;
-
- if (!partime(source, &parseddate, &zone))
- faterror("can't parse date/time: %s", source);
- if (LOCAL_TIME < zone)
- /* No zone was specified. */
- zone = RCSversion<VERSION(5) ? LOCAL_TIME : 0;
- if ((unixtime = maketime(&parseddate, zone)) == -1)
- faterror("bad date/time: %s", source);
- return unixtime;
- }
-
- /*
- * Parse a free-format date in SOURCE, convert it
- * into RCS internal format, and store the result into TARGET.
- */
- void
- str2date(source, target)
- char const *source;
- char target[datesize];
- {
- time2date(str2time(source), target);
- }
-
- /* Convert an RCS internal format date to time_t. */
- time_t
- date2time(source)
- char const source[datesize];
- {
- char s[datesize];
- return str2time(date2str(source, s));
- }
-