xref: /csrg-svn/lib/libc/gen/ctime.c (revision 34821)
122084Smckusick /*
230682Sbostic  * Copyright (c) 1987 Regents of the University of California.
332754Sbostic  * All rights reserved.
432754Sbostic  *
532754Sbostic  * Redistribution and use in source and binary forms are permitted
6*34821Sbostic  * provided that the above copyright notice and this paragraph are
7*34821Sbostic  * duplicated in all such forms and that any documentation,
8*34821Sbostic  * advertising materials, and other materials related to such
9*34821Sbostic  * distribution and use acknowledge that the software was developed
10*34821Sbostic  * by the University of California, Berkeley.  The name of the
11*34821Sbostic  * University may not be used to endorse or promote products derived
12*34821Sbostic  * from this software without specific prior written permission.
13*34821Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34821Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34821Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1632754Sbostic  *
1732754Sbostic  * This code was supplied by Arthur Olson, originally, and has been
1832754Sbostic  * slightly modified at the University of California.
1922084Smckusick  */
2022084Smckusick 
2130684Sbostic #if defined(LIBC_SCCS) && !defined(lint)
22*34821Sbostic static char sccsid[] = "@(#)ctime.c	5.13 (Berkeley) 06/27/88";
2332754Sbostic #endif /* LIBC_SCCS and not lint */
2422084Smckusick 
2530608Sbostic #include "sys/param.h"
2630682Sbostic #include "sys/time.h"
2730608Sbostic #include "tzfile.h"
281959Swnj 
2930608Sbostic char *
3030608Sbostic ctime(t)
3130608Sbostic time_t *t;
321959Swnj {
3330608Sbostic 	struct tm	*localtime();
3430608Sbostic 	char	*asctime();
351959Swnj 
3630608Sbostic 	return(asctime(localtime(t)));
3730608Sbostic }
3830608Sbostic 
391959Swnj /*
4030608Sbostic ** A la X3J11
4130608Sbostic */
421959Swnj 
4330608Sbostic char *
4430608Sbostic asctime(timeptr)
4530608Sbostic register struct tm *	timeptr;
4630608Sbostic {
4730608Sbostic 	static char	wday_name[DAYS_PER_WEEK][3] = {
4830608Sbostic 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
4930608Sbostic 	};
5030608Sbostic 	static char	mon_name[MONS_PER_YEAR][3] = {
5130608Sbostic 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
5230608Sbostic 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
5330608Sbostic 	};
5430608Sbostic 	static char	result[26];
5512974Ssam 
5630809Sbostic 	(void) sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
5730608Sbostic 		wday_name[timeptr->tm_wday],
5830608Sbostic 		mon_name[timeptr->tm_mon],
5930608Sbostic 		timeptr->tm_mday, timeptr->tm_hour,
6030608Sbostic 		timeptr->tm_min, timeptr->tm_sec,
6130608Sbostic 		TM_YEAR_BASE + timeptr->tm_year);
6230608Sbostic 	return result;
6330608Sbostic }
6413876Ssam 
6530608Sbostic #ifndef TRUE
6630608Sbostic #define TRUE		1
6730608Sbostic #define FALSE		0
6830608Sbostic #endif /* !TRUE */
6930608Sbostic 
7030608Sbostic extern char *		getenv();
7130608Sbostic extern char *		strcpy();
7230608Sbostic extern char *		strcat();
7330608Sbostic struct tm *		offtime();
7430608Sbostic 
7530608Sbostic struct ttinfo {				/* time type information */
7630608Sbostic 	long		tt_gmtoff;	/* GMT offset in seconds */
7730608Sbostic 	int		tt_isdst;	/* used to set tm_isdst */
7830608Sbostic 	int		tt_abbrind;	/* abbreviation list index */
7913876Ssam };
8013876Ssam 
8130608Sbostic struct state {
8230608Sbostic 	int		timecnt;
8330608Sbostic 	int		typecnt;
8430608Sbostic 	int		charcnt;
8530608Sbostic 	time_t		ats[TZ_MAX_TIMES];
8630608Sbostic 	unsigned char	types[TZ_MAX_TIMES];
8730608Sbostic 	struct ttinfo	ttis[TZ_MAX_TYPES];
8830608Sbostic 	char		chars[TZ_MAX_CHARS + 1];
8923720Skre };
9023720Skre 
9130608Sbostic static struct state	s;
9230608Sbostic 
9330608Sbostic static int		tz_is_set;
9430608Sbostic 
9530608Sbostic char *			tzname[2] = {
9630608Sbostic 	"GMT",
9730608Sbostic 	"GMT"
9812974Ssam };
9912974Ssam 
10030608Sbostic #ifdef USG_COMPAT
10130608Sbostic time_t			timezone = 0;
10230608Sbostic int			daylight = 0;
10330608Sbostic #endif /* USG_COMPAT */
1041959Swnj 
10530608Sbostic static long
10630608Sbostic detzcode(codep)
10730608Sbostic char *	codep;
1081959Swnj {
10930608Sbostic 	register long	result;
11030608Sbostic 	register int	i;
11130608Sbostic 
11230608Sbostic 	result = 0;
11330608Sbostic 	for (i = 0; i < 4; ++i)
11430608Sbostic 		result = (result << 8) | (codep[i] & 0xff);
11530608Sbostic 	return result;
1161959Swnj }
1171959Swnj 
11830608Sbostic static
11930608Sbostic tzload(name)
12030608Sbostic register char *	name;
1211959Swnj {
12230608Sbostic 	register int	i;
12330608Sbostic 	register int	fid;
1241959Swnj 
12530608Sbostic 	if (name == 0 && (name = TZDEFAULT) == 0)
12630608Sbostic 		return -1;
12730608Sbostic 	{
12830608Sbostic 		register char *	p;
12930608Sbostic 		register int	doaccess;
13030608Sbostic 		char		fullname[MAXPATHLEN];
13130608Sbostic 
13230608Sbostic 		doaccess = name[0] == '/';
13330608Sbostic 		if (!doaccess) {
13430608Sbostic 			if ((p = TZDIR) == 0)
13530608Sbostic 				return -1;
13630608Sbostic 			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
13730608Sbostic 				return -1;
13830608Sbostic 			(void) strcpy(fullname, p);
13930608Sbostic 			(void) strcat(fullname, "/");
14030608Sbostic 			(void) strcat(fullname, name);
14130608Sbostic 			/*
14230608Sbostic 			** Set doaccess if '.' (as in "../") shows up in name.
14330608Sbostic 			*/
14430608Sbostic 			while (*name != '\0')
14530608Sbostic 				if (*name++ == '.')
14630608Sbostic 					doaccess = TRUE;
14730608Sbostic 			name = fullname;
14830608Sbostic 		}
14930608Sbostic 		if (doaccess && access(name, 4) != 0)
15030608Sbostic 			return -1;
15130608Sbostic 		if ((fid = open(name, 0)) == -1)
15230608Sbostic 			return -1;
15325662Sbloom 	}
15430608Sbostic 	{
15530608Sbostic 		register char *			p;
15630608Sbostic 		register struct tzhead *	tzhp;
15730608Sbostic 		char				buf[sizeof s];
15830608Sbostic 
15930608Sbostic 		i = read(fid, buf, sizeof buf);
16030608Sbostic 		if (close(fid) != 0 || i < sizeof *tzhp)
16130608Sbostic 			return -1;
16230608Sbostic 		tzhp = (struct tzhead *) buf;
16330608Sbostic 		s.timecnt = (int) detzcode(tzhp->tzh_timecnt);
16430608Sbostic 		s.typecnt = (int) detzcode(tzhp->tzh_typecnt);
16530608Sbostic 		s.charcnt = (int) detzcode(tzhp->tzh_charcnt);
16630608Sbostic 		if (s.timecnt > TZ_MAX_TIMES ||
16730608Sbostic 			s.typecnt == 0 ||
16830608Sbostic 			s.typecnt > TZ_MAX_TYPES ||
16930608Sbostic 			s.charcnt > TZ_MAX_CHARS)
17030608Sbostic 				return -1;
17130608Sbostic 		if (i < sizeof *tzhp +
17230608Sbostic 			s.timecnt * (4 + sizeof (char)) +
17330608Sbostic 			s.typecnt * (4 + 2 * sizeof (char)) +
17430608Sbostic 			s.charcnt * sizeof (char))
17530608Sbostic 				return -1;
17630608Sbostic 		p = buf + sizeof *tzhp;
17730608Sbostic 		for (i = 0; i < s.timecnt; ++i) {
17830608Sbostic 			s.ats[i] = detzcode(p);
17930608Sbostic 			p += 4;
18012974Ssam 		}
18130608Sbostic 		for (i = 0; i < s.timecnt; ++i)
18230608Sbostic 			s.types[i] = (unsigned char) *p++;
18330608Sbostic 		for (i = 0; i < s.typecnt; ++i) {
18430608Sbostic 			register struct ttinfo *	ttisp;
18530608Sbostic 
18630608Sbostic 			ttisp = &s.ttis[i];
18730608Sbostic 			ttisp->tt_gmtoff = detzcode(p);
18830608Sbostic 			p += 4;
18930608Sbostic 			ttisp->tt_isdst = (unsigned char) *p++;
19030608Sbostic 			ttisp->tt_abbrind = (unsigned char) *p++;
19130608Sbostic 		}
19230608Sbostic 		for (i = 0; i < s.charcnt; ++i)
19330608Sbostic 			s.chars[i] = *p++;
19430608Sbostic 		s.chars[i] = '\0';	/* ensure '\0' at end */
1951959Swnj 	}
19630608Sbostic 	/*
19730608Sbostic 	** Check that all the local time type indices are valid.
19830608Sbostic 	*/
19930608Sbostic 	for (i = 0; i < s.timecnt; ++i)
20030608Sbostic 		if (s.types[i] >= s.typecnt)
20130608Sbostic 			return -1;
20230608Sbostic 	/*
20330608Sbostic 	** Check that all abbreviation indices are valid.
20430608Sbostic 	*/
20530608Sbostic 	for (i = 0; i < s.typecnt; ++i)
20630608Sbostic 		if (s.ttis[i].tt_abbrind >= s.charcnt)
20730608Sbostic 			return -1;
20830608Sbostic 	/*
20930608Sbostic 	** Set tzname elements to initial values.
21030608Sbostic 	*/
21130608Sbostic 	tzname[0] = tzname[1] = &s.chars[0];
21230608Sbostic #ifdef USG_COMPAT
21330898Sbostic 	timezone = -s.ttis[0].tt_gmtoff;
21430608Sbostic 	daylight = 0;
21530608Sbostic #endif /* USG_COMPAT */
21630608Sbostic 	for (i = 1; i < s.typecnt; ++i) {
21730608Sbostic 		register struct ttinfo *	ttisp;
21830608Sbostic 
21930608Sbostic 		ttisp = &s.ttis[i];
22030608Sbostic 		if (ttisp->tt_isdst) {
22130608Sbostic 			tzname[1] = &s.chars[ttisp->tt_abbrind];
22230608Sbostic #ifdef USG_COMPAT
22330608Sbostic 			daylight = 1;
22430608Sbostic #endif /* USG_COMPAT */
22530608Sbostic 		} else {
22630608Sbostic 			tzname[0] = &s.chars[ttisp->tt_abbrind];
22730608Sbostic #ifdef USG_COMPAT
22830898Sbostic 			timezone = -ttisp->tt_gmtoff;
22930608Sbostic #endif /* USG_COMPAT */
23030608Sbostic 		}
23130608Sbostic 	}
23230608Sbostic 	return 0;
2331959Swnj }
2341959Swnj 
2351959Swnj static
23630682Sbostic tzsetkernel()
23730682Sbostic {
23830682Sbostic 	struct timeval	tv;
23930682Sbostic 	struct timezone	tz;
24030714Sbostic 	char	*_tztab();
24130682Sbostic 
24230682Sbostic 	if (gettimeofday(&tv, &tz))
24330682Sbostic 		return -1;
24430682Sbostic 	s.timecnt = 0;		/* UNIX counts *west* of Greenwich */
24530682Sbostic 	s.ttis[0].tt_gmtoff = tz.tz_minuteswest * -SECS_PER_MIN;
24630682Sbostic 	s.ttis[0].tt_abbrind = 0;
24730714Sbostic 	(void)strcpy(s.chars, _tztab(tz.tz_minuteswest, 0));
24830682Sbostic 	tzname[0] = tzname[1] = s.chars;
24930682Sbostic #ifdef USG_COMPAT
25030682Sbostic 	timezone = tz.tz_minuteswest * 60;
25130682Sbostic 	daylight = tz.tz_dsttime;
25230682Sbostic #endif /* USG_COMPAT */
25330682Sbostic 	return 0;
25430682Sbostic }
25530682Sbostic 
25630682Sbostic static
25730608Sbostic tzsetgmt()
2581959Swnj {
25930608Sbostic 	s.timecnt = 0;
26030608Sbostic 	s.ttis[0].tt_gmtoff = 0;
26130608Sbostic 	s.ttis[0].tt_abbrind = 0;
26230608Sbostic 	(void) strcpy(s.chars, "GMT");
26330608Sbostic 	tzname[0] = tzname[1] = s.chars;
26430608Sbostic #ifdef USG_COMPAT
26530608Sbostic 	timezone = 0;
26630608Sbostic 	daylight = 0;
26730608Sbostic #endif /* USG_COMPAT */
2681959Swnj }
2691959Swnj 
27030608Sbostic void
27130608Sbostic tzset()
27230608Sbostic {
27330608Sbostic 	register char *	name;
27430608Sbostic 
27530608Sbostic 	tz_is_set = TRUE;
27630608Sbostic 	name = getenv("TZ");
27730682Sbostic 	if (!name || *name) {			/* did not request GMT */
27830682Sbostic 		if (name && !tzload(name))	/* requested name worked */
27930682Sbostic 			return;
28030682Sbostic 		if (!tzload((char *)0))		/* default name worked */
28130682Sbostic 			return;
28230682Sbostic 		if (!tzsetkernel())		/* kernel guess worked */
28330682Sbostic 			return;
28430682Sbostic 	}
28530682Sbostic 	tzsetgmt();				/* GMT is default */
28630608Sbostic }
28730608Sbostic 
2881959Swnj struct tm *
28930608Sbostic localtime(timep)
29030608Sbostic time_t *	timep;
2911959Swnj {
29230608Sbostic 	register struct ttinfo *	ttisp;
29330608Sbostic 	register struct tm *		tmp;
29430608Sbostic 	register int			i;
29530608Sbostic 	time_t				t;
2961959Swnj 
29730608Sbostic 	if (!tz_is_set)
29830608Sbostic 		(void) tzset();
29930608Sbostic 	t = *timep;
30030608Sbostic 	if (s.timecnt == 0 || t < s.ats[0]) {
30130608Sbostic 		i = 0;
30230608Sbostic 		while (s.ttis[i].tt_isdst)
30330608Sbostic 			if (++i >= s.timecnt) {
30430608Sbostic 				i = 0;
30530608Sbostic 				break;
30630608Sbostic 			}
30730608Sbostic 	} else {
30830608Sbostic 		for (i = 1; i < s.timecnt; ++i)
30930608Sbostic 			if (t < s.ats[i])
31030608Sbostic 				break;
31130608Sbostic 		i = s.types[i - 1];
3121959Swnj 	}
31330608Sbostic 	ttisp = &s.ttis[i];
3141959Swnj 	/*
31530608Sbostic 	** To get (wrong) behavior that's compatible with System V Release 2.0
31630608Sbostic 	** you'd replace the statement below with
31730608Sbostic 	**	tmp = offtime((time_t) (t + ttisp->tt_gmtoff), 0L);
31830608Sbostic 	*/
31930608Sbostic 	tmp = offtime(&t, ttisp->tt_gmtoff);
32030608Sbostic 	tmp->tm_isdst = ttisp->tt_isdst;
32130608Sbostic 	tzname[tmp->tm_isdst] = &s.chars[ttisp->tt_abbrind];
32230608Sbostic 	tmp->tm_zone = &s.chars[ttisp->tt_abbrind];
32330608Sbostic 	return tmp;
32430608Sbostic }
3251959Swnj 
32630608Sbostic struct tm *
32730608Sbostic gmtime(clock)
32830608Sbostic time_t *	clock;
32930608Sbostic {
33030608Sbostic 	register struct tm *	tmp;
3311959Swnj 
33230608Sbostic 	tmp = offtime(clock, 0L);
33330608Sbostic 	tzname[0] = "GMT";
33430608Sbostic 	tmp->tm_zone = "GMT";		/* UCT ? */
33530608Sbostic 	return tmp;
33630608Sbostic }
3371959Swnj 
33830608Sbostic static int	mon_lengths[2][MONS_PER_YEAR] = {
33930608Sbostic 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
34030608Sbostic 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
34130608Sbostic };
3421959Swnj 
34330608Sbostic static int	year_lengths[2] = {
34430608Sbostic 	DAYS_PER_NYEAR, DAYS_PER_LYEAR
34530608Sbostic };
3461959Swnj 
34730608Sbostic struct tm *
34830608Sbostic offtime(clock, offset)
34930608Sbostic time_t *	clock;
35030608Sbostic long		offset;
3511959Swnj {
35230608Sbostic 	register struct tm *	tmp;
35330608Sbostic 	register long		days;
35430608Sbostic 	register long		rem;
35530608Sbostic 	register int		y;
35630608Sbostic 	register int		yleap;
35730608Sbostic 	register int *		ip;
35830608Sbostic 	static struct tm	tm;
3591959Swnj 
36030608Sbostic 	tmp = &tm;
36130608Sbostic 	days = *clock / SECS_PER_DAY;
36230608Sbostic 	rem = *clock % SECS_PER_DAY;
36330608Sbostic 	rem += offset;
36430608Sbostic 	while (rem < 0) {
36530608Sbostic 		rem += SECS_PER_DAY;
36630608Sbostic 		--days;
3671959Swnj 	}
36830608Sbostic 	while (rem >= SECS_PER_DAY) {
36930608Sbostic 		rem -= SECS_PER_DAY;
37030608Sbostic 		++days;
37130608Sbostic 	}
37230608Sbostic 	tmp->tm_hour = (int) (rem / SECS_PER_HOUR);
37330608Sbostic 	rem = rem % SECS_PER_HOUR;
37430608Sbostic 	tmp->tm_min = (int) (rem / SECS_PER_MIN);
37530608Sbostic 	tmp->tm_sec = (int) (rem % SECS_PER_MIN);
37630608Sbostic 	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYS_PER_WEEK);
37730608Sbostic 	if (tmp->tm_wday < 0)
37830608Sbostic 		tmp->tm_wday += DAYS_PER_WEEK;
37930608Sbostic 	y = EPOCH_YEAR;
38030608Sbostic 	if (days >= 0)
38130608Sbostic 		for ( ; ; ) {
38230608Sbostic 			yleap = isleap(y);
38330608Sbostic 			if (days < (long) year_lengths[yleap])
38430608Sbostic 				break;
38530608Sbostic 			++y;
38630608Sbostic 			days = days - (long) year_lengths[yleap];
38730608Sbostic 		}
38830608Sbostic 	else do {
38930608Sbostic 		--y;
39030608Sbostic 		yleap = isleap(y);
39130608Sbostic 		days = days + (long) year_lengths[yleap];
39230608Sbostic 	} while (days < 0);
39330608Sbostic 	tmp->tm_year = y - TM_YEAR_BASE;
39430608Sbostic 	tmp->tm_yday = (int) days;
39530608Sbostic 	ip = mon_lengths[yleap];
39630608Sbostic 	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
39730608Sbostic 		days = days - (long) ip[tmp->tm_mon];
39830608Sbostic 	tmp->tm_mday = (int) (days + 1);
39930608Sbostic 	tmp->tm_isdst = 0;
40030608Sbostic 	tmp->tm_zone = "";
40130608Sbostic 	tmp->tm_gmtoff = offset;
40230608Sbostic 	return tmp;
4031959Swnj }
404