xref: /csrg-svn/lib/libc/gen/ctime.c (revision 30684)
122084Smckusick /*
230682Sbostic  * Copyright (c) 1987 Regents of the University of California.
322084Smckusick  * All rights reserved.  The Berkeley software License Agreement
422084Smckusick  * specifies the terms and conditions for redistribution.
522084Smckusick  */
622084Smckusick 
7*30684Sbostic #if defined(LIBC_SCCS) && !defined(lint)
8*30684Sbostic static char sccsid[] = "@(#)ctime.c	5.8 (Berkeley) 03/28/87";
9*30684Sbostic #endif LIBC_SCCS and not lint
1022084Smckusick 
1130608Sbostic #include "sys/param.h"
1230682Sbostic #include "sys/time.h"
1330608Sbostic #include "tzfile.h"
141959Swnj 
1530608Sbostic char *
1630608Sbostic ctime(t)
1730608Sbostic time_t *t;
181959Swnj {
1930608Sbostic 	struct tm	*localtime();
2030608Sbostic 	char	*asctime();
211959Swnj 
2230608Sbostic 	return(asctime(localtime(t)));
2330608Sbostic }
2430608Sbostic 
251959Swnj /*
2630608Sbostic ** A la X3J11
2730608Sbostic */
281959Swnj 
2930608Sbostic char *
3030608Sbostic asctime(timeptr)
3130608Sbostic register struct tm *	timeptr;
3230608Sbostic {
3330608Sbostic 	static char	wday_name[DAYS_PER_WEEK][3] = {
3430608Sbostic 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
3530608Sbostic 	};
3630608Sbostic 	static char	mon_name[MONS_PER_YEAR][3] = {
3730608Sbostic 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
3830608Sbostic 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
3930608Sbostic 	};
4030608Sbostic 	static char	result[26];
4112974Ssam 
4230608Sbostic 	(void) sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
4330608Sbostic 		wday_name[timeptr->tm_wday],
4430608Sbostic 		mon_name[timeptr->tm_mon],
4530608Sbostic 		timeptr->tm_mday, timeptr->tm_hour,
4630608Sbostic 		timeptr->tm_min, timeptr->tm_sec,
4730608Sbostic 		TM_YEAR_BASE + timeptr->tm_year);
4830608Sbostic 	return result;
4930608Sbostic }
5013876Ssam 
5130608Sbostic #ifndef TRUE
5230608Sbostic #define TRUE		1
5330608Sbostic #define FALSE		0
5430608Sbostic #endif /* !TRUE */
5530608Sbostic 
5630608Sbostic extern char *		getenv();
5730608Sbostic extern char *		strcpy();
5830608Sbostic extern char *		strcat();
5930608Sbostic struct tm *		offtime();
6030608Sbostic 
6130608Sbostic struct ttinfo {				/* time type information */
6230608Sbostic 	long		tt_gmtoff;	/* GMT offset in seconds */
6330608Sbostic 	int		tt_isdst;	/* used to set tm_isdst */
6430608Sbostic 	int		tt_abbrind;	/* abbreviation list index */
6513876Ssam };
6613876Ssam 
6730608Sbostic struct state {
6830608Sbostic 	int		timecnt;
6930608Sbostic 	int		typecnt;
7030608Sbostic 	int		charcnt;
7130608Sbostic 	time_t		ats[TZ_MAX_TIMES];
7230608Sbostic 	unsigned char	types[TZ_MAX_TIMES];
7330608Sbostic 	struct ttinfo	ttis[TZ_MAX_TYPES];
7430608Sbostic 	char		chars[TZ_MAX_CHARS + 1];
7523720Skre };
7623720Skre 
7730608Sbostic static struct state	s;
7830608Sbostic 
7930608Sbostic static int		tz_is_set;
8030608Sbostic 
8130608Sbostic char *			tzname[2] = {
8230608Sbostic 	"GMT",
8330608Sbostic 	"GMT"
8412974Ssam };
8512974Ssam 
8630608Sbostic #ifdef USG_COMPAT
8730608Sbostic time_t			timezone = 0;
8830608Sbostic int			daylight = 0;
8930608Sbostic #endif /* USG_COMPAT */
901959Swnj 
9130608Sbostic static long
9230608Sbostic detzcode(codep)
9330608Sbostic char *	codep;
941959Swnj {
9530608Sbostic 	register long	result;
9630608Sbostic 	register int	i;
9730608Sbostic 
9830608Sbostic 	result = 0;
9930608Sbostic 	for (i = 0; i < 4; ++i)
10030608Sbostic 		result = (result << 8) | (codep[i] & 0xff);
10130608Sbostic 	return result;
1021959Swnj }
1031959Swnj 
10430608Sbostic static
10530608Sbostic tzload(name)
10630608Sbostic register char *	name;
1071959Swnj {
10830608Sbostic 	register int	i;
10930608Sbostic 	register int	fid;
1101959Swnj 
11130608Sbostic 	if (name == 0 && (name = TZDEFAULT) == 0)
11230608Sbostic 		return -1;
11330608Sbostic 	{
11430608Sbostic 		register char *	p;
11530608Sbostic 		register int	doaccess;
11630608Sbostic 		char		fullname[MAXPATHLEN];
11730608Sbostic 
11830608Sbostic 		doaccess = name[0] == '/';
11930608Sbostic 		if (!doaccess) {
12030608Sbostic 			if ((p = TZDIR) == 0)
12130608Sbostic 				return -1;
12230608Sbostic 			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
12330608Sbostic 				return -1;
12430608Sbostic 			(void) strcpy(fullname, p);
12530608Sbostic 			(void) strcat(fullname, "/");
12630608Sbostic 			(void) strcat(fullname, name);
12730608Sbostic 			/*
12830608Sbostic 			** Set doaccess if '.' (as in "../") shows up in name.
12930608Sbostic 			*/
13030608Sbostic 			while (*name != '\0')
13130608Sbostic 				if (*name++ == '.')
13230608Sbostic 					doaccess = TRUE;
13330608Sbostic 			name = fullname;
13430608Sbostic 		}
13530608Sbostic 		if (doaccess && access(name, 4) != 0)
13630608Sbostic 			return -1;
13730608Sbostic 		if ((fid = open(name, 0)) == -1)
13830608Sbostic 			return -1;
13925662Sbloom 	}
14030608Sbostic 	{
14130608Sbostic 		register char *			p;
14230608Sbostic 		register struct tzhead *	tzhp;
14330608Sbostic 		char				buf[sizeof s];
14430608Sbostic 
14530608Sbostic 		i = read(fid, buf, sizeof buf);
14630608Sbostic 		if (close(fid) != 0 || i < sizeof *tzhp)
14730608Sbostic 			return -1;
14830608Sbostic 		tzhp = (struct tzhead *) buf;
14930608Sbostic 		s.timecnt = (int) detzcode(tzhp->tzh_timecnt);
15030608Sbostic 		s.typecnt = (int) detzcode(tzhp->tzh_typecnt);
15130608Sbostic 		s.charcnt = (int) detzcode(tzhp->tzh_charcnt);
15230608Sbostic 		if (s.timecnt > TZ_MAX_TIMES ||
15330608Sbostic 			s.typecnt == 0 ||
15430608Sbostic 			s.typecnt > TZ_MAX_TYPES ||
15530608Sbostic 			s.charcnt > TZ_MAX_CHARS)
15630608Sbostic 				return -1;
15730608Sbostic 		if (i < sizeof *tzhp +
15830608Sbostic 			s.timecnt * (4 + sizeof (char)) +
15930608Sbostic 			s.typecnt * (4 + 2 * sizeof (char)) +
16030608Sbostic 			s.charcnt * sizeof (char))
16130608Sbostic 				return -1;
16230608Sbostic 		p = buf + sizeof *tzhp;
16330608Sbostic 		for (i = 0; i < s.timecnt; ++i) {
16430608Sbostic 			s.ats[i] = detzcode(p);
16530608Sbostic 			p += 4;
16612974Ssam 		}
16730608Sbostic 		for (i = 0; i < s.timecnt; ++i)
16830608Sbostic 			s.types[i] = (unsigned char) *p++;
16930608Sbostic 		for (i = 0; i < s.typecnt; ++i) {
17030608Sbostic 			register struct ttinfo *	ttisp;
17130608Sbostic 
17230608Sbostic 			ttisp = &s.ttis[i];
17330608Sbostic 			ttisp->tt_gmtoff = detzcode(p);
17430608Sbostic 			p += 4;
17530608Sbostic 			ttisp->tt_isdst = (unsigned char) *p++;
17630608Sbostic 			ttisp->tt_abbrind = (unsigned char) *p++;
17730608Sbostic 		}
17830608Sbostic 		for (i = 0; i < s.charcnt; ++i)
17930608Sbostic 			s.chars[i] = *p++;
18030608Sbostic 		s.chars[i] = '\0';	/* ensure '\0' at end */
1811959Swnj 	}
18230608Sbostic 	/*
18330608Sbostic 	** Check that all the local time type indices are valid.
18430608Sbostic 	*/
18530608Sbostic 	for (i = 0; i < s.timecnt; ++i)
18630608Sbostic 		if (s.types[i] >= s.typecnt)
18730608Sbostic 			return -1;
18830608Sbostic 	/*
18930608Sbostic 	** Check that all abbreviation indices are valid.
19030608Sbostic 	*/
19130608Sbostic 	for (i = 0; i < s.typecnt; ++i)
19230608Sbostic 		if (s.ttis[i].tt_abbrind >= s.charcnt)
19330608Sbostic 			return -1;
19430608Sbostic 	/*
19530608Sbostic 	** Set tzname elements to initial values.
19630608Sbostic 	*/
19730608Sbostic 	tzname[0] = tzname[1] = &s.chars[0];
19830608Sbostic #ifdef USG_COMPAT
19930608Sbostic 	timezone = s.ttis[0].tt_gmtoff;
20030608Sbostic 	daylight = 0;
20130608Sbostic #endif /* USG_COMPAT */
20230608Sbostic 	for (i = 1; i < s.typecnt; ++i) {
20330608Sbostic 		register struct ttinfo *	ttisp;
20430608Sbostic 
20530608Sbostic 		ttisp = &s.ttis[i];
20630608Sbostic 		if (ttisp->tt_isdst) {
20730608Sbostic 			tzname[1] = &s.chars[ttisp->tt_abbrind];
20830608Sbostic #ifdef USG_COMPAT
20930608Sbostic 			daylight = 1;
21030608Sbostic #endif /* USG_COMPAT */
21130608Sbostic 		} else {
21230608Sbostic 			tzname[0] = &s.chars[ttisp->tt_abbrind];
21330608Sbostic #ifdef USG_COMPAT
21430608Sbostic 			timezone = ttisp->tt_gmtoff;
21530608Sbostic #endif /* USG_COMPAT */
21630608Sbostic 		}
21730608Sbostic 	}
21830608Sbostic 	return 0;
2191959Swnj }
2201959Swnj 
2211959Swnj static
22230682Sbostic tzsetkernel()
22330682Sbostic {
22430682Sbostic 	struct timeval	tv;
22530682Sbostic 	struct timezone	tz;
22630682Sbostic 	char	*tztab();
22730682Sbostic 
22830682Sbostic 	if (gettimeofday(&tv, &tz))
22930682Sbostic 		return -1;
23030682Sbostic 	s.timecnt = 0;		/* UNIX counts *west* of Greenwich */
23130682Sbostic 	s.ttis[0].tt_gmtoff = tz.tz_minuteswest * -SECS_PER_MIN;
23230682Sbostic 	s.ttis[0].tt_abbrind = 0;
23330682Sbostic 	(void)strcpy(s.chars, tztab(tz.tz_minuteswest, 0));
23430682Sbostic 	tzname[0] = tzname[1] = s.chars;
23530682Sbostic #ifdef USG_COMPAT
23630682Sbostic 	timezone = tz.tz_minuteswest * 60;
23730682Sbostic 	daylight = tz.tz_dsttime;
23830682Sbostic #endif /* USG_COMPAT */
23930682Sbostic 	return 0;
24030682Sbostic }
24130682Sbostic 
24230682Sbostic static
24330608Sbostic tzsetgmt()
2441959Swnj {
24530608Sbostic 	s.timecnt = 0;
24630608Sbostic 	s.ttis[0].tt_gmtoff = 0;
24730608Sbostic 	s.ttis[0].tt_abbrind = 0;
24830608Sbostic 	(void) strcpy(s.chars, "GMT");
24930608Sbostic 	tzname[0] = tzname[1] = s.chars;
25030608Sbostic #ifdef USG_COMPAT
25130608Sbostic 	timezone = 0;
25230608Sbostic 	daylight = 0;
25330608Sbostic #endif /* USG_COMPAT */
2541959Swnj }
2551959Swnj 
25630608Sbostic void
25730608Sbostic tzset()
25830608Sbostic {
25930608Sbostic 	register char *	name;
26030608Sbostic 
26130608Sbostic 	tz_is_set = TRUE;
26230608Sbostic 	name = getenv("TZ");
26330682Sbostic 	if (!name || *name) {			/* did not request GMT */
26430682Sbostic 		if (name && !tzload(name))	/* requested name worked */
26530682Sbostic 			return;
26630682Sbostic 		if (!tzload((char *)0))		/* default name worked */
26730682Sbostic 			return;
26830682Sbostic 		if (!tzsetkernel())		/* kernel guess worked */
26930682Sbostic 			return;
27030682Sbostic 	}
27130682Sbostic 	tzsetgmt();				/* GMT is default */
27230608Sbostic }
27330608Sbostic 
2741959Swnj struct tm *
27530608Sbostic localtime(timep)
27630608Sbostic time_t *	timep;
2771959Swnj {
27830608Sbostic 	register struct ttinfo *	ttisp;
27930608Sbostic 	register struct tm *		tmp;
28030608Sbostic 	register int			i;
28130608Sbostic 	time_t				t;
2821959Swnj 
28330608Sbostic 	if (!tz_is_set)
28430608Sbostic 		(void) tzset();
28530608Sbostic 	t = *timep;
28630608Sbostic 	if (s.timecnt == 0 || t < s.ats[0]) {
28730608Sbostic 		i = 0;
28830608Sbostic 		while (s.ttis[i].tt_isdst)
28930608Sbostic 			if (++i >= s.timecnt) {
29030608Sbostic 				i = 0;
29130608Sbostic 				break;
29230608Sbostic 			}
29330608Sbostic 	} else {
29430608Sbostic 		for (i = 1; i < s.timecnt; ++i)
29530608Sbostic 			if (t < s.ats[i])
29630608Sbostic 				break;
29730608Sbostic 		i = s.types[i - 1];
2981959Swnj 	}
29930608Sbostic 	ttisp = &s.ttis[i];
3001959Swnj 	/*
30130608Sbostic 	** To get (wrong) behavior that's compatible with System V Release 2.0
30230608Sbostic 	** you'd replace the statement below with
30330608Sbostic 	**	tmp = offtime((time_t) (t + ttisp->tt_gmtoff), 0L);
30430608Sbostic 	*/
30530608Sbostic 	tmp = offtime(&t, ttisp->tt_gmtoff);
30630608Sbostic 	tmp->tm_isdst = ttisp->tt_isdst;
30730608Sbostic 	tzname[tmp->tm_isdst] = &s.chars[ttisp->tt_abbrind];
30830608Sbostic 	tmp->tm_zone = &s.chars[ttisp->tt_abbrind];
30930608Sbostic 	return tmp;
31030608Sbostic }
3111959Swnj 
31230608Sbostic struct tm *
31330608Sbostic gmtime(clock)
31430608Sbostic time_t *	clock;
31530608Sbostic {
31630608Sbostic 	register struct tm *	tmp;
3171959Swnj 
31830608Sbostic 	tmp = offtime(clock, 0L);
31930608Sbostic 	tzname[0] = "GMT";
32030608Sbostic 	tmp->tm_zone = "GMT";		/* UCT ? */
32130608Sbostic 	return tmp;
32230608Sbostic }
3231959Swnj 
32430608Sbostic static int	mon_lengths[2][MONS_PER_YEAR] = {
32530608Sbostic 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
32630608Sbostic 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
32730608Sbostic };
3281959Swnj 
32930608Sbostic static int	year_lengths[2] = {
33030608Sbostic 	DAYS_PER_NYEAR, DAYS_PER_LYEAR
33130608Sbostic };
3321959Swnj 
33330608Sbostic struct tm *
33430608Sbostic offtime(clock, offset)
33530608Sbostic time_t *	clock;
33630608Sbostic long		offset;
3371959Swnj {
33830608Sbostic 	register struct tm *	tmp;
33930608Sbostic 	register long		days;
34030608Sbostic 	register long		rem;
34130608Sbostic 	register int		y;
34230608Sbostic 	register int		yleap;
34330608Sbostic 	register int *		ip;
34430608Sbostic 	static struct tm	tm;
3451959Swnj 
34630608Sbostic 	tmp = &tm;
34730608Sbostic 	days = *clock / SECS_PER_DAY;
34830608Sbostic 	rem = *clock % SECS_PER_DAY;
34930608Sbostic 	rem += offset;
35030608Sbostic 	while (rem < 0) {
35130608Sbostic 		rem += SECS_PER_DAY;
35230608Sbostic 		--days;
3531959Swnj 	}
35430608Sbostic 	while (rem >= SECS_PER_DAY) {
35530608Sbostic 		rem -= SECS_PER_DAY;
35630608Sbostic 		++days;
35730608Sbostic 	}
35830608Sbostic 	tmp->tm_hour = (int) (rem / SECS_PER_HOUR);
35930608Sbostic 	rem = rem % SECS_PER_HOUR;
36030608Sbostic 	tmp->tm_min = (int) (rem / SECS_PER_MIN);
36130608Sbostic 	tmp->tm_sec = (int) (rem % SECS_PER_MIN);
36230608Sbostic 	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYS_PER_WEEK);
36330608Sbostic 	if (tmp->tm_wday < 0)
36430608Sbostic 		tmp->tm_wday += DAYS_PER_WEEK;
36530608Sbostic 	y = EPOCH_YEAR;
36630608Sbostic 	if (days >= 0)
36730608Sbostic 		for ( ; ; ) {
36830608Sbostic 			yleap = isleap(y);
36930608Sbostic 			if (days < (long) year_lengths[yleap])
37030608Sbostic 				break;
37130608Sbostic 			++y;
37230608Sbostic 			days = days - (long) year_lengths[yleap];
37330608Sbostic 		}
37430608Sbostic 	else do {
37530608Sbostic 		--y;
37630608Sbostic 		yleap = isleap(y);
37730608Sbostic 		days = days + (long) year_lengths[yleap];
37830608Sbostic 	} while (days < 0);
37930608Sbostic 	tmp->tm_year = y - TM_YEAR_BASE;
38030608Sbostic 	tmp->tm_yday = (int) days;
38130608Sbostic 	ip = mon_lengths[yleap];
38230608Sbostic 	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
38330608Sbostic 		days = days - (long) ip[tmp->tm_mon];
38430608Sbostic 	tmp->tm_mday = (int) (days + 1);
38530608Sbostic 	tmp->tm_isdst = 0;
38630608Sbostic 	tmp->tm_zone = "";
38730608Sbostic 	tmp->tm_gmtoff = offset;
38830608Sbostic 	return tmp;
3891959Swnj }
390