123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- 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";
- s: string;
- if(fname == nil){
- s = readfile("/env/timezone");
- if(s == nil)
- s = readfile("/locale/timezone");
- }else{
- if(fname[0] != '/' && fname[0] != '#')
- fname = "/locale/" + fname;
- s = readfile(fname);
- }
- if(s == nil)
- return tz;
- if(s[0] == '/' || s[0] == '#'){
- if(s[len s-1] == '\n')
- s = s[0: len s-1];
- s = readfile(s);
- if(s == nil)
- return tz;
- }
- (n, val) := sys->tokenize(s, "\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;
- }
- readfile(name: string): string
- {
- fd := sys->open(name, Sys->OREAD);
- if(fd == nil)
- return nil;
- buf := array[2048] of byte;
- n := sys->read(fd, buf, len buf);
- if(n <= 0)
- return nil;
- return string buf[0:n];
- }
- 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;
- # optional time zone
- while(date != nil && date[0] == ' ')
- date = date[1:];
- if(date != nil && !(date[0] >= '0' && date[0] <= '9')){
- for(i := 0; i < len date; i++)
- if(date[i] == ' '){
- (tm.zone, tm.tzoff) = tzinfo(date[0: i]);
- date = date[i:];
- break;
- }
- }
- # YY|YYYY
- (nil, tm.year) = datenum(date);
- if(tm.year > 1900)
- tm.year -= 1900;
- if(tm.zone == ""){
- tm.zone = "GMT";
- tm.tzoff = 0;
- }
- } 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);
- }
|