diff options
Diffstat (limited to 'appl/lib/daytime.b')
| -rw-r--r-- | appl/lib/daytime.b | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/appl/lib/daytime.b b/appl/lib/daytime.b new file mode 100644 index 00000000..a650f8ca --- /dev/null +++ b/appl/lib/daytime.b @@ -0,0 +1,478 @@ +implement Daytime; +# +# These routines convert time as follows: +# +# The epoch is 0000 Jan 1 1970 GMT. +# The argument time is in microseconds since then. +# The local(t) entry returns a reference to an ADT +# containing +# +# seconds (0-59) +# minutes (0-59) +# hours (0-23) +# day of month (1-31) +# month (0-11) +# year-1900 +# weekday (0-6, Sun is 0) +# day of the year +# daylight savings flag +# +# The routine gets the daylight savings time from the file /locale/timezone. +# +# text(tvec) +# where tvec is produced by local +# returns a string that has the time in the form +# +# Thu Jan 01 00:00:00 GMT 1970n0 +# 012345678901234567890123456789 +# 0 1 2 +# +# time() just reads the time from /dev/time +# and then calls localtime, then asctime. +# +# The sign bit of second times will turn on 68 years from the epoch ->2038 +# +include "sys.m"; +include "string.m"; +include "daytime.m"; + +S: String; +sys: Sys; + +dmsize := array[] of { + 31, 28, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 +}; +ldmsize := array[] of { + 31, 29, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 +}; + +Timezone: adt +{ + stname: string; + dlname: string; + stdiff: int; + dldiff: int; + dlpairs: array of int; +}; + +timezone: ref Timezone; + +now(): int +{ + if(sys == nil) + sys = load Sys Sys->PATH; + + fd := sys->open("/dev/time", sys->OREAD); + if(fd == nil) + return 0; + buf := array[128] of byte; + n := sys->read(fd, buf, len buf); + if(n < 0) + return 0; + + t := (big string buf[0:n]) / big 1000000; + return int t; +} + +time(): string +{ + t := now(); + tm := local(t); + return text(tm); +} + +local(tim: int): ref Tm +{ + ct: ref Tm; + + if(timezone == nil) + timezone = readtimezone(nil); + + t := tim + timezone.stdiff; + dlflag := 0; + for(i := 0; i+1 < len timezone.dlpairs; i += 2) { + if(t >= timezone.dlpairs[i] && t < timezone.dlpairs[i+1]) { + t = tim + timezone.dldiff; + dlflag++; + break; + } + } + ct = gmt(t); + if(dlflag) { + ct.zone = timezone.dlname; + ct.tzoff = timezone.dldiff; + } + else { + ct.zone = timezone.stname; + ct.tzoff = timezone.stdiff; + } + return ct; +} + +gmt(tim: int): ref Tm +{ + xtime := ref Tm; + + # break initial number into days + hms := tim % 86400; + day := tim / 86400; + if(hms < 0) { + hms += 86400; + day -= 1; + } + + # generate hours:minutes:seconds + xtime.sec = hms % 60; + d1 := hms / 60; + xtime.min = d1 % 60; + d1 /= 60; + xtime.hour = d1; + + # day is the day number. + # generate day of the week. + # The addend is 4 mod 7 (1/1/1970 was Thursday) + xtime.wday = (day + 7340036) % 7; + + # year number + if(day >= 0) + for(d1 = 70; day >= dysize(d1+1900); d1++) + day -= dysize(d1+1900); + else + for (d1 = 70; day < 0; d1--) + day += dysize(d1+1900-1); + xtime.year = d1; + d0 := day; + xtime.yday = d0; + + # generate month + if(dysize(d1+1900) == 366) + dmsz := ldmsize; + else + dmsz = dmsize; + for(d1 = 0; d0 >= dmsz[d1]; d1++) + d0 -= dmsz[d1]; + xtime.mday = d0 + 1; + xtime.mon = d1; + xtime.zone = "GMT"; + xtime.tzoff = 0; + return xtime; +} + +wkday := array[] of { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +weekday := array[] of { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +month := array[] of { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +text(t: ref Tm): string +{ + if(sys == nil) + sys = load Sys Sys->PATH; + + year := 1900+t.year; + + return sys->sprint("%s %s %.2d %.2d:%.2d:%.2d %s %d", + wkday[t.wday], + month[t.mon], + t.mday, + t.hour, + t.min, + t.sec, + t.zone, + year); +} + +filet(now: int, file: int): string +{ + if(sys == nil) + sys = load Sys Sys->PATH; + + t := local(file); + if(now - file < 6*30*24*3600) + return sys->sprint("%s %.2d %.2d:%.2d", + month[t.mon], t.mday, t.hour, t.min); + + year := 1900+t.year; + + return sys->sprint("%s %.2d %d", month[t.mon], t.mday, year); +} + +dysize(y: int): int +{ + if(y%4 == 0 && (y%100 != 0 || y%400 == 0)) + return 366; + return 365; +} + +readtimezone(fname: string): ref Timezone +{ + if(sys == nil) + sys = load Sys Sys->PATH; + + tz := ref Timezone; + tz.stdiff = 0; + tz.stname = "GMT"; + + fd: ref Sys->FD; + if(fname == nil){ + fd = sys->open("/env/timezone", Sys->OREAD); + if(fd == nil) + fd = sys->open("/locale/timezone", Sys->OREAD); + }else + fd = sys->open("/locale/" + fname, sys->OREAD); + if(fd == nil) + return tz; + buf := array[2048] of byte; + cnt := sys->read(fd, buf, len buf); + if(cnt <= 0) + return tz; + + (n, val) := sys->tokenize(string buf[0:cnt], "\t \n\r"); + if(n < 4) + return tz; + + tz.stname = hd val; + val = tl val; + tz.stdiff = int hd val; + val = tl val; + tz.dlname = hd val; + val = tl val; + tz.dldiff = int hd val; + val = tl val; + + tz.dlpairs = array[n-4] of {* => 0}; + for(j := 0; val != nil; val = tl val) + tz.dlpairs[j++] = int hd val; + return tz; +} + +SEC2MIN: con 60; +SEC2HOUR: con 60*SEC2MIN; +SEC2DAY: con 24*SEC2HOUR; + +tm2epoch(tm: ref Tm): int +{ + secs := 0; + + # + # seconds per year + # + yr := tm.year + 1900; + if(yr < 1970) + for(i := yr; i < 1970; i++) + secs -= dysize(i) * SEC2DAY; + else + for(i = 1970; i < yr; i++) + secs += dysize(i) * SEC2DAY; + # + # seconds per month + # + if(dysize(yr) == 366) + dmsz := ldmsize; + else + dmsz = dmsize; + for(i = 0; i < tm.mon; i++) + secs += dmsz[i] * SEC2DAY; + + # + # secs in last month + # + secs += (tm.mday-1) * SEC2DAY; + + # + # hours, minutes, seconds + # + secs += tm.hour * SEC2HOUR; + secs += tm.min * SEC2MIN; + secs += tm.sec; + + # + # time zone offset includes daylight savings time + # + return secs - tm.tzoff; +} + +# handle three formats (we'll be a bit more tolerant) +# Sun, 06 Nov 1994 08:49:37 TZ (rfc822+rfc1123) +# Sunday, 06-Nov-94 08:49:37 TZ (rfc850, obsoleted by rfc1036) +# Sun Nov 6 08:49:37 1994 (ANSI C's asctime() format, assume GMT) +# +# return nil on parsing error +# +string2tm(date: string): ref Tm +{ + buf: string; + ok: int; + tm := ref Tm; + + if(S == nil) + S = load String String->PATH; + + # Weekday|Wday + (date, buf) = dateword(date); + tm.wday = strlookup(wkday, buf); + if(tm.wday < 0) + tm.wday = strlookup(weekday, buf); + if(tm.wday < 0) + return nil; + + # Try Mon + odate := date; + (date, buf) = dateword(date); + tm.mon = strlookup(month, buf); + if(tm.mon >= 0) { + # Mon was OK, so asctime() format + # DD + (date, tm.mday) = datenum(date); + if(tm.mday < 1 || tm.mday > 31) + return nil; + + # HH:MM:SS + (ok, date) = hhmmss(date, tm); + if(!ok) + return nil; + + # YY|YYYY + (buf, tm.year) = datenum(date); + if(tm.year > 1900) + tm.year -= 1900; + } else { + # Mon was not OK + date = odate; + # DD Mon YYYY or DD-Mon-(YY|YYYY) + (date, tm.mday) = datenum(date); + if(tm.mday < 1 || tm.mday > 31) + return nil; + (date, buf) = dateword(date); + tm.mon = strlookup(month, buf); + if(tm.mon < 0 || tm.mon >= 12) + return nil; + (date, tm.year) = datenum(date); + if(tm.year > 1900) + tm.year -= 1900; + + # HH:MM:SS + (ok, buf) = hhmmss(date, tm); + if(!ok) + return nil; + } + (tm.zone, tm.tzoff) = tzinfo(buf); + if(tm.zone == "") + return nil; + + return tm; +} + +dateword(date: string): (string, string) +{ + notalnum: con "^A-Za-z0-9"; + + date = S->drop(date, notalnum); + (w, rest) := S->splitl(date, notalnum); + return (rest, w); +} + +datenum(date: string): (string, int) +{ + notdig: con "^0-9"; + + date = S->drop(date, notdig); + (num, rest) := S->splitl(date, notdig); + return (rest, int num); +} + +strlookup(a: array of string, s: string): int +{ + n := len a; + for(i := 0; i < n; i++) { + if(s == a[i]) + return i; + } + return -1; +} + +hhmmss(date: string, tm: ref Tm): (int, string) +{ + err := (0, ""); + + (date, tm.hour) = datenum(date); + if(tm.hour < 0 || tm.hour >= 24) + return err; + (date, tm.min) = datenum(date); + if(tm.min < 0 || tm.min >= 60) + return err; + (date, tm.sec) = datenum(date); + if(tm.sec < 0 || tm.sec >= 60) + return err; + + return (1, date); +} + +tzinfo(tz: string): (string, int) +{ + # strip leading and trailing whitespace + WS: con " \t"; + tz = S->drop(tz, WS); + for(n := len tz; n > 0; n--) { + if(S->in(tz[n-1], WS) == 0) + break; + } + if(n < len tz) + tz = tz[:n]; + + # if no timezone, default to GMT + if(tz == nil) + return ("GMT", 0); + + # GMT aliases + case tz { + "GMT" or + "UT" or + "UTC" or + "Z" => + return ("GMT", 0); + } + + # [+-]hhmm (hours and minutes offset from GMT) + if(len tz == 5 && (tz[0] == '+' || tz[0] == '-')) { + h := int tz[1:3]; + m := int tz[3:5]; + if(h > 23 || m > 59) + return ("", 0); + tzoff := h*SEC2HOUR + m*SEC2MIN; + if(tz[0] == '-') + tzoff = -tzoff; + return ("GMT", tzoff); + } + + # try continental US timezones + filename: string; + case tz { + "CST" or "CDT" => + filename = "CST.CDT"; + "EST" or "EDT" => + filename = "EST.EDT"; + "MST" or "MDT" => + filename = "MST.MDT"; + "PST" or "PDT" => + filename = "PST.PDT"; + * => + ; # default to local timezone + } + tzdata := readtimezone(filename); + if(tzdata.stname == tz) + return (tzdata.stname, tzdata.stdiff); + if(tzdata.dlname == tz) + return (tzdata.dlname, tzdata.dldiff); + + return ("", 0); +} |
