xref: /csrg-svn/lib/libc/gen/ctime.c (revision 32754)
122084Smckusick /*
230682Sbostic  * Copyright (c) 1987 Regents of the University of California.
3*32754Sbostic  * All rights reserved.
4*32754Sbostic  *
5*32754Sbostic  * Redistribution and use in source and binary forms are permitted
6*32754Sbostic  * provided that this notice is preserved and that due credit is given
7*32754Sbostic  * to the University of California at Berkeley. The name of the University
8*32754Sbostic  * may not be used to endorse or promote products derived from this
9*32754Sbostic  * software without specific written prior permission. This software
10*32754Sbostic  * is provided ``as is'' without express or implied warranty.
11*32754Sbostic  *
12*32754Sbostic  * This code was supplied by Arthur Olson, originally, and has been
13*32754Sbostic  * slightly modified at the University of California.
1422084Smckusick  */
1522084Smckusick 
1630684Sbostic #if defined(LIBC_SCCS) && !defined(lint)
17*32754Sbostic static char sccsid[] = "@(#)ctime.c	5.12 (Berkeley) 12/03/87";
18*32754Sbostic #endif /* LIBC_SCCS and not lint */
1922084Smckusick 
2030608Sbostic #include "sys/param.h"
2130682Sbostic #include "sys/time.h"
2230608Sbostic #include "tzfile.h"
231959Swnj 
2430608Sbostic char *
2530608Sbostic ctime(t)
2630608Sbostic time_t *t;
271959Swnj {
2830608Sbostic 	struct tm	*localtime();
2930608Sbostic 	char	*asctime();
301959Swnj 
3130608Sbostic 	return(asctime(localtime(t)));
3230608Sbostic }
3330608Sbostic 
341959Swnj /*
3530608Sbostic ** A la X3J11
3630608Sbostic */
371959Swnj 
3830608Sbostic char *
3930608Sbostic asctime(timeptr)
4030608Sbostic register struct tm *	timeptr;
4130608Sbostic {
4230608Sbostic 	static char	wday_name[DAYS_PER_WEEK][3] = {
4330608Sbostic 		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
4430608Sbostic 	};
4530608Sbostic 	static char	mon_name[MONS_PER_YEAR][3] = {
4630608Sbostic 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
4730608Sbostic 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
4830608Sbostic 	};
4930608Sbostic 	static char	result[26];
5012974Ssam 
5130809Sbostic 	(void) sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
5230608Sbostic 		wday_name[timeptr->tm_wday],
5330608Sbostic 		mon_name[timeptr->tm_mon],
5430608Sbostic 		timeptr->tm_mday, timeptr->tm_hour,
5530608Sbostic 		timeptr->tm_min, timeptr->tm_sec,
5630608Sbostic 		TM_YEAR_BASE + timeptr->tm_year);
5730608Sbostic 	return result;
5830608Sbostic }
5913876Ssam 
6030608Sbostic #ifndef TRUE
6130608Sbostic #define TRUE		1
6230608Sbostic #define FALSE		0
6330608Sbostic #endif /* !TRUE */
6430608Sbostic 
6530608Sbostic extern char *		getenv();
6630608Sbostic extern char *		strcpy();
6730608Sbostic extern char *		strcat();
6830608Sbostic struct tm *		offtime();
6930608Sbostic 
7030608Sbostic struct ttinfo {				/* time type information */
7130608Sbostic 	long		tt_gmtoff;	/* GMT offset in seconds */
7230608Sbostic 	int		tt_isdst;	/* used to set tm_isdst */
7330608Sbostic 	int		tt_abbrind;	/* abbreviation list index */
7413876Ssam };
7513876Ssam 
7630608Sbostic struct state {
7730608Sbostic 	int		timecnt;
7830608Sbostic 	int		typecnt;
7930608Sbostic 	int		charcnt;
8030608Sbostic 	time_t		ats[TZ_MAX_TIMES];
8130608Sbostic 	unsigned char	types[TZ_MAX_TIMES];
8230608Sbostic 	struct ttinfo	ttis[TZ_MAX_TYPES];
8330608Sbostic 	char		chars[TZ_MAX_CHARS + 1];
8423720Skre };
8523720Skre 
8630608Sbostic static struct state	s;
8730608Sbostic 
8830608Sbostic static int		tz_is_set;
8930608Sbostic 
9030608Sbostic char *			tzname[2] = {
9130608Sbostic 	"GMT",
9230608Sbostic 	"GMT"
9312974Ssam };
9412974Ssam 
9530608Sbostic #ifdef USG_COMPAT
9630608Sbostic time_t			timezone = 0;
9730608Sbostic int			daylight = 0;
9830608Sbostic #endif /* USG_COMPAT */
991959Swnj 
10030608Sbostic static long
10130608Sbostic detzcode(codep)
10230608Sbostic char *	codep;
1031959Swnj {
10430608Sbostic 	register long	result;
10530608Sbostic 	register int	i;
10630608Sbostic 
10730608Sbostic 	result = 0;
10830608Sbostic 	for (i = 0; i < 4; ++i)
10930608Sbostic 		result = (result << 8) | (codep[i] & 0xff);
11030608Sbostic 	return result;
1111959Swnj }
1121959Swnj 
11330608Sbostic static
11430608Sbostic tzload(name)
11530608Sbostic register char *	name;
1161959Swnj {
11730608Sbostic 	register int	i;
11830608Sbostic 	register int	fid;
1191959Swnj 
12030608Sbostic 	if (name == 0 && (name = TZDEFAULT) == 0)
12130608Sbostic 		return -1;
12230608Sbostic 	{
12330608Sbostic 		register char *	p;
12430608Sbostic 		register int	doaccess;
12530608Sbostic 		char		fullname[MAXPATHLEN];
12630608Sbostic 
12730608Sbostic 		doaccess = name[0] == '/';
12830608Sbostic 		if (!doaccess) {
12930608Sbostic 			if ((p = TZDIR) == 0)
13030608Sbostic 				return -1;
13130608Sbostic 			if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
13230608Sbostic 				return -1;
13330608Sbostic 			(void) strcpy(fullname, p);
13430608Sbostic 			(void) strcat(fullname, "/");
13530608Sbostic 			(void) strcat(fullname, name);
13630608Sbostic 			/*
13730608Sbostic 			** Set doaccess if '.' (as in "../") shows up in name.
13830608Sbostic 			*/
13930608Sbostic 			while (*name != '\0')
14030608Sbostic 				if (*name++ == '.')
14130608Sbostic 					doaccess = TRUE;
14230608Sbostic 			name = fullname;
14330608Sbostic 		}
14430608Sbostic 		if (doaccess && access(name, 4) != 0)
14530608Sbostic 			return -1;
14630608Sbostic 		if ((fid = open(name, 0)) == -1)
14730608Sbostic 			return -1;
14825662Sbloom 	}
14930608Sbostic 	{
15030608Sbostic 		register char *			p;
15130608Sbostic 		register struct tzhead *	tzhp;
15230608Sbostic 		char				buf[sizeof s];
15330608Sbostic 
15430608Sbostic 		i = read(fid, buf, sizeof buf);
15530608Sbostic 		if (close(fid) != 0 || i < sizeof *tzhp)
15630608Sbostic 			return -1;
15730608Sbostic 		tzhp = (struct tzhead *) buf;
15830608Sbostic 		s.timecnt = (int) detzcode(tzhp->tzh_timecnt);
15930608Sbostic 		s.typecnt = (int) detzcode(tzhp->tzh_typecnt);
16030608Sbostic 		s.charcnt = (int) detzcode(tzhp->tzh_charcnt);
16130608Sbostic 		if (s.timecnt > TZ_MAX_TIMES ||
16230608Sbostic 			s.typecnt == 0 ||
16330608Sbostic 			s.typecnt > TZ_MAX_TYPES ||
16430608Sbostic 			s.charcnt > TZ_MAX_CHARS)
16530608Sbostic 				return -1;
16630608Sbostic 		if (i < sizeof *tzhp +
16730608Sbostic 			s.timecnt * (4 + sizeof (char)) +
16830608Sbostic 			s.typecnt * (4 + 2 * sizeof (char)) +
16930608Sbostic 			s.charcnt * sizeof (char))
17030608Sbostic 				return -1;
17130608Sbostic 		p = buf + sizeof *tzhp;
17230608Sbostic 		for (i = 0; i < s.timecnt; ++i) {
17330608Sbostic 			s.ats[i] = detzcode(p);
17430608Sbostic 			p += 4;
17512974Ssam 		}
17630608Sbostic 		for (i = 0; i < s.timecnt; ++i)
17730608Sbostic 			s.types[i] = (unsigned char) *p++;
17830608Sbostic 		for (i = 0; i < s.typecnt; ++i) {
17930608Sbostic 			register struct ttinfo *	ttisp;
18030608Sbostic 
18130608Sbostic 			ttisp = &s.ttis[i];
18230608Sbostic 			ttisp->tt_gmtoff = detzcode(p);
18330608Sbostic 			p += 4;
18430608Sbostic 			ttisp->tt_isdst = (unsigned char) *p++;
18530608Sbostic 			ttisp->tt_abbrind = (unsigned char) *p++;
18630608Sbostic 		}
18730608Sbostic 		for (i = 0; i < s.charcnt; ++i)
18830608Sbostic 			s.chars[i] = *p++;
18930608Sbostic 		s.chars[i] = '\0';	/* ensure '\0' at end */
1901959Swnj 	}
19130608Sbostic 	/*
19230608Sbostic 	** Check that all the local time type indices are valid.
19330608Sbostic 	*/
19430608Sbostic 	for (i = 0; i < s.timecnt; ++i)
19530608Sbostic 		if (s.types[i] >= s.typecnt)
19630608Sbostic 			return -1;
19730608Sbostic 	/*
19830608Sbostic 	** Check that all abbreviation indices are valid.
19930608Sbostic 	*/
20030608Sbostic 	for (i = 0; i < s.typecnt; ++i)
20130608Sbostic 		if (s.ttis[i].tt_abbrind >= s.charcnt)
20230608Sbostic 			return -1;
20330608Sbostic 	/*
20430608Sbostic 	** Set tzname elements to initial values.
20530608Sbostic 	*/
20630608Sbostic 	tzname[0] = tzname[1] = &s.chars[0];
20730608Sbostic #ifdef USG_COMPAT
20830898Sbostic 	timezone = -s.ttis[0].tt_gmtoff;
20930608Sbostic 	daylight = 0;
21030608Sbostic #endif /* USG_COMPAT */
21130608Sbostic 	for (i = 1; i < s.typecnt; ++i) {
21230608Sbostic 		register struct ttinfo *	ttisp;
21330608Sbostic 
21430608Sbostic 		ttisp = &s.ttis[i];
21530608Sbostic 		if (ttisp->tt_isdst) {
21630608Sbostic 			tzname[1] = &s.chars[ttisp->tt_abbrind];
21730608Sbostic #ifdef USG_COMPAT
21830608Sbostic 			daylight = 1;
21930608Sbostic #endif /* USG_COMPAT */
22030608Sbostic 		} else {
22130608Sbostic 			tzname[0] = &s.chars[ttisp->tt_abbrind];
22230608Sbostic #ifdef USG_COMPAT
22330898Sbostic 			timezone = -ttisp->tt_gmtoff;
22430608Sbostic #endif /* USG_COMPAT */
22530608Sbostic 		}
22630608Sbostic 	}
22730608Sbostic 	return 0;
2281959Swnj }
2291959Swnj 
2301959Swnj static
23130682Sbostic tzsetkernel()
23230682Sbostic {
23330682Sbostic 	struct timeval	tv;
23430682Sbostic 	struct timezone	tz;
23530714Sbostic 	char	*_tztab();
23630682Sbostic 
23730682Sbostic 	if (gettimeofday(&tv, &tz))
23830682Sbostic 		return -1;
23930682Sbostic 	s.timecnt = 0;		/* UNIX counts *west* of Greenwich */
24030682Sbostic 	s.ttis[0].tt_gmtoff = tz.tz_minuteswest * -SECS_PER_MIN;
24130682Sbostic 	s.ttis[0].tt_abbrind = 0;
24230714Sbostic 	(void)strcpy(s.chars, _tztab(tz.tz_minuteswest, 0));
24330682Sbostic 	tzname[0] = tzname[1] = s.chars;
24430682Sbostic #ifdef USG_COMPAT
24530682Sbostic 	timezone = tz.tz_minuteswest * 60;
24630682Sbostic 	daylight = tz.tz_dsttime;
24730682Sbostic #endif /* USG_COMPAT */
24830682Sbostic 	return 0;
24930682Sbostic }
25030682Sbostic 
25130682Sbostic static
25230608Sbostic tzsetgmt()
2531959Swnj {
25430608Sbostic 	s.timecnt = 0;
25530608Sbostic 	s.ttis[0].tt_gmtoff = 0;
25630608Sbostic 	s.ttis[0].tt_abbrind = 0;
25730608Sbostic 	(void) strcpy(s.chars, "GMT");
25830608Sbostic 	tzname[0] = tzname[1] = s.chars;
25930608Sbostic #ifdef USG_COMPAT
26030608Sbostic 	timezone = 0;
26130608Sbostic 	daylight = 0;
26230608Sbostic #endif /* USG_COMPAT */
2631959Swnj }
2641959Swnj 
26530608Sbostic void
26630608Sbostic tzset()
26730608Sbostic {
26830608Sbostic 	register char *	name;
26930608Sbostic 
27030608Sbostic 	tz_is_set = TRUE;
27130608Sbostic 	name = getenv("TZ");
27230682Sbostic 	if (!name || *name) {			/* did not request GMT */
27330682Sbostic 		if (name && !tzload(name))	/* requested name worked */
27430682Sbostic 			return;
27530682Sbostic 		if (!tzload((char *)0))		/* default name worked */
27630682Sbostic 			return;
27730682Sbostic 		if (!tzsetkernel())		/* kernel guess worked */
27830682Sbostic 			return;
27930682Sbostic 	}
28030682Sbostic 	tzsetgmt();				/* GMT is default */
28130608Sbostic }
28230608Sbostic 
2831959Swnj struct tm *
28430608Sbostic localtime(timep)
28530608Sbostic time_t *	timep;
2861959Swnj {
28730608Sbostic 	register struct ttinfo *	ttisp;
28830608Sbostic 	register struct tm *		tmp;
28930608Sbostic 	register int			i;
29030608Sbostic 	time_t				t;
2911959Swnj 
29230608Sbostic 	if (!tz_is_set)
29330608Sbostic 		(void) tzset();
29430608Sbostic 	t = *timep;
29530608Sbostic 	if (s.timecnt == 0 || t < s.ats[0]) {
29630608Sbostic 		i = 0;
29730608Sbostic 		while (s.ttis[i].tt_isdst)
29830608Sbostic 			if (++i >= s.timecnt) {
29930608Sbostic 				i = 0;
30030608Sbostic 				break;
30130608Sbostic 			}
30230608Sbostic 	} else {
30330608Sbostic 		for (i = 1; i < s.timecnt; ++i)
30430608Sbostic 			if (t < s.ats[i])
30530608Sbostic 				break;
30630608Sbostic 		i = s.types[i - 1];
3071959Swnj 	}
30830608Sbostic 	ttisp = &s.ttis[i];
3091959Swnj 	/*
31030608Sbostic 	** To get (wrong) behavior that's compatible with System V Release 2.0
31130608Sbostic 	** you'd replace the statement below with
31230608Sbostic 	**	tmp = offtime((time_t) (t + ttisp->tt_gmtoff), 0L);
31330608Sbostic 	*/
31430608Sbostic 	tmp = offtime(&t, ttisp->tt_gmtoff);
31530608Sbostic 	tmp->tm_isdst = ttisp->tt_isdst;
31630608Sbostic 	tzname[tmp->tm_isdst] = &s.chars[ttisp->tt_abbrind];
31730608Sbostic 	tmp->tm_zone = &s.chars[ttisp->tt_abbrind];
31830608Sbostic 	return tmp;
31930608Sbostic }
3201959Swnj 
32130608Sbostic struct tm *
32230608Sbostic gmtime(clock)
32330608Sbostic time_t *	clock;
32430608Sbostic {
32530608Sbostic 	register struct tm *	tmp;
3261959Swnj 
32730608Sbostic 	tmp = offtime(clock, 0L);
32830608Sbostic 	tzname[0] = "GMT";
32930608Sbostic 	tmp->tm_zone = "GMT";		/* UCT ? */
33030608Sbostic 	return tmp;
33130608Sbostic }
3321959Swnj 
33330608Sbostic static int	mon_lengths[2][MONS_PER_YEAR] = {
33430608Sbostic 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
33530608Sbostic 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
33630608Sbostic };
3371959Swnj 
33830608Sbostic static int	year_lengths[2] = {
33930608Sbostic 	DAYS_PER_NYEAR, DAYS_PER_LYEAR
34030608Sbostic };
3411959Swnj 
34230608Sbostic struct tm *
34330608Sbostic offtime(clock, offset)
34430608Sbostic time_t *	clock;
34530608Sbostic long		offset;
3461959Swnj {
34730608Sbostic 	register struct tm *	tmp;
34830608Sbostic 	register long		days;
34930608Sbostic 	register long		rem;
35030608Sbostic 	register int		y;
35130608Sbostic 	register int		yleap;
35230608Sbostic 	register int *		ip;
35330608Sbostic 	static struct tm	tm;
3541959Swnj 
35530608Sbostic 	tmp = &tm;
35630608Sbostic 	days = *clock / SECS_PER_DAY;
35730608Sbostic 	rem = *clock % SECS_PER_DAY;
35830608Sbostic 	rem += offset;
35930608Sbostic 	while (rem < 0) {
36030608Sbostic 		rem += SECS_PER_DAY;
36130608Sbostic 		--days;
3621959Swnj 	}
36330608Sbostic 	while (rem >= SECS_PER_DAY) {
36430608Sbostic 		rem -= SECS_PER_DAY;
36530608Sbostic 		++days;
36630608Sbostic 	}
36730608Sbostic 	tmp->tm_hour = (int) (rem / SECS_PER_HOUR);
36830608Sbostic 	rem = rem % SECS_PER_HOUR;
36930608Sbostic 	tmp->tm_min = (int) (rem / SECS_PER_MIN);
37030608Sbostic 	tmp->tm_sec = (int) (rem % SECS_PER_MIN);
37130608Sbostic 	tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYS_PER_WEEK);
37230608Sbostic 	if (tmp->tm_wday < 0)
37330608Sbostic 		tmp->tm_wday += DAYS_PER_WEEK;
37430608Sbostic 	y = EPOCH_YEAR;
37530608Sbostic 	if (days >= 0)
37630608Sbostic 		for ( ; ; ) {
37730608Sbostic 			yleap = isleap(y);
37830608Sbostic 			if (days < (long) year_lengths[yleap])
37930608Sbostic 				break;
38030608Sbostic 			++y;
38130608Sbostic 			days = days - (long) year_lengths[yleap];
38230608Sbostic 		}
38330608Sbostic 	else do {
38430608Sbostic 		--y;
38530608Sbostic 		yleap = isleap(y);
38630608Sbostic 		days = days + (long) year_lengths[yleap];
38730608Sbostic 	} while (days < 0);
38830608Sbostic 	tmp->tm_year = y - TM_YEAR_BASE;
38930608Sbostic 	tmp->tm_yday = (int) days;
39030608Sbostic 	ip = mon_lengths[yleap];
39130608Sbostic 	for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
39230608Sbostic 		days = days - (long) ip[tmp->tm_mon];
39330608Sbostic 	tmp->tm_mday = (int) (days + 1);
39430608Sbostic 	tmp->tm_isdst = 0;
39530608Sbostic 	tmp->tm_zone = "";
39630608Sbostic 	tmp->tm_gmtoff = offset;
39730608Sbostic 	return tmp;
3981959Swnj }
399