xref: /inferno-os/appl/lib/daytime.b (revision af1b61f55a195350b4491438e8b2c381c4ba34bc)
137da2899SCharles.Forsythimplement Daytime;
237da2899SCharles.Forsyth#
337da2899SCharles.Forsyth# These routines convert time as follows:
437da2899SCharles.Forsyth#
537da2899SCharles.Forsyth# The epoch is 0000 Jan 1 1970 GMT.
637da2899SCharles.Forsyth# The argument time is in microseconds since then.
737da2899SCharles.Forsyth# The local(t) entry returns a reference to an ADT
837da2899SCharles.Forsyth# containing
937da2899SCharles.Forsyth#
1037da2899SCharles.Forsyth#	seconds (0-59)
1137da2899SCharles.Forsyth#	minutes (0-59)
1237da2899SCharles.Forsyth#	hours (0-23)
1337da2899SCharles.Forsyth#	day of month (1-31)
1437da2899SCharles.Forsyth#	month (0-11)
1537da2899SCharles.Forsyth#	year-1900
1637da2899SCharles.Forsyth#	weekday (0-6, Sun is 0)
1737da2899SCharles.Forsyth#	day of the year
1837da2899SCharles.Forsyth#	daylight savings flag
1937da2899SCharles.Forsyth#
2037da2899SCharles.Forsyth# The routine gets the daylight savings time from the file /locale/timezone.
2137da2899SCharles.Forsyth#
2237da2899SCharles.Forsyth# text(tvec)
2337da2899SCharles.Forsyth# where tvec is produced by local
2437da2899SCharles.Forsyth# returns a string that has the time in the form
2537da2899SCharles.Forsyth#
2637da2899SCharles.Forsyth#	Thu Jan 01 00:00:00 GMT 1970n0
2737da2899SCharles.Forsyth#	012345678901234567890123456789
2837da2899SCharles.Forsyth#	0	  1	    2
2937da2899SCharles.Forsyth#
3037da2899SCharles.Forsyth# time() just reads the time from /dev/time
3137da2899SCharles.Forsyth# and then calls localtime, then asctime.
3237da2899SCharles.Forsyth#
3337da2899SCharles.Forsyth# The sign bit of second times will turn on 68 years from the epoch ->2038
3437da2899SCharles.Forsyth#
3537da2899SCharles.Forsythinclude	"sys.m";
3637da2899SCharles.Forsythinclude	"string.m";
3737da2899SCharles.Forsythinclude "daytime.m";
3837da2899SCharles.Forsyth
3937da2899SCharles.ForsythS: String;
4037da2899SCharles.Forsythsys: Sys;
4137da2899SCharles.Forsyth
4237da2899SCharles.Forsythdmsize := array[] of {
4337da2899SCharles.Forsyth	31, 28, 31, 30, 31, 30,
4437da2899SCharles.Forsyth	31, 31, 30, 31, 30, 31
4537da2899SCharles.Forsyth};
4637da2899SCharles.Forsythldmsize := array[] of {
4737da2899SCharles.Forsyth	31, 29, 31, 30, 31, 30,
4837da2899SCharles.Forsyth	31, 31, 30, 31, 30, 31
4937da2899SCharles.Forsyth};
5037da2899SCharles.Forsyth
5137da2899SCharles.ForsythTimezone: adt
5237da2899SCharles.Forsyth{
5337da2899SCharles.Forsyth	stname: string;
5437da2899SCharles.Forsyth	dlname: string;
5537da2899SCharles.Forsyth	stdiff:	int;
5637da2899SCharles.Forsyth	dldiff: int;
5737da2899SCharles.Forsyth	dlpairs: array of int;
5837da2899SCharles.Forsyth};
5937da2899SCharles.Forsyth
6037da2899SCharles.Forsythtimezone: ref Timezone;
6137da2899SCharles.Forsyth
6237da2899SCharles.Forsythnow(): int
6337da2899SCharles.Forsyth{
6437da2899SCharles.Forsyth	if(sys == nil)
6537da2899SCharles.Forsyth		sys = load Sys Sys->PATH;
6637da2899SCharles.Forsyth
6737da2899SCharles.Forsyth	fd := sys->open("/dev/time", sys->OREAD);
6837da2899SCharles.Forsyth	if(fd == nil)
6937da2899SCharles.Forsyth		return 0;
7037da2899SCharles.Forsyth	buf := array[128] of byte;
7137da2899SCharles.Forsyth	n := sys->read(fd, buf, len buf);
7237da2899SCharles.Forsyth	if(n < 0)
7337da2899SCharles.Forsyth		return 0;
7437da2899SCharles.Forsyth
7537da2899SCharles.Forsyth	t := (big string buf[0:n]) / big 1000000;
7637da2899SCharles.Forsyth	return int t;
7737da2899SCharles.Forsyth}
7837da2899SCharles.Forsyth
7937da2899SCharles.Forsythtime(): string
8037da2899SCharles.Forsyth{
8137da2899SCharles.Forsyth	t := now();
8237da2899SCharles.Forsyth	tm := local(t);
8337da2899SCharles.Forsyth	return text(tm);
8437da2899SCharles.Forsyth}
8537da2899SCharles.Forsyth
8637da2899SCharles.Forsythlocal(tim: int): ref Tm
8737da2899SCharles.Forsyth{
8837da2899SCharles.Forsyth	ct: ref Tm;
8937da2899SCharles.Forsyth
9037da2899SCharles.Forsyth	if(timezone == nil)
9137da2899SCharles.Forsyth		timezone = readtimezone(nil);
9237da2899SCharles.Forsyth
9337da2899SCharles.Forsyth	t := tim + timezone.stdiff;
9437da2899SCharles.Forsyth	dlflag := 0;
9537da2899SCharles.Forsyth	for(i := 0; i+1 < len timezone.dlpairs; i += 2) {
9637da2899SCharles.Forsyth		if(t >= timezone.dlpairs[i] && t < timezone.dlpairs[i+1]) {
9737da2899SCharles.Forsyth			t = tim + timezone.dldiff;
9837da2899SCharles.Forsyth			dlflag++;
9937da2899SCharles.Forsyth			break;
10037da2899SCharles.Forsyth		}
10137da2899SCharles.Forsyth	}
10237da2899SCharles.Forsyth	ct = gmt(t);
10337da2899SCharles.Forsyth	if(dlflag) {
10437da2899SCharles.Forsyth		ct.zone = timezone.dlname;
10537da2899SCharles.Forsyth		ct.tzoff = timezone.dldiff;
10637da2899SCharles.Forsyth	}
10737da2899SCharles.Forsyth	else {
10837da2899SCharles.Forsyth		ct.zone = timezone.stname;
10937da2899SCharles.Forsyth		ct.tzoff = timezone.stdiff;
11037da2899SCharles.Forsyth	}
11137da2899SCharles.Forsyth	return ct;
11237da2899SCharles.Forsyth}
11337da2899SCharles.Forsyth
11437da2899SCharles.Forsythgmt(tim: int): ref Tm
11537da2899SCharles.Forsyth{
11637da2899SCharles.Forsyth	xtime := ref Tm;
11737da2899SCharles.Forsyth
11837da2899SCharles.Forsyth	# break initial number into days
11937da2899SCharles.Forsyth	hms := tim % 86400;
12037da2899SCharles.Forsyth	day := tim / 86400;
12137da2899SCharles.Forsyth	if(hms < 0) {
12237da2899SCharles.Forsyth		hms += 86400;
12337da2899SCharles.Forsyth		day -= 1;
12437da2899SCharles.Forsyth	}
12537da2899SCharles.Forsyth
12637da2899SCharles.Forsyth	# generate hours:minutes:seconds
12737da2899SCharles.Forsyth	xtime.sec = hms % 60;
12837da2899SCharles.Forsyth	d1 := hms / 60;
12937da2899SCharles.Forsyth	xtime.min = d1 % 60;
13037da2899SCharles.Forsyth	d1 /= 60;
13137da2899SCharles.Forsyth	xtime.hour = d1;
13237da2899SCharles.Forsyth
13337da2899SCharles.Forsyth	# day is the day number.
13437da2899SCharles.Forsyth	# generate day of the week.
13537da2899SCharles.Forsyth	# The addend is 4 mod 7 (1/1/1970 was Thursday)
13637da2899SCharles.Forsyth	xtime.wday = (day + 7340036) % 7;
13737da2899SCharles.Forsyth
13837da2899SCharles.Forsyth	# year number
13937da2899SCharles.Forsyth	if(day >= 0)
14037da2899SCharles.Forsyth		for(d1 = 70; day >= dysize(d1+1900); d1++)
14137da2899SCharles.Forsyth			day -= dysize(d1+1900);
14237da2899SCharles.Forsyth	else
14337da2899SCharles.Forsyth		for (d1 = 70; day < 0; d1--)
14437da2899SCharles.Forsyth			day += dysize(d1+1900-1);
14537da2899SCharles.Forsyth	xtime.year = d1;
14637da2899SCharles.Forsyth	d0 := day;
14737da2899SCharles.Forsyth	xtime.yday = d0;
14837da2899SCharles.Forsyth
14937da2899SCharles.Forsyth	# generate month
15037da2899SCharles.Forsyth	if(dysize(d1+1900) == 366)
15137da2899SCharles.Forsyth		dmsz := ldmsize;
15237da2899SCharles.Forsyth	else
15337da2899SCharles.Forsyth		dmsz = dmsize;
15437da2899SCharles.Forsyth	for(d1 = 0; d0 >= dmsz[d1]; d1++)
15537da2899SCharles.Forsyth		d0 -= dmsz[d1];
15637da2899SCharles.Forsyth	xtime.mday = d0 + 1;
15737da2899SCharles.Forsyth	xtime.mon = d1;
15837da2899SCharles.Forsyth	xtime.zone = "GMT";
15937da2899SCharles.Forsyth	xtime.tzoff = 0;
16037da2899SCharles.Forsyth	return xtime;
16137da2899SCharles.Forsyth}
16237da2899SCharles.Forsyth
16337da2899SCharles.Forsythwkday := array[] of {
16437da2899SCharles.Forsyth	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
16537da2899SCharles.Forsyth};
16637da2899SCharles.Forsyth
16737da2899SCharles.Forsythweekday := array[] of {
16837da2899SCharles.Forsyth	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
16937da2899SCharles.Forsyth};
17037da2899SCharles.Forsyth
17137da2899SCharles.Forsythmonth := array[] of {
17237da2899SCharles.Forsyth	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
17337da2899SCharles.Forsyth	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
17437da2899SCharles.Forsyth};
17537da2899SCharles.Forsyth
17637da2899SCharles.Forsythtext(t: ref Tm): string
17737da2899SCharles.Forsyth{
17837da2899SCharles.Forsyth	if(sys == nil)
17937da2899SCharles.Forsyth		sys = load Sys Sys->PATH;
18037da2899SCharles.Forsyth
18137da2899SCharles.Forsyth	year := 1900+t.year;
18237da2899SCharles.Forsyth
18337da2899SCharles.Forsyth	return sys->sprint("%s %s %.2d %.2d:%.2d:%.2d %s %d",
18437da2899SCharles.Forsyth		wkday[t.wday],
18537da2899SCharles.Forsyth		month[t.mon],
18637da2899SCharles.Forsyth		t.mday,
18737da2899SCharles.Forsyth		t.hour,
18837da2899SCharles.Forsyth		t.min,
18937da2899SCharles.Forsyth		t.sec,
19037da2899SCharles.Forsyth		t.zone,
19137da2899SCharles.Forsyth		year);
19237da2899SCharles.Forsyth}
19337da2899SCharles.Forsyth
19437da2899SCharles.Forsythfilet(now: int, file: int): string
19537da2899SCharles.Forsyth{
19637da2899SCharles.Forsyth	if(sys == nil)
19737da2899SCharles.Forsyth		sys = load Sys Sys->PATH;
19837da2899SCharles.Forsyth
19937da2899SCharles.Forsyth	t := local(file);
20037da2899SCharles.Forsyth	if(now - file < 6*30*24*3600)
20137da2899SCharles.Forsyth		return sys->sprint("%s %.2d %.2d:%.2d",
20237da2899SCharles.Forsyth			month[t.mon], t.mday, t.hour, t.min);
20337da2899SCharles.Forsyth
20437da2899SCharles.Forsyth	year := 1900+t.year;
20537da2899SCharles.Forsyth
20637da2899SCharles.Forsyth	return sys->sprint("%s %.2d  %d", month[t.mon], t.mday, year);
20737da2899SCharles.Forsyth}
20837da2899SCharles.Forsyth
20937da2899SCharles.Forsythdysize(y: int): int
21037da2899SCharles.Forsyth{
21137da2899SCharles.Forsyth	if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
21237da2899SCharles.Forsyth		return 366;
21337da2899SCharles.Forsyth	return 365;
21437da2899SCharles.Forsyth}
21537da2899SCharles.Forsyth
21637da2899SCharles.Forsythreadtimezone(fname: string): ref Timezone
21737da2899SCharles.Forsyth{
21837da2899SCharles.Forsyth	if(sys == nil)
21937da2899SCharles.Forsyth		sys = load Sys Sys->PATH;
22037da2899SCharles.Forsyth
22137da2899SCharles.Forsyth	tz := ref Timezone;
22237da2899SCharles.Forsyth	tz.stdiff = 0;
22337da2899SCharles.Forsyth	tz.stname = "GMT";
22437da2899SCharles.Forsyth
225*af1b61f5SCharles.Forsyth	s: string;
22637da2899SCharles.Forsyth	if(fname == nil){
227*af1b61f5SCharles.Forsyth		s = readfile("/env/timezone");
228*af1b61f5SCharles.Forsyth		if(s == nil)
229*af1b61f5SCharles.Forsyth			s = readfile("/locale/timezone");
230*af1b61f5SCharles.Forsyth	}else{
231*af1b61f5SCharles.Forsyth		if(fname[0] != '/' && fname[0] != '#')
232*af1b61f5SCharles.Forsyth			fname = "/locale/" + fname;
233*af1b61f5SCharles.Forsyth		s = readfile(fname);
234*af1b61f5SCharles.Forsyth	}
235*af1b61f5SCharles.Forsyth	if(s == nil)
23637da2899SCharles.Forsyth		return tz;
237*af1b61f5SCharles.Forsyth	if(s[0] == '/' || s[0] == '#'){
238*af1b61f5SCharles.Forsyth		if(s[len s-1] == '\n')
239*af1b61f5SCharles.Forsyth			s = s[0: len s-1];
240*af1b61f5SCharles.Forsyth		s = readfile(s);
241*af1b61f5SCharles.Forsyth		if(s == nil)
24237da2899SCharles.Forsyth			return tz;
243*af1b61f5SCharles.Forsyth	}
244*af1b61f5SCharles.Forsyth	(n, val) := sys->tokenize(s, "\t \n\r");
24537da2899SCharles.Forsyth	if(n < 4)
24637da2899SCharles.Forsyth		return tz;
24737da2899SCharles.Forsyth
24837da2899SCharles.Forsyth	tz.stname = hd val;
24937da2899SCharles.Forsyth	val = tl val;
25037da2899SCharles.Forsyth	tz.stdiff = int hd val;
25137da2899SCharles.Forsyth	val = tl val;
25237da2899SCharles.Forsyth	tz.dlname = hd val;
25337da2899SCharles.Forsyth	val = tl val;
25437da2899SCharles.Forsyth	tz.dldiff = int hd val;
25537da2899SCharles.Forsyth	val = tl val;
25637da2899SCharles.Forsyth
25737da2899SCharles.Forsyth	tz.dlpairs = array[n-4] of {* => 0};
25837da2899SCharles.Forsyth	for(j := 0; val != nil; val = tl val)
25937da2899SCharles.Forsyth		tz.dlpairs[j++] = int hd val;
26037da2899SCharles.Forsyth	return tz;
26137da2899SCharles.Forsyth}
26237da2899SCharles.Forsyth
263*af1b61f5SCharles.Forsythreadfile(name: string): string
264*af1b61f5SCharles.Forsyth{
265*af1b61f5SCharles.Forsyth	fd := sys->open(name, Sys->OREAD);
266*af1b61f5SCharles.Forsyth	if(fd == nil)
267*af1b61f5SCharles.Forsyth		return nil;
268*af1b61f5SCharles.Forsyth	buf := array[2048] of byte;
269*af1b61f5SCharles.Forsyth	n := sys->read(fd, buf, len buf);
270*af1b61f5SCharles.Forsyth	if(n <= 0)
271*af1b61f5SCharles.Forsyth		return nil;
272*af1b61f5SCharles.Forsyth	return string buf[0:n];
273*af1b61f5SCharles.Forsyth}
274*af1b61f5SCharles.Forsyth
27537da2899SCharles.ForsythSEC2MIN:	con 60;
27637da2899SCharles.ForsythSEC2HOUR:	con 60*SEC2MIN;
27737da2899SCharles.ForsythSEC2DAY:	con 24*SEC2HOUR;
27837da2899SCharles.Forsyth
27937da2899SCharles.Forsythtm2epoch(tm: ref Tm): int
28037da2899SCharles.Forsyth{
28137da2899SCharles.Forsyth	secs := 0;
28237da2899SCharles.Forsyth
28337da2899SCharles.Forsyth	#
28437da2899SCharles.Forsyth	#  seconds per year
28537da2899SCharles.Forsyth	#
28637da2899SCharles.Forsyth	yr := tm.year + 1900;
28737da2899SCharles.Forsyth	if(yr < 1970)
28837da2899SCharles.Forsyth		for(i := yr; i < 1970; i++)
28937da2899SCharles.Forsyth			secs -= dysize(i) * SEC2DAY;
29037da2899SCharles.Forsyth	else
29137da2899SCharles.Forsyth		for(i = 1970; i < yr; i++)
29237da2899SCharles.Forsyth			secs += dysize(i) * SEC2DAY;
29337da2899SCharles.Forsyth	#
29437da2899SCharles.Forsyth	#  seconds per month
29537da2899SCharles.Forsyth	#
29637da2899SCharles.Forsyth	if(dysize(yr) == 366)
29737da2899SCharles.Forsyth		dmsz := ldmsize;
29837da2899SCharles.Forsyth	else
29937da2899SCharles.Forsyth		dmsz = dmsize;
30037da2899SCharles.Forsyth	for(i = 0; i < tm.mon; i++)
30137da2899SCharles.Forsyth		secs += dmsz[i] * SEC2DAY;
30237da2899SCharles.Forsyth
30337da2899SCharles.Forsyth	#
30437da2899SCharles.Forsyth	# secs in last month
30537da2899SCharles.Forsyth	#
30637da2899SCharles.Forsyth	secs += (tm.mday-1) * SEC2DAY;
30737da2899SCharles.Forsyth
30837da2899SCharles.Forsyth	#
30937da2899SCharles.Forsyth	# hours, minutes, seconds
31037da2899SCharles.Forsyth	#
31137da2899SCharles.Forsyth	secs += tm.hour * SEC2HOUR;
31237da2899SCharles.Forsyth	secs += tm.min * SEC2MIN;
31337da2899SCharles.Forsyth	secs += tm.sec;
31437da2899SCharles.Forsyth
31537da2899SCharles.Forsyth	#
31637da2899SCharles.Forsyth	#  time zone offset includes daylight savings time
31737da2899SCharles.Forsyth	#
31837da2899SCharles.Forsyth	return secs - tm.tzoff;
31937da2899SCharles.Forsyth}
32037da2899SCharles.Forsyth
32137da2899SCharles.Forsyth# handle three formats (we'll be a bit more tolerant)
32237da2899SCharles.Forsyth#  Sun, 06 Nov 1994 08:49:37 TZ  (rfc822+rfc1123)
32337da2899SCharles.Forsyth#  Sunday, 06-Nov-94 08:49:37 TZ (rfc850, obsoleted by rfc1036)
32437da2899SCharles.Forsyth#  Sun Nov  6 08:49:37 1994	 (ANSI C's asctime() format, assume GMT)
32537da2899SCharles.Forsyth#
32637da2899SCharles.Forsyth# return nil on parsing error
32737da2899SCharles.Forsyth#
32837da2899SCharles.Forsythstring2tm(date: string): ref Tm
32937da2899SCharles.Forsyth{
33037da2899SCharles.Forsyth	buf: string;
33137da2899SCharles.Forsyth	ok: int;
33237da2899SCharles.Forsyth	tm := ref Tm;
33337da2899SCharles.Forsyth
33437da2899SCharles.Forsyth	if(S == nil)
33537da2899SCharles.Forsyth		S = load String String->PATH;
33637da2899SCharles.Forsyth
33737da2899SCharles.Forsyth	# Weekday|Wday
33837da2899SCharles.Forsyth	(date, buf) = dateword(date);
33937da2899SCharles.Forsyth	tm.wday = strlookup(wkday, buf);
34037da2899SCharles.Forsyth	if(tm.wday < 0)
34137da2899SCharles.Forsyth		tm.wday = strlookup(weekday, buf);
34237da2899SCharles.Forsyth	if(tm.wday < 0)
34337da2899SCharles.Forsyth		return nil;
34437da2899SCharles.Forsyth
34537da2899SCharles.Forsyth	# Try Mon
34637da2899SCharles.Forsyth	odate := date;
34737da2899SCharles.Forsyth	(date, buf) = dateword(date);
34837da2899SCharles.Forsyth	tm.mon = strlookup(month, buf);
34937da2899SCharles.Forsyth	if(tm.mon >= 0) {
35037da2899SCharles.Forsyth		# Mon was OK, so asctime() format
35137da2899SCharles.Forsyth		# DD
35237da2899SCharles.Forsyth		(date, tm.mday) = datenum(date);
35337da2899SCharles.Forsyth		if(tm.mday < 1 || tm.mday > 31)
35437da2899SCharles.Forsyth			return nil;
35537da2899SCharles.Forsyth
35637da2899SCharles.Forsyth		# HH:MM:SS
35737da2899SCharles.Forsyth		(ok, date) = hhmmss(date, tm);
35837da2899SCharles.Forsyth		if(!ok)
35937da2899SCharles.Forsyth			return nil;
36037da2899SCharles.Forsyth
3614252603cSCharles.Forsyth		# optional time zone
3624252603cSCharles.Forsyth		while(date != nil && date[0] == ' ')
3634252603cSCharles.Forsyth			date = date[1:];
3644252603cSCharles.Forsyth		if(date != nil && !(date[0] >= '0' && date[0] <= '9')){
3654252603cSCharles.Forsyth			for(i := 0; i < len date; i++)
3664252603cSCharles.Forsyth				if(date[i] == ' '){
3674252603cSCharles.Forsyth					(tm.zone, tm.tzoff) = tzinfo(date[0: i]);
3684252603cSCharles.Forsyth					date = date[i:];
3694252603cSCharles.Forsyth					break;
3704252603cSCharles.Forsyth				}
3714252603cSCharles.Forsyth		}
3724252603cSCharles.Forsyth
37337da2899SCharles.Forsyth		# YY|YYYY
3744252603cSCharles.Forsyth		(nil, tm.year) = datenum(date);
37537da2899SCharles.Forsyth		if(tm.year > 1900)
37637da2899SCharles.Forsyth			tm.year -= 1900;
3774252603cSCharles.Forsyth		if(tm.zone == ""){
3784252603cSCharles.Forsyth			tm.zone = "GMT";
3794252603cSCharles.Forsyth			tm.tzoff = 0;
3804252603cSCharles.Forsyth		}
38137da2899SCharles.Forsyth	} else {
38237da2899SCharles.Forsyth		# Mon was not OK
38337da2899SCharles.Forsyth		date = odate;
38437da2899SCharles.Forsyth		# DD Mon YYYY or DD-Mon-(YY|YYYY)
38537da2899SCharles.Forsyth		(date, tm.mday) = datenum(date);
38637da2899SCharles.Forsyth		if(tm.mday < 1 || tm.mday > 31)
38737da2899SCharles.Forsyth			return nil;
38837da2899SCharles.Forsyth		(date, buf) = dateword(date);
38937da2899SCharles.Forsyth		tm.mon = strlookup(month, buf);
39037da2899SCharles.Forsyth		if(tm.mon < 0 || tm.mon >= 12)
39137da2899SCharles.Forsyth			return nil;
39237da2899SCharles.Forsyth		(date, tm.year) = datenum(date);
39337da2899SCharles.Forsyth		if(tm.year > 1900)
39437da2899SCharles.Forsyth			tm.year -= 1900;
39537da2899SCharles.Forsyth
39637da2899SCharles.Forsyth		# HH:MM:SS
39737da2899SCharles.Forsyth		(ok, buf) = hhmmss(date, tm);
39837da2899SCharles.Forsyth		if(!ok)
39937da2899SCharles.Forsyth			return nil;
40037da2899SCharles.Forsyth		(tm.zone, tm.tzoff) = tzinfo(buf);
40137da2899SCharles.Forsyth		if(tm.zone == "")
40237da2899SCharles.Forsyth			return nil;
4034252603cSCharles.Forsyth	}
40437da2899SCharles.Forsyth
40537da2899SCharles.Forsyth	return tm;
40637da2899SCharles.Forsyth}
40737da2899SCharles.Forsyth
40837da2899SCharles.Forsythdateword(date: string): (string, string)
40937da2899SCharles.Forsyth{
41037da2899SCharles.Forsyth	notalnum: con "^A-Za-z0-9";
41137da2899SCharles.Forsyth
41237da2899SCharles.Forsyth	date = S->drop(date, notalnum);
41337da2899SCharles.Forsyth	(w, rest) := S->splitl(date, notalnum);
41437da2899SCharles.Forsyth	return (rest, w);
41537da2899SCharles.Forsyth}
41637da2899SCharles.Forsyth
41737da2899SCharles.Forsythdatenum(date: string): (string, int)
41837da2899SCharles.Forsyth{
41937da2899SCharles.Forsyth	notdig: con "^0-9";
42037da2899SCharles.Forsyth
42137da2899SCharles.Forsyth	date = S->drop(date, notdig);
42237da2899SCharles.Forsyth	(num, rest) := S->splitl(date, notdig);
42337da2899SCharles.Forsyth	return (rest, int num);
42437da2899SCharles.Forsyth}
42537da2899SCharles.Forsyth
42637da2899SCharles.Forsythstrlookup(a: array of string, s: string): int
42737da2899SCharles.Forsyth{
42837da2899SCharles.Forsyth	n := len a;
42937da2899SCharles.Forsyth	for(i := 0; i < n; i++) {
43037da2899SCharles.Forsyth		if(s == a[i])
43137da2899SCharles.Forsyth			return i;
43237da2899SCharles.Forsyth	}
43337da2899SCharles.Forsyth	return -1;
43437da2899SCharles.Forsyth}
43537da2899SCharles.Forsyth
43637da2899SCharles.Forsythhhmmss(date: string, tm: ref Tm): (int, string)
43737da2899SCharles.Forsyth{
43837da2899SCharles.Forsyth	err := (0, "");
43937da2899SCharles.Forsyth
44037da2899SCharles.Forsyth	(date, tm.hour) = datenum(date);
44137da2899SCharles.Forsyth	if(tm.hour < 0 || tm.hour >= 24)
44237da2899SCharles.Forsyth		return err;
44337da2899SCharles.Forsyth	(date, tm.min) = datenum(date);
44437da2899SCharles.Forsyth	if(tm.min < 0 || tm.min >= 60)
44537da2899SCharles.Forsyth		return err;
44637da2899SCharles.Forsyth	(date, tm.sec) = datenum(date);
44737da2899SCharles.Forsyth	if(tm.sec < 0 || tm.sec >= 60)
44837da2899SCharles.Forsyth		return err;
44937da2899SCharles.Forsyth
45037da2899SCharles.Forsyth	return (1, date);
45137da2899SCharles.Forsyth}
45237da2899SCharles.Forsyth
45337da2899SCharles.Forsythtzinfo(tz: string): (string, int)
45437da2899SCharles.Forsyth{
45537da2899SCharles.Forsyth	# strip leading and trailing whitespace
45637da2899SCharles.Forsyth	WS: con " \t";
45737da2899SCharles.Forsyth	tz = S->drop(tz, WS);
45837da2899SCharles.Forsyth	for(n := len tz; n > 0; n--) {
45937da2899SCharles.Forsyth		if(S->in(tz[n-1], WS) == 0)
46037da2899SCharles.Forsyth			break;
46137da2899SCharles.Forsyth	}
46237da2899SCharles.Forsyth	if(n < len tz)
46337da2899SCharles.Forsyth		tz = tz[:n];
46437da2899SCharles.Forsyth
46537da2899SCharles.Forsyth	# if no timezone, default to GMT
46637da2899SCharles.Forsyth	if(tz == nil)
46737da2899SCharles.Forsyth		return ("GMT", 0);
46837da2899SCharles.Forsyth
46937da2899SCharles.Forsyth	# GMT aliases
47037da2899SCharles.Forsyth	case tz {
47137da2899SCharles.Forsyth	"GMT" or
47237da2899SCharles.Forsyth	"UT" or
47337da2899SCharles.Forsyth	"UTC" or
47437da2899SCharles.Forsyth	"Z" =>
47537da2899SCharles.Forsyth		return ("GMT", 0);
47637da2899SCharles.Forsyth	}
47737da2899SCharles.Forsyth
47837da2899SCharles.Forsyth	# [+-]hhmm (hours and minutes offset from GMT)
47937da2899SCharles.Forsyth	if(len tz == 5 && (tz[0] == '+' || tz[0] == '-')) {
48037da2899SCharles.Forsyth		h := int tz[1:3];
48137da2899SCharles.Forsyth		m := int tz[3:5];
48237da2899SCharles.Forsyth		if(h > 23 || m > 59)
48337da2899SCharles.Forsyth			return ("", 0);
48437da2899SCharles.Forsyth		tzoff := h*SEC2HOUR + m*SEC2MIN;
48537da2899SCharles.Forsyth		if(tz[0] == '-')
48637da2899SCharles.Forsyth			tzoff = -tzoff;
48737da2899SCharles.Forsyth		return ("GMT", tzoff);
48837da2899SCharles.Forsyth	}
48937da2899SCharles.Forsyth
49037da2899SCharles.Forsyth	# try continental US timezones
49137da2899SCharles.Forsyth	filename: string;
49237da2899SCharles.Forsyth	case tz {
49337da2899SCharles.Forsyth	"CST" or "CDT" =>
49437da2899SCharles.Forsyth		filename = "CST.CDT";
49537da2899SCharles.Forsyth	"EST" or "EDT" =>
49637da2899SCharles.Forsyth		filename = "EST.EDT";
49737da2899SCharles.Forsyth	"MST" or "MDT" =>
49837da2899SCharles.Forsyth		filename = "MST.MDT";
49937da2899SCharles.Forsyth	"PST" or "PDT" =>
50037da2899SCharles.Forsyth		filename = "PST.PDT";
50137da2899SCharles.Forsyth	* =>
50237da2899SCharles.Forsyth		;	# default to local timezone
50337da2899SCharles.Forsyth	}
50437da2899SCharles.Forsyth	tzdata := readtimezone(filename);
50537da2899SCharles.Forsyth	if(tzdata.stname == tz)
50637da2899SCharles.Forsyth		return (tzdata.stname, tzdata.stdiff);
50737da2899SCharles.Forsyth	if(tzdata.dlname == tz)
50837da2899SCharles.Forsyth		return (tzdata.dlname, tzdata.dldiff);
50937da2899SCharles.Forsyth
51037da2899SCharles.Forsyth	return ("", 0);
51137da2899SCharles.Forsyth}
512