lib.fnl (3062B)
1 (import-macros { : define-tests : expect : expect= } :assert) 2 (local { : view} (require :fennel)) 3 4 5 (fn prepend [head tbl] 6 (if tbl 7 (doto tbl 8 (table.insert 1 head)) 9 [head])) 10 11 (fn clone [tbl] 12 (collect [k v (pairs tbl)] (values k v))) 13 14 15 (fn string-stream [] 16 (let [strings []] 17 { 18 :write #(table.insert strings $2) 19 :to-string #(table.concat strings) 20 })) 21 22 (fn trace [x] (print :trace (view x)) x) 23 24 25 ;; there are two date formats used in most of the extant posts: 26 ;; "Fri Jun 29 14:17:56 2018" (emacs current-time-string) 27 ;; and "Fri, 18 Oct 2013 10:18:34 +0100" (*rfc2822") 28 29 (local months 30 {"jan" 1 31 "feb" 2 32 "mar" 3 33 "apr" 4 34 "may" 5 35 "jun" 6 36 "jul" 7 37 "aug" 8 38 "sep" 9 39 "oct" 10 40 "nov" 11 41 "dec" 12 42 }) 43 44 (fn parse-rfc2822-timestamp [date-str] 45 (let [(s _ weekday day month year hour min sec offset) 46 (string.find date-str "(%a+), (%d+) (%a+) (%d+) (%d+):(%d+):(%d+) ([+-]%d+)" 1)] 47 (and s 48 (os.time { 49 : year 50 :month (. months (string.lower month)) 51 : day 52 : hour 53 : min 54 : sec 55 :isdst (not (= offset "+0000")) ;; massive cheating here 56 })))) 57 58 (fn parse-emacs-timestamp [date-str] 59 (let [(s _ weekday month day hour min sec year) 60 (string.find date-str "(%a+) (%a+) +(%d+) (%d+):(%d+):(%d+) (%d+)" 1)] 61 (and s 62 (os.time { 63 : year 64 :month (. months (string.lower month)) 65 : day 66 : hour 67 : min 68 : sec 69 :isdst true ; probably? 70 })))) 71 72 (fn parse-iso-timestamp [date-str] 73 (let [(s _ year month day hour min sec frac) 74 (string.find date-str "(%d+)%-(%d+)%-(%d+)[T ](%d+):(%d+):(%d+)%.(%d+) -Z")] 75 (and s 76 (os.time { 77 : year 78 : month 79 : day 80 : hour 81 : min 82 : sec 83 :isdst false 84 })))) 85 86 (fn parse-timestamp [s] 87 (or (parse-iso-timestamp s) 88 (parse-rfc2822-timestamp s) 89 (parse-emacs-timestamp s))) 90 91 (define-tests :date-parsing 92 (expect= (parse-rfc2822-timestamp "Fri, 18 Oct 2013 10:18:34 +0100") 93 1382087914) 94 (expect= (parse-rfc2822-timestamp "Tue, 28 Dec 2004 21:41:24 +0000") 95 1104270084) 96 (expect= (parse-emacs-timestamp "Sat Jul 19 23:53:21 2025") 1752965601) 97 (expect= (parse-rfc2822-timestamp "Sat Jul 19 23:53:21 2025") nil) 98 (expect= (parse-timestamp "Sat Jul 19 23:53:21 2025") 1752965601) 99 (expect= (parse-timestamp "Wed Aug 8 12:51:52 2018") 1533729112) 100 101 (expect= (parse-timestamp "2024-05-08T12:46:56.865432Z") 1715172416) 102 103 (expect= (parse-timestamp "2023-10-03 19:10:23.000000000 Z") 1696360223) 104 105 106 ) 107 108 109 110 111 { 112 : prepend : string-stream : trace "%%" string.format 113 : clone 114 : parse-timestamp 115 }