daytime.b 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. implement Daytime;
  2. #
  3. # These routines convert time as follows:
  4. #
  5. # The epoch is 0000 Jan 1 1970 GMT.
  6. # The argument time is in microseconds since then.
  7. # The local(t) entry returns a reference to an ADT
  8. # containing
  9. #
  10. # seconds (0-59)
  11. # minutes (0-59)
  12. # hours (0-23)
  13. # day of month (1-31)
  14. # month (0-11)
  15. # year-1900
  16. # weekday (0-6, Sun is 0)
  17. # day of the year
  18. # daylight savings flag
  19. #
  20. # The routine gets the daylight savings time from the file /locale/timezone.
  21. #
  22. # text(tvec)
  23. # where tvec is produced by local
  24. # returns a string that has the time in the form
  25. #
  26. # Thu Jan 01 00:00:00 GMT 1970n0
  27. # 012345678901234567890123456789
  28. # 0 1 2
  29. #
  30. # time() just reads the time from /dev/time
  31. # and then calls localtime, then asctime.
  32. #
  33. # The sign bit of second times will turn on 68 years from the epoch ->2038
  34. #
  35. include "sys.m";
  36. include "string.m";
  37. include "daytime.m";
  38. S: String;
  39. sys: Sys;
  40. dmsize := array[] of {
  41. 31, 28, 31, 30, 31, 30,
  42. 31, 31, 30, 31, 30, 31
  43. };
  44. ldmsize := array[] of {
  45. 31, 29, 31, 30, 31, 30,
  46. 31, 31, 30, 31, 30, 31
  47. };
  48. Timezone: adt
  49. {
  50. stname: string;
  51. dlname: string;
  52. stdiff: int;
  53. dldiff: int;
  54. dlpairs: array of int;
  55. };
  56. timezone: ref Timezone;
  57. now(): int
  58. {
  59. if(sys == nil)
  60. sys = load Sys Sys->PATH;
  61. fd := sys->open("/dev/time", sys->OREAD);
  62. if(fd == nil)
  63. return 0;
  64. buf := array[128] of byte;
  65. n := sys->read(fd, buf, len buf);
  66. if(n < 0)
  67. return 0;
  68. t := (big string buf[0:n]) / big 1000000;
  69. return int t;
  70. }
  71. time(): string
  72. {
  73. t := now();
  74. tm := local(t);
  75. return text(tm);
  76. }
  77. local(tim: int): ref Tm
  78. {
  79. ct: ref Tm;
  80. if(timezone == nil)
  81. timezone = readtimezone(nil);
  82. t := tim + timezone.stdiff;
  83. dlflag := 0;
  84. for(i := 0; i+1 < len timezone.dlpairs; i += 2) {
  85. if(t >= timezone.dlpairs[i] && t < timezone.dlpairs[i+1]) {
  86. t = tim + timezone.dldiff;
  87. dlflag++;
  88. break;
  89. }
  90. }
  91. ct = gmt(t);
  92. if(dlflag) {
  93. ct.zone = timezone.dlname;
  94. ct.tzoff = timezone.dldiff;
  95. }
  96. else {
  97. ct.zone = timezone.stname;
  98. ct.tzoff = timezone.stdiff;
  99. }
  100. return ct;
  101. }
  102. gmt(tim: int): ref Tm
  103. {
  104. xtime := ref Tm;
  105. # break initial number into days
  106. hms := tim % 86400;
  107. day := tim / 86400;
  108. if(hms < 0) {
  109. hms += 86400;
  110. day -= 1;
  111. }
  112. # generate hours:minutes:seconds
  113. xtime.sec = hms % 60;
  114. d1 := hms / 60;
  115. xtime.min = d1 % 60;
  116. d1 /= 60;
  117. xtime.hour = d1;
  118. # day is the day number.
  119. # generate day of the week.
  120. # The addend is 4 mod 7 (1/1/1970 was Thursday)
  121. xtime.wday = (day + 7340036) % 7;
  122. # year number
  123. if(day >= 0)
  124. for(d1 = 70; day >= dysize(d1+1900); d1++)
  125. day -= dysize(d1+1900);
  126. else
  127. for (d1 = 70; day < 0; d1--)
  128. day += dysize(d1+1900-1);
  129. xtime.year = d1;
  130. d0 := day;
  131. xtime.yday = d0;
  132. # generate month
  133. if(dysize(d1+1900) == 366)
  134. dmsz := ldmsize;
  135. else
  136. dmsz = dmsize;
  137. for(d1 = 0; d0 >= dmsz[d1]; d1++)
  138. d0 -= dmsz[d1];
  139. xtime.mday = d0 + 1;
  140. xtime.mon = d1;
  141. xtime.zone = "GMT";
  142. xtime.tzoff = 0;
  143. return xtime;
  144. }
  145. wkday := array[] of {
  146. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  147. };
  148. weekday := array[] of {
  149. "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
  150. };
  151. month := array[] of {
  152. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  153. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  154. };
  155. text(t: ref Tm): string
  156. {
  157. if(sys == nil)
  158. sys = load Sys Sys->PATH;
  159. year := 1900+t.year;
  160. return sys->sprint("%s %s %.2d %.2d:%.2d:%.2d %s %d",
  161. wkday[t.wday],
  162. month[t.mon],
  163. t.mday,
  164. t.hour,
  165. t.min,
  166. t.sec,
  167. t.zone,
  168. year);
  169. }
  170. filet(now: int, file: int): string
  171. {
  172. if(sys == nil)
  173. sys = load Sys Sys->PATH;
  174. t := local(file);
  175. if(now - file < 6*30*24*3600)
  176. return sys->sprint("%s %.2d %.2d:%.2d",
  177. month[t.mon], t.mday, t.hour, t.min);
  178. year := 1900+t.year;
  179. return sys->sprint("%s %.2d %d", month[t.mon], t.mday, year);
  180. }
  181. dysize(y: int): int
  182. {
  183. if(y%4 == 0 && (y%100 != 0 || y%400 == 0))
  184. return 366;
  185. return 365;
  186. }
  187. readtimezone(fname: string): ref Timezone
  188. {
  189. if(sys == nil)
  190. sys = load Sys Sys->PATH;
  191. tz := ref Timezone;
  192. tz.stdiff = 0;
  193. tz.stname = "GMT";
  194. s: string;
  195. if(fname == nil){
  196. s = readfile("/env/timezone");
  197. if(s == nil)
  198. s = readfile("/locale/timezone");
  199. }else{
  200. if(fname[0] != '/' && fname[0] != '#')
  201. fname = "/locale/" + fname;
  202. s = readfile(fname);
  203. }
  204. if(s == nil)
  205. return tz;
  206. if(s[0] == '/' || s[0] == '#'){
  207. if(s[len s-1] == '\n')
  208. s = s[0: len s-1];
  209. s = readfile(s);
  210. if(s == nil)
  211. return tz;
  212. }
  213. (n, val) := sys->tokenize(s, "\t \n\r");
  214. if(n < 4)
  215. return tz;
  216. tz.stname = hd val;
  217. val = tl val;
  218. tz.stdiff = int hd val;
  219. val = tl val;
  220. tz.dlname = hd val;
  221. val = tl val;
  222. tz.dldiff = int hd val;
  223. val = tl val;
  224. tz.dlpairs = array[n-4] of {* => 0};
  225. for(j := 0; val != nil; val = tl val)
  226. tz.dlpairs[j++] = int hd val;
  227. return tz;
  228. }
  229. readfile(name: string): string
  230. {
  231. fd := sys->open(name, Sys->OREAD);
  232. if(fd == nil)
  233. return nil;
  234. buf := array[2048] of byte;
  235. n := sys->read(fd, buf, len buf);
  236. if(n <= 0)
  237. return nil;
  238. return string buf[0:n];
  239. }
  240. SEC2MIN: con 60;
  241. SEC2HOUR: con 60*SEC2MIN;
  242. SEC2DAY: con 24*SEC2HOUR;
  243. tm2epoch(tm: ref Tm): int
  244. {
  245. secs := 0;
  246. #
  247. # seconds per year
  248. #
  249. yr := tm.year + 1900;
  250. if(yr < 1970)
  251. for(i := yr; i < 1970; i++)
  252. secs -= dysize(i) * SEC2DAY;
  253. else
  254. for(i = 1970; i < yr; i++)
  255. secs += dysize(i) * SEC2DAY;
  256. #
  257. # seconds per month
  258. #
  259. if(dysize(yr) == 366)
  260. dmsz := ldmsize;
  261. else
  262. dmsz = dmsize;
  263. for(i = 0; i < tm.mon; i++)
  264. secs += dmsz[i] * SEC2DAY;
  265. #
  266. # secs in last month
  267. #
  268. secs += (tm.mday-1) * SEC2DAY;
  269. #
  270. # hours, minutes, seconds
  271. #
  272. secs += tm.hour * SEC2HOUR;
  273. secs += tm.min * SEC2MIN;
  274. secs += tm.sec;
  275. #
  276. # time zone offset includes daylight savings time
  277. #
  278. return secs - tm.tzoff;
  279. }
  280. # handle three formats (we'll be a bit more tolerant)
  281. # Sun, 06 Nov 1994 08:49:37 TZ (rfc822+rfc1123)
  282. # Sunday, 06-Nov-94 08:49:37 TZ (rfc850, obsoleted by rfc1036)
  283. # Sun Nov 6 08:49:37 1994 (ANSI C's asctime() format, assume GMT)
  284. #
  285. # return nil on parsing error
  286. #
  287. string2tm(date: string): ref Tm
  288. {
  289. buf: string;
  290. ok: int;
  291. tm := ref Tm;
  292. if(S == nil)
  293. S = load String String->PATH;
  294. # Weekday|Wday
  295. (date, buf) = dateword(date);
  296. tm.wday = strlookup(wkday, buf);
  297. if(tm.wday < 0)
  298. tm.wday = strlookup(weekday, buf);
  299. if(tm.wday < 0)
  300. return nil;
  301. # Try Mon
  302. odate := date;
  303. (date, buf) = dateword(date);
  304. tm.mon = strlookup(month, buf);
  305. if(tm.mon >= 0) {
  306. # Mon was OK, so asctime() format
  307. # DD
  308. (date, tm.mday) = datenum(date);
  309. if(tm.mday < 1 || tm.mday > 31)
  310. return nil;
  311. # HH:MM:SS
  312. (ok, date) = hhmmss(date, tm);
  313. if(!ok)
  314. return nil;
  315. # optional time zone
  316. while(date != nil && date[0] == ' ')
  317. date = date[1:];
  318. if(date != nil && !(date[0] >= '0' && date[0] <= '9')){
  319. for(i := 0; i < len date; i++)
  320. if(date[i] == ' '){
  321. (tm.zone, tm.tzoff) = tzinfo(date[0: i]);
  322. date = date[i:];
  323. break;
  324. }
  325. }
  326. # YY|YYYY
  327. (nil, tm.year) = datenum(date);
  328. if(tm.year > 1900)
  329. tm.year -= 1900;
  330. if(tm.zone == ""){
  331. tm.zone = "GMT";
  332. tm.tzoff = 0;
  333. }
  334. } else {
  335. # Mon was not OK
  336. date = odate;
  337. # DD Mon YYYY or DD-Mon-(YY|YYYY)
  338. (date, tm.mday) = datenum(date);
  339. if(tm.mday < 1 || tm.mday > 31)
  340. return nil;
  341. (date, buf) = dateword(date);
  342. tm.mon = strlookup(month, buf);
  343. if(tm.mon < 0 || tm.mon >= 12)
  344. return nil;
  345. (date, tm.year) = datenum(date);
  346. if(tm.year > 1900)
  347. tm.year -= 1900;
  348. # HH:MM:SS
  349. (ok, buf) = hhmmss(date, tm);
  350. if(!ok)
  351. return nil;
  352. (tm.zone, tm.tzoff) = tzinfo(buf);
  353. if(tm.zone == "")
  354. return nil;
  355. }
  356. return tm;
  357. }
  358. dateword(date: string): (string, string)
  359. {
  360. notalnum: con "^A-Za-z0-9";
  361. date = S->drop(date, notalnum);
  362. (w, rest) := S->splitl(date, notalnum);
  363. return (rest, w);
  364. }
  365. datenum(date: string): (string, int)
  366. {
  367. notdig: con "^0-9";
  368. date = S->drop(date, notdig);
  369. (num, rest) := S->splitl(date, notdig);
  370. return (rest, int num);
  371. }
  372. strlookup(a: array of string, s: string): int
  373. {
  374. n := len a;
  375. for(i := 0; i < n; i++) {
  376. if(s == a[i])
  377. return i;
  378. }
  379. return -1;
  380. }
  381. hhmmss(date: string, tm: ref Tm): (int, string)
  382. {
  383. err := (0, "");
  384. (date, tm.hour) = datenum(date);
  385. if(tm.hour < 0 || tm.hour >= 24)
  386. return err;
  387. (date, tm.min) = datenum(date);
  388. if(tm.min < 0 || tm.min >= 60)
  389. return err;
  390. (date, tm.sec) = datenum(date);
  391. if(tm.sec < 0 || tm.sec >= 60)
  392. return err;
  393. return (1, date);
  394. }
  395. tzinfo(tz: string): (string, int)
  396. {
  397. # strip leading and trailing whitespace
  398. WS: con " \t";
  399. tz = S->drop(tz, WS);
  400. for(n := len tz; n > 0; n--) {
  401. if(S->in(tz[n-1], WS) == 0)
  402. break;
  403. }
  404. if(n < len tz)
  405. tz = tz[:n];
  406. # if no timezone, default to GMT
  407. if(tz == nil)
  408. return ("GMT", 0);
  409. # GMT aliases
  410. case tz {
  411. "GMT" or
  412. "UT" or
  413. "UTC" or
  414. "Z" =>
  415. return ("GMT", 0);
  416. }
  417. # [+-]hhmm (hours and minutes offset from GMT)
  418. if(len tz == 5 && (tz[0] == '+' || tz[0] == '-')) {
  419. h := int tz[1:3];
  420. m := int tz[3:5];
  421. if(h > 23 || m > 59)
  422. return ("", 0);
  423. tzoff := h*SEC2HOUR + m*SEC2MIN;
  424. if(tz[0] == '-')
  425. tzoff = -tzoff;
  426. return ("GMT", tzoff);
  427. }
  428. # try continental US timezones
  429. filename: string;
  430. case tz {
  431. "CST" or "CDT" =>
  432. filename = "CST.CDT";
  433. "EST" or "EDT" =>
  434. filename = "EST.EDT";
  435. "MST" or "MDT" =>
  436. filename = "MST.MDT";
  437. "PST" or "PDT" =>
  438. filename = "PST.PDT";
  439. * =>
  440. ; # default to local timezone
  441. }
  442. tzdata := readtimezone(filename);
  443. if(tzdata.stname == tz)
  444. return (tzdata.stname, tzdata.stdiff);
  445. if(tzdata.dlname == tz)
  446. return (tzdata.dlname, tzdata.dldiff);
  447. return ("", 0);
  448. }