summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/rcs/lib/maketime.c
blob: b87d744b3e4032c298972da483e298f427cd2a61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
#
/*
 * 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.1.1.1  1993/06/18  04:22:13  jkh
 * Updated GNU utilities
 *
 * Revision 5.3  1991/08/19  03:13:55  eggert
 * Add setfiledate, str2time, TZ_must_be_set.
 *
 * 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
 *
 */


#include "rcsbase.h"

libId(maketId, "$Id: maketime.c,v 1.1.1.1 1993/06/18 04:22:13 jkh 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) : 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 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  =  (RCSversion<VERSION(5) ? localtime : gmtime)(&unixtime)))
		faterror("UTC is not available; perhaps TZ is not set?");
	return tm;
}

/*
* 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);
	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
	);
}



	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, &times);
}
OpenPOWER on IntegriCloud