File:  [MW Coherent from dump] / coherent / b / lib / libc / gen / ctime.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:35 2019 UTC (7 years ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * libc/gen/ctime.c
 * Convert time to ASCII representation.
 *
 * Pseudo system-5, employs TIMEZONE environment for gmt offset,
 * timezone abbreviations, and daylight savings time information.
 *	TIMEZONE=SSS:mmm:DDD:n.d.m:n.d.m:h:m
 * SSS - Standard timezone abbreviation up to 31 characters long
 * mmm - minutes west of GMT
 * DDD - Daylight timezone abbreviation also 31 characters
 * n.d.m - n'th occurrence of d'th day of week in m'th month of
 *	start and end of daylight savings time.  Negative n indicates
 *	counting backwards from end of month.  Zero n indicates absolute date,
 *	d'th day of m'th month.  Days and months from 1 to n.
 * h - Hour of change from standard to daylight.
 * m - Minutes of adjustment at change.
 * Example - Central standard in current US conventions:
 *	TIMEZONE=CST:3600:CDT:-1.1.4:-1.1.10:2:60
 * Only the first two fields are required.
 * If no daylight timezone is specified, then no daylight conversion is done.
 * If no dates for daylight are given, they default to 83-05-10 US standard.
 * If no hour of daylight time change is specified, it defaults to 2AM.
 * If no minutes of adjustment is specified, it defaults to 60.
 * These defaults can be changed by overwriting tzdstdef[].
 */


#include <time.h>
#if	0
/* Required for ftime(), now conditionalized out, cf. comments below. */
#include <sys/timeb.h>
#endif

#define	FEB	1
#define	NWDAY	7		/* Number of weekdays */
#define	NMON	12		/* Number of months */
#define	todigit(c) ((c)+'0')
#define	NTZNAME	31		/* max time zone name size */

/* Static data. */
static	char	daynames[3*NWDAY+1] = "SunMonTueWedThuFriSat";
static	char	dpm[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static	int	dstadjust = 60*60;	/* In seconds */
static	char	dsthour = 2;
static	struct	dsttimes {
	char dst_occur, dst_day, dst_month;
} dsttimes[2] = { 
	{  1, 0, 3 },			/* First Sunday in April */
	{ -1, 0, 9 }			/* Last Sunday in October */
};
static	char	months[3*NMON+1] = "JanFebMarAprMayJunJulAugSepOctNovDec";
static	char	timestr[] = "AAA AAA DD DD:DD:DD DDDD\n";
static	struct	tm	tm;
static	char	tz0[NTZNAME+1] = "GMT";
static	char	tz1[NTZNAME+1] = "";

/* Global data. */
long	timezone = 0L;
char	*tzname[2] = { tz0, tz1 };
char	tzdstdef[] = "1.1.4:-1.1.10:2:60......";

/* Set the timezone parameters, once is enough */
void
settz()
{
	extern char *getenv();
	register char *cp1, *cp2;
	static int settz = 0;

	if (settz++ > 0)
		return;
	if ((cp1 = getenv("TIMEZONE")) == ((char *)0))
		return;
	timezone = 0;

	/* Read primary timezone name and nul terminate */
	cp2 = tzname[0];
	while (*cp1 && *cp1 != ':' && cp2 < &tzname[0][NTZNAME])
		*cp2++ = *cp1++;
	*cp2++ = '\0';
	while (*cp1 && *cp1++ != ':');

	/* Read timezone offset and convert to seconds */
	timezone = (long)atoi(cp1) * 60L;
	while (*cp1 && *cp1++ != ':');

	/* Read daylight timezone name and nul terminate */
	cp2 = tzname[1];
	while (*cp1 && *cp1 != ':' && cp2 < &tzname[1][NTZNAME])
		*cp2++ = *cp1++;
	*cp2++ = '\0';
	while (*cp1 && *cp1++ != ':');

	/* Exit if no daylight time */
	if (tzname[1][0] == '\0')
		return;

	/* Set default dst parameters */
	setdst(tzdstdef);

	/* Set supplied dst parameters */
	setdst(cp1);
}

/* Parse and set dst parameters */
static
setdst(cp1) register char *cp1;
{
	/* Get optional start of daylight time */
	if (*cp1) {
		dsttimes[0].dst_occur = atoi(cp1);
		while (*cp1 && *cp1++ != '.');
		dsttimes[0].dst_day = atoi(cp1)-1;
		while (*cp1 && *cp1++ != '.');
		dsttimes[0].dst_month = atoi(cp1)-1;
		while (*cp1 && *cp1++ != ':');
	}

	/* Get optional end of daylight time */
	if (*cp1) {
		dsttimes[1].dst_occur = atoi(cp1);
		while (*cp1 && *cp1++ != '.');
		dsttimes[1].dst_day = atoi(cp1)-1;
		while (*cp1 && *cp1++ != '.');
		dsttimes[1].dst_month = atoi(cp1)-1;
		while (*cp1 && *cp1++ != ':');
	}

	/* Get optional hour of daylight time advance */
	if (*cp1) {
		dsthour = atoi(cp1);
		while (*cp1 && *cp1++ != ':');
	}

	/* Get optional minutes of adjustment */
	if (*cp1)
		dstadjust = atoi(cp1) * 60;
}

#if	0
/*
 * This ftime() is not required for COH 4.0.1r78 or later,
 * it is now a system call using the stub generated by sys/i386/scall.s5.
 */
/*
 * ftime()
 */
ftime (tb) struct timeb *tb;
{
	time_t t;

	tb->time = time(&t);
	settz();	
	localtime(&t);
	tb->millitm = 0;
	tb->timezone = (short)(timezone / 60L);
       	tb->dstflag = isdaylight();
}
#endif

/*
 * Most common interface, returns a static string
 * which is a printable version of the time and date.
 */
char *
ctime(tp)
long *tp;
{
	return asctime(localtime(tp));
}

/*
 * Do what gmtime does for the local timezone.
 * And correct for daylight time.
 */
struct tm *
localtime(tp)
long *tp;
{
	long ltime;

	settz();
	ltime = *tp - timezone;
	gmtime(&ltime);

	/*
	 * If necessary, adjust for daylight saving time.
	 */
	if (isdaylight()) {
		ltime = *tp - timezone + dstadjust;
		gmtime(&ltime);
		tm.tm_isdst = 1;
	} else
		tm.tm_isdst = 0;
	return &tm;
}

/*
 * Returns a printable version of the time
 * which has been broken down as in the tm structure.
 */
char *
asctime(tmp)
struct tm *tmp;
{
	register char *cp, *xp;
	register unsigned i;

	cp = timestr;
	/*
	 * Day of week
	 */
	if ((i = tmp->tm_wday) >= NWDAY)
		i = 0;
	xp = &daynames[i*3];
	*cp++ = *xp++;
	*cp++ = *xp++;
	*cp++ = *xp++;
	*cp++ = ' ';
	/*
	 * Month
	 */
	if ((i = tmp->tm_mon) >= NMON)
		i = 0;
	xp = &months[i*3];
	*cp++ = *xp++;
	*cp++ = *xp++;
	*cp++ = *xp++;
	*cp++ = ' ';
	/*
	 * Day of month
	 */
	if ((i = tmp->tm_mday) >= 10)
		*cp++ = todigit(i/10);
	else
		*cp++ = ' ';
	*cp++ = todigit(i%10);
	*cp++ = ' ';
	/*
	 * Hours:mins:seconds
	 */
	*cp++ = todigit((i = tmp->tm_hour)/10);
	*cp++ = todigit(i%10);
	*cp++ = ':';
	*cp++ = todigit((i = tmp->tm_min)/10);
	*cp++ = todigit(i%10);
	*cp++ = ':';
	*cp++ = todigit((i = tmp->tm_sec)/10);
	*cp++ = todigit(i%10);
	*cp++ = ' ';
	/*
	 * Year
	 */
	i = tmp->tm_year + 1900;
	*cp++ = todigit(i/1000);
	i = i%1000;
	*cp++ = todigit(i/100);
	i = i%100;
	*cp++ = todigit(i/10);
	*cp++ = todigit(i%10);
	*cp++ = '\n';
	*cp++ = '\0';
	return timestr;
}

/*
 * Do conversions for Greenwich Mean Time to
 * the tm structure.
 */
struct tm *
gmtime(tp)
long *tp;
{
	long xtime;
	unsigned days;
	long secs;
	int year;
	int ydays;
	int wday;
	register char *mp;

	if ((xtime = *tp) < 0)
		xtime = 0;
	days = xtime/(60L*60L*24L);
	secs = xtime%(60L*60L*24L);
	tm.tm_hour = secs/(60L*60L);
	secs = secs%(60L*60L);
	tm.tm_min = secs/60;
	tm.tm_sec = secs%60;
	/*
	 * Start at Thursday (wday=4) Jan 1, 1970 (the Epoch)
	 * and calculate from there.
	 */
	wday = (4+days)%NWDAY;
	year = 1970;
	for (;;) {
		ydays = isleap(year) ? 366 : 365;
		if (days < ydays)
			break;
		year++;
		days -= ydays;
	}
	tm.tm_year = year-1900;
	tm.tm_yday = days;
	tm.tm_wday = wday;
	/*
	 * Setup february's #days now.
	 */
	if (isleap(year))
		dpm[FEB] = 29;
	else
		dpm[FEB] = 28;
	for (mp = &dpm[0]; mp < &dpm[NMON] && days >= *mp; mp++)
		days -= *mp;
	tm.tm_mon = mp-dpm;
	tm.tm_mday = days+1;
	return &tm;
}

/*
 * Return 1 on leap years; 0 otherwise.
 */
static
isleap(yr)
register yr;
{
	if (yr%4000 == 0)
		return 0;
	return (yr%400==0 || (yr%100!=0 && yr%4==0));
}

/*
 * Check for daylight savings time.
 * Watch out for the Southern Hemisphere, where start month > end month.
 * This does not do the case where start == end correctly for all cases.
 */
static
isdaylight()
{
	register int month, start, end, xday;

	if (tzname[1][0] == 0)
		return 0;			/* No name, no daylight time */
	month = tm.tm_mon;
	start = dsttimes[0].dst_month;
	end = dsttimes[1].dst_month;
	if ((start <= end && (month < start || month > end))
	 || (start >  end && (month < start && month > end)))
		return 0;			/* current month is never DST */
	else if (month == start) {		/* DST starts this month */
		xday = nthday(&dsttimes[0]);	/* DST starts on xday */
		if (tm.tm_mday != xday)
			return (tm.tm_mday > xday);
		return (tm.tm_hour >= dsthour);
	} else if (month == end) {		/* DST ends this month */
		xday = nthday(&dsttimes[1]);	/* DST ends on xday */
		if (tm.tm_mday != xday)
			return (tm.tm_mday < xday);
		return (tm.tm_hour < dsthour-1);
	} else
		return 1;			/* current month is always DST */
}

static
nthday(dp)
register struct dsttimes *dp;
{
	register int nthday;
	register int nth;

	if ((nth = dp->dst_occur) == 0)
		return dp->dst_day;
	nthday = tm.tm_mday - tm.tm_wday + dp->dst_day;
	if (nth > 0) {
		while (nthday > 0)
			nthday -= 7;
		do
			nthday += 7;
		while (--nth > 0);
	} else {
		while (nthday < dpm[tm.tm_mon])
			nthday += 7;
		do
			nthday -= 7;
		while (++nth < 0);
	}
	return nthday;
}

/* end of ctime.c */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.