xref: /csrg-svn/lib/libc/gen/ctime.c (revision 32754)
1 /*
2  * Copyright (c) 1987 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific written prior permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  *
12  * This code was supplied by Arthur Olson, originally, and has been
13  * slightly modified at the University of California.
14  */
15 
16 #if defined(LIBC_SCCS) && !defined(lint)
17 static char sccsid[] = "@(#)ctime.c	5.12 (Berkeley) 12/03/87";
18 #endif /* LIBC_SCCS and not lint */
19 
20 #include "sys/param.h"
21 #include "sys/time.h"
22 #include "tzfile.h"
23 
24 char *
25 ctime(t)
26 time_t *t;
27 {
28 	struct tm	*localtime();
29 	char	*asctime();
30 
31 	return(asctime(localtime(t)));
32 }
33 
34 /*
35 ** A la X3J11
36 */
37 
38 char *
39 asctime(timeptr)
40 register struct tm *	timeptr;
41 {
42 	static char	wday_name[DAYS_PER_WEEK][3] = {
43 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
44 	};
45 	static char	mon_name[MONS_PER_YEAR][3] = {
46 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
47 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
48 	};
49 	static char	result[26];
50 
51 	(void) sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
52 		wday_name[timeptr->tm_wday],
53 		mon_name[timeptr->tm_mon],
54 		timeptr->tm_mday, timeptr->tm_hour,
55 		timeptr->tm_min, timeptr->tm_sec,
56 		TM_YEAR_BASE + timeptr->tm_year);
57 	return result;
58 }
59 
60 #ifndef TRUE
61 #define TRUE		1
62 #define FALSE		0
63 #endif /* !TRUE */
64 
65 extern char *		getenv();
66 extern char *		strcpy();
67 extern char *		strcat();
68 struct tm *		offtime();
69 
70 struct ttinfo {				/* time type information */
71 	long		tt_gmtoff;	/* GMT offset in seconds */
72 	int		tt_isdst;	/* used to set tm_isdst */
73 	int		tt_abbrind;	/* abbreviation list index */
74 };
75 
76 struct state {
77 	int		timecnt;
78 	int		typecnt;
79 	int		charcnt;
80 	time_t		ats[TZ_MAX_TIMES];
81 	unsigned char	types[TZ_MAX_TIMES];
82 	struct ttinfo	ttis[TZ_MAX_TYPES];
83 	char		chars[TZ_MAX_CHARS + 1];
84 };
85 
86 static struct state	s;
87 
88 static int		tz_is_set;
89 
90 char *			tzname[2] = {
91 	"GMT",
92 	"GMT"
93 };
94 
95 #ifdef USG_COMPAT
96 time_t			timezone = 0;
97 int			daylight = 0;
98 #endif /* USG_COMPAT */
99 
100 static long
101 detzcode(codep)
102 char *	codep;
103 {
104 	register long	result;
105 	register int	i;
106 
107 	result = 0;
108 	for (i = 0; i < 4; ++i)
109 		result = (result << 8) | (codep[i] & 0xff);
110 	return result;
111 }
112 
113 static
114 tzload(name)
115 register char *	name;
116 {
117 	register int	i;
118 	register int	fid;
119 
120 	if (name == 0 && (name = TZDEFAULT) == 0)
121 		return -1;
122 	{
123 		register char *	p;
124 		register int	doaccess;
125 		char		fullname[MAXPATHLEN];
126 
127 		doaccess = name[0] == '/';
128 		if (!doaccess) {
129 			if ((p = TZDIR) == 0)
130 				return -1;
131 			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
132 				return -1;
133 			(void) strcpy(fullname, p);
134 			(void) strcat(fullname, "/");
135 			(void) strcat(fullname, name);
136 			/*
137 			** Set doaccess if '.' (as in "../") shows up in name.
138 			*/
139 			while (*name != '\0')
140 				if (*name++ == '.')
141 					doaccess = TRUE;
142 			name = fullname;
143 		}
144 		if (doaccess && access(name, 4) != 0)
145 			return -1;
146 		if ((fid = open(name, 0)) == -1)
147 			return -1;
148 	}
149 	{
150 		register char *			p;
151 		register struct tzhead *	tzhp;
152 		char				buf[sizeof s];
153 
154 		i = read(fid, buf, sizeof buf);
155 		if (close(fid) != 0 || i < sizeof *tzhp)
156 			return -1;
157 		tzhp = (struct tzhead *) buf;
158 		s.timecnt = (int) detzcode(tzhp->tzh_timecnt);
159 		s.typecnt = (int) detzcode(tzhp->tzh_typecnt);
160 		s.charcnt = (int) detzcode(tzhp->tzh_charcnt);
161 		if (s.timecnt > TZ_MAX_TIMES ||
162 			s.typecnt == 0 ||
163 			s.typecnt > TZ_MAX_TYPES ||
164 			s.charcnt > TZ_MAX_CHARS)
165 				return -1;
166 		if (i < sizeof *tzhp +
167 			s.timecnt * (4 + sizeof (char)) +
168 			s.typecnt * (4 + 2 * sizeof (char)) +
169 			s.charcnt * sizeof (char))
170 				return -1;
171 		p = buf + sizeof *tzhp;
172 		for (i = 0; i < s.timecnt; ++i) {
173 			s.ats[i] = detzcode(p);
174 			p += 4;
175 		}
176 		for (i = 0; i < s.timecnt; ++i)
177 			s.types[i] = (unsigned char) *p++;
178 		for (i = 0; i < s.typecnt; ++i) {
179 			register struct ttinfo *	ttisp;
180 
181 			ttisp = &s.ttis[i];
182 			ttisp->tt_gmtoff = detzcode(p);
183 			p += 4;
184 			ttisp->tt_isdst = (unsigned char) *p++;
185 			ttisp->tt_abbrind = (unsigned char) *p++;
186 		}
187 		for (i = 0; i < s.charcnt; ++i)
188 			s.chars[i] = *p++;
189 		s.chars[i] = '\0';	/* ensure '\0' at end */
190 	}
191 	/*
192 	** Check that all the local time type indices are valid.
193 	*/
194 	for (i = 0; i < s.timecnt; ++i)
195 		if (s.types[i] >= s.typecnt)
196 			return -1;
197 	/*
198 	** Check that all abbreviation indices are valid.
199 	*/
200 	for (i = 0; i < s.typecnt; ++i)
201 		if (s.ttis[i].tt_abbrind >= s.charcnt)
202 			return -1;
203 	/*
204 	** Set tzname elements to initial values.
205 	*/
206 	tzname[0] = tzname[1] = &s.chars[0];
207 #ifdef USG_COMPAT
208 	timezone = -s.ttis[0].tt_gmtoff;
209 	daylight = 0;
210 #endif /* USG_COMPAT */
211 	for (i = 1; i < s.typecnt; ++i) {
212 		register struct ttinfo *	ttisp;
213 
214 		ttisp = &s.ttis[i];
215 		if (ttisp->tt_isdst) {
216 			tzname[1] = &s.chars[ttisp->tt_abbrind];
217 #ifdef USG_COMPAT
218 			daylight = 1;
219 #endif /* USG_COMPAT */
220 		} else {
221 			tzname[0] = &s.chars[ttisp->tt_abbrind];
222 #ifdef USG_COMPAT
223 			timezone = -ttisp->tt_gmtoff;
224 #endif /* USG_COMPAT */
225 		}
226 	}
227 	return 0;
228 }
229 
230 static
231 tzsetkernel()
232 {
233 	struct timeval	tv;
234 	struct timezone	tz;
235 	char	*_tztab();
236 
237 	if (gettimeofday(&tv, &tz))
238 		return -1;
239 	s.timecnt = 0;		/* UNIX counts *west* of Greenwich */
240 	s.ttis[0].tt_gmtoff = tz.tz_minuteswest * -SECS_PER_MIN;
241 	s.ttis[0].tt_abbrind = 0;
242 	(void)strcpy(s.chars, _tztab(tz.tz_minuteswest, 0));
243 	tzname[0] = tzname[1] = s.chars;
244 #ifdef USG_COMPAT
245 	timezone = tz.tz_minuteswest * 60;
246 	daylight = tz.tz_dsttime;
247 #endif /* USG_COMPAT */
248 	return 0;
249 }
250 
251 static
252 tzsetgmt()
253 {
254 	s.timecnt = 0;
255 	s.ttis[0].tt_gmtoff = 0;
256 	s.ttis[0].tt_abbrind = 0;
257 	(void) strcpy(s.chars, "GMT");
258 	tzname[0] = tzname[1] = s.chars;
259 #ifdef USG_COMPAT
260 	timezone = 0;
261 	daylight = 0;
262 #endif /* USG_COMPAT */
263 }
264 
265 void
266 tzset()
267 {
268 	register char *	name;
269 
270 	tz_is_set = TRUE;
271 	name = getenv("TZ");
272 	if (!name || *name) {			/* did not request GMT */
273 		if (name && !tzload(name))	/* requested name worked */
274 			return;
275 		if (!tzload((char *)0))		/* default name worked */
276 			return;
277 		if (!tzsetkernel())		/* kernel guess worked */
278 			return;
279 	}
280 	tzsetgmt();				/* GMT is default */
281 }
282 
283 struct tm *
284 localtime(timep)
285 time_t *	timep;
286 {
287 	register struct ttinfo *	ttisp;
288 	register struct tm *		tmp;
289 	register int			i;
290 	time_t				t;
291 
292 	if (!tz_is_set)
293 		(void) tzset();
294 	t = *timep;
295 	if (s.timecnt == 0 || t < s.ats[0]) {
296 		i = 0;
297 		while (s.ttis[i].tt_isdst)
298 			if (++i >= s.timecnt) {
299 				i = 0;
300 				break;
301 			}
302 	} else {
303 		for (i = 1; i < s.timecnt; ++i)
304 			if (t < s.ats[i])
305 				break;
306 		i = s.types[i - 1];
307 	}
308 	ttisp = &s.ttis[i];
309 	/*
310 	** To get (wrong) behavior that's compatible with System V Release 2.0
311 	** you'd replace the statement below with
312 	**	tmp = offtime((time_t) (t + ttisp->tt_gmtoff), 0L);
313 	*/
314 	tmp = offtime(&t, ttisp->tt_gmtoff);
315 	tmp->tm_isdst = ttisp->tt_isdst;
316 	tzname[tmp->tm_isdst] = &s.chars[ttisp->tt_abbrind];
317 	tmp->tm_zone = &s.chars[ttisp->tt_abbrind];
318 	return tmp;
319 }
320 
321 struct tm *
322 gmtime(clock)
323 time_t *	clock;
324 {
325 	register struct tm *	tmp;
326 
327 	tmp = offtime(clock, 0L);
328 	tzname[0] = "GMT";
329 	tmp->tm_zone = "GMT";		/* UCT ? */
330 	return tmp;
331 }
332 
333 static int	mon_lengths[2][MONS_PER_YEAR] = {
334 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
335 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
336 };
337 
338 static int	year_lengths[2] = {
339 	DAYS_PER_NYEAR, DAYS_PER_LYEAR
340 };
341 
342 struct tm *
343 offtime(clock, offset)
344 time_t *	clock;
345 long		offset;
346 {
347 	register struct tm *	tmp;
348 	register long		days;
349 	register long		rem;
350 	register int		y;
351 	register int		yleap;
352 	register int *		ip;
353 	static struct tm	tm;
354 
355 	tmp = &tm;
356 	days = *clock / SECS_PER_DAY;
357 	rem = *clock % SECS_PER_DAY;
358 	rem += offset;
359 	while (rem < 0) {
360 		rem += SECS_PER_DAY;
361 		--days;
362 	}
363 	while (rem >= SECS_PER_DAY) {
364 		rem -= SECS_PER_DAY;
365 		++days;
366 	}
367 	tmp->tm_hour = (int) (rem / SECS_PER_HOUR);
368 	rem = rem % SECS_PER_HOUR;
369 	tmp->tm_min = (int) (rem / SECS_PER_MIN);
370 	tmp->tm_sec = (int) (rem % SECS_PER_MIN);
371 	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYS_PER_WEEK);
372 	if (tmp->tm_wday < 0)
373 		tmp->tm_wday += DAYS_PER_WEEK;
374 	y = EPOCH_YEAR;
375 	if (days >= 0)
376 		for ( ; ; ) {
377 			yleap = isleap(y);
378 			if (days < (long) year_lengths[yleap])
379 				break;
380 			++y;
381 			days = days - (long) year_lengths[yleap];
382 		}
383 	else do {
384 		--y;
385 		yleap = isleap(y);
386 		days = days + (long) year_lengths[yleap];
387 	} while (days < 0);
388 	tmp->tm_year = y - TM_YEAR_BASE;
389 	tmp->tm_yday = (int) days;
390 	ip = mon_lengths[yleap];
391 	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
392 		days = days - (long) ip[tmp->tm_mon];
393 	tmp->tm_mday = (int) (days + 1);
394 	tmp->tm_isdst = 0;
395 	tmp->tm_zone = "";
396 	tmp->tm_gmtoff = offset;
397 	return tmp;
398 }
399