/* * 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 * 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) */ #include "rcsbase.h" libId(maketId, "$Id: maketime.c,v 1.2 1993/08/02 17:47:15 mycroft Exp $") static struct tm const *time2tm P((time_t)); #define given(v) (0 <= (v)) /* Negative values are unspecified. */ static int const 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) struct tm const *atm; int zone; { register struct tm const *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) : trestm_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 version 5 or newer; * use local time otherwise. */ static struct tm const * time2tm(unixtime) time_t unixtime; { struct tm const *tm; # if TZ_must_be_set static char const *TZ; if (!TZ && !(TZ = getenv("TZ"))) faterror("TZ is not set"); # endif if (!(tm = (RCSversiontm_year + (tm->tm_year<100 ? 0 : 1900), tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec ); } static time_t str2time(source) char const *source; /* Parse a free-format date in SOURCE, yielding a Unix format time. */ { 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); return unixtime; } void str2date(source, target) char const *source; char target[datesize]; /* Parse a free-format date in SOURCE, convert it * into RCS internal format, and store the result into TARGET. */ { time2date(str2time(source), target); } int setfiledate(file, date) char const *file, date[datesize]; /* Set the access and modification time of FILE to DATE. */ { static struct utimbuf times; /* static so unused fields are zero */ char datebuf[datesize]; if (!date) return 0; times.actime = times.modtime = str2time(date2str(date, datebuf)); return utime(file, ×); }