home *** CD-ROM | disk | FTP | other *** search
- /*
- * MAKETIME derive 32-bit time value from TM structure.
- *
- * Usage:
- * int zone; Minutes west of GMT, or
- * 48*60 for localtime
- * time_t t;
- * struct tm *tp; Pointer to TM structure from <time.h>
- * t = maketime(tp,zone);
- *
- * Returns:
- * -1 if failure; parameter out of range or nonsensical.
- * else time-value.
- * Notes:
- * This code is quasi-public; it may be used freely in like software.
- * It is not to be sold, nor used in licensed software without
- * permission of the author.
- * For everyone's benefit, please report bugs and improvements!
- * Copyright 1981 by Ken Harrenstien, SRI International.
- * (ARPANET: KLH @ SRI)
- */
- /* $Log: maketime.c,v $
- * Revision 1.2.1.1 91/01/18 12:15:21 berliner
- * For CVS 1.2, contributed by Paul Eggert
- *
- * Revision 1.3 1991/01/18 00:14:48 eggert
- * Make minimal changes to interface to CVS 1.0.
- *
- * Revision 5.2 1990/11/01 05:03:30 eggert
- * Remove lint.
- *
- * Revision 5.1 1990/10/04 06:30:13 eggert
- * Calculate the GMT offset of 'xxx LT' as of xxx, not as of now.
- * Don't assume time_t is 32 bits. Fix bugs near epoch and near end of time.
- *
- * Revision 5.0 1990/08/22 08:12:38 eggert
- * Switch to GMT and fix the bugs exposed thereby.
- * Permit dates past 1999/12/31. Ansify and Posixate.
- *
- * Revision 1.8 88/11/08 13:54:53 narten
- * allow negative timezones (-24h <= x <= 24h)
- *
- * Revision 1.7 88/08/28 14:47:52 eggert
- * Allow cc -R. Remove unportable "#endif XXX"s.
- *
- * Revision 1.6 87/12/18 17:05:58 narten
- * include rcsparam.h
- *
- * Revision 1.5 87/12/18 11:35:51 narten
- * maketime.c: fixed USG code - you have tgo call "tzset" in order to have
- * "timezone" set. ("localtime" calls it, but it's probably better not to
- * count on "localtime" having been called.)
- *
- * Revision 1.4 87/10/18 10:26:57 narten
- * Updating version numbers. Changes relative to 1.0 are actually
- * relative to 1.2
- *
- * Revision 1.3 87/09/24 13:58:45 narten
- * Sources now pass through lint (if you ignore printf/sprintf/fprintf
- * warnings)
- *
- * Revision 1.2 87/03/27 14:21:48 jenkins
- * Port to suns
- *
- * Revision 1.2 83/12/05 10:12:56 wft
- * added cond. compilation for USG Unix; long timezone;
- *
- * Revision 1.1 82/05/06 11:38:00 wft
- * Initial revision
- *
- */
-
-
- /* minimal changes needed to get this file to work for CVS rather than RCS */
- /* #include "rcsbase.h" */
- #include <sys/types.h>
- #include <time.h>
- #include "cvs.h"
- #define datesize 32
- #define faterror(x,y) error(0,x,y)
- #define libId(x,y) static char x[] = y;
- #define P(x) ()
- #define RCSversion 5
- #define VERSION(x) x
- #define VOID (void)
- #if !__STDC__
- # define const
- #endif
- void str2date();
- Make_Date(a,b) const char *a; char *b; { str2date(a,b); }
-
- libId(maketId, "$Id: maketime.c,v 1.2.1.1 91/01/18 12:15:21 berliner Exp $")
-
- static const struct tm *time2tm P((time_t));
-
- #define given(v) (0 <= (v)) /* Negative values are unspecified. */
-
- static const int daytb[] = {
- /* # days in year thus far, indexed by month (0-12!!) */
- 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
- };
-
- static time_t
- maketime(atm,zone)
- const struct tm *atm;
- int zone;
- {
- register const struct tm *tp;
- register int i;
- int year, yday, mon, day, hour, min, sec, leap, localzone;
- int attempts;
- time_t t, tres;
-
- attempts = 2;
- localzone = zone==48*60;
- tres = -1;
- year = mon = day = 0; /* Keep lint happy. */
-
- do {
-
- if (localzone || !given(atm->tm_year)) {
- if (tres == -1)
- if ((tres = time((time_t*)0)) == -1)
- return -1;
- tp = time2tm(tres);
- /* Get breakdowns of default time, adjusting to zone. */
- year = tp->tm_year; /* Use to set up defaults */
- yday = tp->tm_yday;
- mon = tp->tm_mon;
- day = tp->tm_mday;
- hour = tp->tm_hour;
- min = tp->tm_min;
- if (localzone) {
- tp = localtime(&tres);
- zone =
- min - tp->tm_min + 60*(
- hour - tp->tm_hour + 24*(
- /* If years differ, it's by one day. */
- year - tp->tm_year
- ? year - tp->tm_year
- : yday - tp->tm_yday));
- }
- /* Adjust the default day, month and year according to zone. */
- if ((min -= zone) < 0) {
- if (hour-(59-min)/60 < 0 && --day <= 0) {
- if (--mon < 0) {
- --year;
- mon = 11;
- }
- day = daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3));
- }
- } else
- if (
- 24 <= hour+min/60 &&
- daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3)) < ++day
- ) {
- if (11 < ++mon) {
- ++year;
- mon = 0;
- }
- day = 1;
- }
- }
- if (zone < -24*60 || 24*60 < zone)
- return -1;
-
-
- #ifdef DEBUG
- printf("first YMD: %d %d %d\n",year,mon,day);
- #endif
- tp = atm;
-
- /* First must find date, using specified year, month, day.
- * If one of these is unspecified, it defaults either to the
- * current date (if no more global spec was given) or to the
- * zero-value for that spec (i.e. a more global spec was seen).
- * Reject times that do not fit in time_t,
- * without assuming that time_t is 32 bits or is signed.
- */
- if (given(tp->tm_year))
- {
- year = tp->tm_year;
- mon = 0; /* Since year was given, default */
- day = 1; /* for remaining specs is zero */
- }
- if (year < 69) /* 1969/12/31 OK in some timezones. */
- return -1; /* ERR: year out of range */
- leap = !(year&3) && (year%100 || !((year+300)%400));
- year -= 70; /* UNIX time starts at 1970 */
-
- /*
- * Find day of year.
- */
- {
- if (given(tp->tm_mon))
- { mon = tp->tm_mon; /* Month was specified */
- day = 1; /* so set remaining default */
- }
- if (11 < (unsigned)mon)
- return -1; /* ERR: bad month */
- if (given(tp->tm_mday)) day = tp->tm_mday;
- if(day < 1
- || (((daytb[mon+1]-daytb[mon]) < day)
- && (day!=29 || mon!=1 || !leap) ))
- return -1; /* ERR: bad day */
- yday = daytb[mon] /* Add # of days in months so far */
- + ((leap /* Leap year, and past Feb? If */
- && mon>1)? 1:0) /* so, add leap day for this year */
- + day-1; /* And finally add # days this mon */
-
- }
- if (leap+365 <= (unsigned)yday)
- return -1; /* ERR: bad YDAY */
-
- if (year < 0) {
- if (yday != 364)
- return -1; /* ERR: too early */
- t = -1;
- } else {
- tres = year*365; /* Get # days of years so far */
- if (tres/365 != year)
- return -1; /* ERR: overflow */
- t = tres
- + ((year+1)>>2) /* plus # of leap days since 1970 */
- + yday; /* and finally add # days this year */
- if (t+4 < tres)
- return -1; /* ERR: overflow */
- }
- tres = t;
-
- if (given(i = tp->tm_wday)) /* Check WDAY if present */
- if (i != (tres+4)%7) /* 1970/01/01 was Thu = 4 */
- return -1; /* ERR: bad WDAY */
-
- #ifdef DEBUG
- printf("YMD: %d %d %d, T=%ld\n",year,mon,day,tres);
- #endif
- /*
- * Now determine time. If not given, default to zeros
- * (since time is always the least global spec)
- */
- tres *= 86400L; /* Get # seconds (24*60*60) */
- if (tres/86400L != t)
- return -1; /* ERR: overflow */
- hour = min = sec = 0;
- if (given(tp->tm_hour)) hour = tp->tm_hour;
- if (given(tp->tm_min )) min = tp->tm_min;
- if (given(tp->tm_sec )) sec = tp->tm_sec;
- if (60 <= (unsigned)min || 60 < (unsigned)sec)
- return -1; /* ERR: MS out of range */
- if (24 <= (unsigned)hour)
- if(hour != 24 || (min+sec) !=0) /* Allow 24:00 */
- return -1; /* ERR: H out of range */
-
- t = tres;
- tres += sec + 60L*(zone + min + 60*hour);
-
- #ifdef DEBUG
- printf("HMS: %d %d %d T=%ld\n",hour,min,sec,tres);
- #endif
-
- if (!localzone) /* check for overflow */
- return (year<0 ? (tres<0||86400L<=tres) : tres<t) ? -1 : tres;
-
- /* Check results; LT may have had a different GMT offset back then. */
- tp = localtime(&tres);
- if (given(atm->tm_sec) && atm->tm_sec != tp->tm_sec)
- return -1; /* If seconds don't match, we're in trouble. */
- if (!(
- given(atm->tm_min) && atm->tm_min != tp->tm_min ||
- given(atm->tm_hour) && atm->tm_hour != tp->tm_hour ||
- given(atm->tm_mday) && atm->tm_mday != tp->tm_mday ||
- given(atm->tm_mon) && atm->tm_mon != tp->tm_mon ||
- given(atm->tm_year) && atm->tm_year != tp->tm_year
- ))
- return tres; /* Everything matches. */
-
- } while (--attempts);
-
- return -1;
- }
-
- /*
- * Convert Unix time to struct tm format.
- * Use Coordinated Universal Time (UTC) if available and if version 5 or newer;
- * use local time otherwise.
- */
- static const struct tm *
- time2tm(unixtime)
- time_t unixtime;
- {
- const struct tm *tm;
- return
- VERSION(5)<=RCSversion && (tm = gmtime(&unixtime))
- ? tm
- : localtime(&unixtime);
- }
-
- /*
- * 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 const struct tm *tm = time2tm(unixtime);
- VOID sprintf(date, DATEFORM,
- tm->tm_year + (tm->tm_year<100 ? 0 : 1900),
- tm->tm_mon+1, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec
- );
- }
-
-
-
- void
- str2date(source, target)
- const char *source;
- char target[datesize];
- /* Parse a free-format date in SOURCE, convert it
- * into RCS internal format, and store the result into TARGET.
- */
- {
- int zone;
- time_t unixtime;
- struct tm parseddate;
-
- if (!partime(source, &parseddate, &zone))
- faterror("can't parse date/time: %s", source);
- if ((unixtime = maketime(&parseddate, zone)) == -1)
- faterror("bad date/time: %s", source);
- time2date(unixtime, target);
- }
-