My agentic slop goes here. Not intended for anyone else!
at jsont 5.6 kB view raw
1open Printf 2open Scanf 3 4type t = Ptime.t 5 6let epoch = Ptime.epoch 7let compare = Ptime.compare 8let max d1 d2 = if compare d1 d2 < 0 then d2 else d1 9let min d1 d2 = if compare d1 d2 < 0 then d1 else d2 10let month_to_int = Hashtbl.create 12 11 12let () = 13 let add m i = Hashtbl.add month_to_int m i in 14 add "Jan" 1 ; 15 add "Feb" 2 ; 16 add "Mar" 3 ; 17 add "Apr" 4 ; 18 add "May" 5 ; 19 add "Jun" 6 ; 20 add "Jul" 7 ; 21 add "Aug" 8 ; 22 add "Sep" 9 ; 23 add "Oct" 10 ; 24 add "Nov" 11 ; 25 add "Dec" 12 26 27let map f = function Some x -> f x | None -> None 28let map2 f a b = match (a, b) with Some a, Some b -> f a b | _ -> None 29 30(* RFC3339 date *) 31let of_rfc3339 s = 32 match Ptime.of_rfc3339 ~strict:false s with 33 | Result.Error _ -> 34 invalid_arg (sprintf "Syndic.Date.of_string: cannot parse %S" s) 35 | Result.Ok (t, tz_offset_s, _) -> ( 36 match Ptime.of_date_time @@ Ptime.to_date_time ?tz_offset_s t with 37 | Some x -> x 38 | None -> invalid_arg (sprintf "Syndic.Data.of_string: cannot part %S" s) ) 39 40(* Format: 41 http://www.rssboard.org/rss-specification#ltpubdategtSubelementOfLtitemgt 42 Examples: Sun, 19 May 2002 15:21:36 GMT Sat, 25 Sep 2010 08:01:00 -0700 20 43 Mar 2013 03:47:14 +0000 *) 44let of_rfc822 s = 45 let make_date day month year h m maybe_s z = 46 let month = 47 if String.length month <= 3 then month else String.sub month 0 3 48 in 49 let month = Hashtbl.find month_to_int month in 50 let date = Ptime.of_date (year, month, day) in 51 let s = 52 if maybe_s <> "" && maybe_s.[0] = ':' then 53 float_of_string (String.sub maybe_s 1 (String.length maybe_s - 1)) 54 else 0. 55 in 56 let span = Ptime.Span.of_int_s ((h * 3600) + (m * 60)) in 57 let span = 58 map (fun x -> Some (Ptime.Span.add span x)) (Ptime.Span.of_float_s s) 59 in 60 let date_and_time = 61 if z = "" || z = "GMT" || z = "UT" || z = "Z" then 62 map2 (fun date span -> Ptime.add_span date span) date span 63 |> map (fun x -> Some (Ptime.to_date_time x)) 64 else 65 (* FIXME: this should be made more robust. *) 66 let tz_offset_s = 67 match z with 68 | "EST" -> -5 * 3600 69 | "EDT" -> -4 * 3600 70 | "CST" -> -6 * 3600 71 | "CDT" -> -5 * 3600 72 | "MST" -> -7 * 3600 73 | "MDT" -> -6 * 3600 74 | "PST" -> -8 * 3600 75 | "PDT" -> -7 * 3600 76 | "A" -> -1 * 3600 77 | "M" -> -12 * 3600 78 | "N" -> 1 * 3600 79 | "Y" -> 12 * 3600 80 | _ -> 81 let zh = sscanf (String.sub z 0 3) "%i" (fun i -> i) in 82 let zm = sscanf (String.sub z 3 2) "%i" (fun i -> i) in 83 let tz_sign = if zh < 0 then -1 else 1 in 84 if zh < 0 then tz_sign * ((-zh * 3600) + (zm * 60)) 85 else tz_sign * ((zh * 3600) + (zm * 60)) 86 in 87 let rt = map2 (fun date span -> Ptime.add_span date span) date span in 88 (* XXX: We lose minutes with this conversion, but Calendar does not 89 propose to handle minutes. *) 90 map (fun x -> Some (Ptime.to_date_time ~tz_offset_s x)) rt 91 in 92 match map Ptime.of_date_time date_and_time with 93 | Some x -> x 94 | None -> invalid_arg (sprintf "Syndic.Date.of_rfc822: cannot parse") 95 in 96 try 97 if 'A' <= s.[0] && s.[0] <= 'Z' then 98 try sscanf s "%_s %i %s %i %i:%i%s %s" make_date with _ -> 99 try sscanf s "%_s %ist %s %i %i:%i%s %s" make_date with _ -> 100 (* For e.g. "May 15th, 2019" — even though it is not standard *) 101 sscanf s "%s %i%_s %i" (fun m d y -> make_date d m y 0 0 "" "UT") 102 else 103 try sscanf s "%i %s %i %i:%i%s %s" make_date with _ -> 104 sscanf s "%i %s %i" (fun d m y -> make_date d m y 0 0 "" "UT") 105 with _ -> 106 (* Fallback: Some RSS feeds use RFC3339 dates instead of RFC822 *) 107 try of_rfc3339 s 108 with _ -> invalid_arg (sprintf "Syndic.Date.of_string+: cannot parse %S" s) 109 110type month = 111 | Jan 112 | Feb 113 | Mar 114 | Apr 115 | May 116 | Jun 117 | Jul 118 | Aug 119 | Sep 120 | Oct 121 | Nov 122 | Dec 123 124type day = Thu | Fri | Sat | Sun | Mon | Tue | Wed 125 126let string_of_month = function 127 | Jan -> "Jan" 128 | Feb -> "Feb" 129 | Mar -> "Mar" 130 | Apr -> "Apr" 131 | May -> "May" 132 | Jun -> "Jun" 133 | Jul -> "Jul" 134 | Aug -> "Aug" 135 | Sep -> "Sep" 136 | Oct -> "Oct" 137 | Nov -> "Nov" 138 | Dec -> "Dec" 139 140let month_of_date = 141 let months = 142 [|Jan; Feb; Mar; Apr; May; Jun; Jul; Aug; Sep; Oct; Nov; Dec|] 143 in 144 fun t -> 145 let _, i, _ = Ptime.to_date t in 146 months.(i - 1) 147 148let to_rfc3339 d = 149 (* Example: 2014-03-19T15:51:25.050-07:00 *) 150 Ptime.to_rfc3339 d 151 152(* Convenience functions *) 153 154let day_of_week = 155 let wday = [|Thu; Fri; Sat; Sun; Mon; Tue; Wed|] in 156 fun t -> 157 let i = fst Ptime.(Span.to_d_ps @@ to_span t) mod 7 in 158 wday.((if i < 0 then 7 + i else i)) 159 160let string_of_day = function 161 | Thu -> "Thu" 162 | Fri -> "Fri" 163 | Sat -> "Sat" 164 | Sun -> "Sun" 165 | Mon -> "Mon" 166 | Tue -> "Tue" 167 | Wed -> "Wed" 168 169let year t = 170 let year, _, _ = Ptime.to_date t in 171 year 172 173let month = month_of_date 174 175let day t = 176 let (_, _, day), _ = Ptime.to_date_time t in 177 day 178 179let hour t = 180 let _, ((hh, _, _), _) = Ptime.to_date_time t in 181 hh 182 183let minute t = 184 let _, ((_, mm, _), _) = Ptime.to_date_time t in 185 mm 186 187let second t = 188 let _, ((_, _, ss), _) = Ptime.to_date_time t in 189 float_of_int ss 190 191let to_rfc822 t = 192 (* Example: Sat, 25 Sep 2010 08:01:00 -0700 *) 193 let ds = day_of_week t |> string_of_day in 194 let ms = month_of_date t |> string_of_month in 195 let (y, _m, d), ((hh, mm, ss), t) = Ptime.to_date_time t in 196 Printf.sprintf "%s, %d %s %d %02d:%02d:%02d %04d" ds d ms y hh mm ss t