CCSDS 502.0-B-3 Orbit Ephemeris Message parser and interpolator
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Replace custom date/time code with ptime across 4 packages

- ocaml-sgp4: epoch_unix uses Ptime.of_date instead of manual leap year math
- ocaml-oem: parse_epoch uses Ptime.of_rfc3339 ~strict:false, replacing
60 lines of manual ISO 8601 splitting
- space: parse_since uses Ptime.of_rfc3339 ~strict:false instead of Scanf
- monopam: day_of_week uses Ptime.weekday, add_days uses Ptime.add_span

Reviewed all 12 flagged spots in mono — the remaining 8 either already
use ptime properly (tomlt, cookeio, requests) or are not date parsing
(coordinate Julian arithmetic, filename extraction).

+5 -55
+5 -55
lib/oem.ml
··· 79 79 (** Parse CCSDS epoch: "YYYY-MM-DDThh:mm:ss.sss" or "YYYY-MM-DD hh:mm:ss.sss". *) 80 80 let parse_epoch s = 81 81 let s = String.trim s in 82 - (* Normalize separator *) 83 - let s = 84 - if String.contains s 'T' then s 85 - else 86 - match String.index_opt s ' ' with 87 - | Some i -> 88 - String.sub s 0 i ^ "T" ^ String.sub s (i + 1) (String.length s - i - 1) 89 - | None -> s 82 + let rfc = 83 + if String.contains s 'Z' || String.contains s '+' then s else s ^ "Z" 90 84 in 91 - (* Split at T *) 92 - match String.split_on_char 'T' s with 93 - | [ date; time ] -> 94 - let parse_date d = 95 - match String.split_on_char '-' d with 96 - | [ y; m; d ] -> 97 - (try Some (int_of_string y, int_of_string m, int_of_string d) 98 - with Failure _ | Invalid_argument _ -> None) 99 - | _ -> None 100 - in 101 - let parse_time t = 102 - (* Handle fractional seconds *) 103 - let parts = String.split_on_char ':' t in 104 - match parts with 105 - | [ h; m; s ] -> 106 - let sec_parts = String.split_on_char '.' s in 107 - let sec_int = 108 - match sec_parts with 109 - | si :: _ -> (try int_of_string si with Failure _ -> 0) 110 - | [] -> 0 111 - in 112 - let frac = 113 - match sec_parts with 114 - | _ :: f :: _ -> 115 - let f = if String.length f > 9 then String.sub f 0 9 else f in 116 - let denom = Float.of_int (int_of_float (10. ** Float.of_int (String.length f))) in 117 - (try Float.of_int (int_of_string f) /. denom with Failure _ -> 0.) 118 - | _ -> 0. 119 - in 120 - (try 121 - Some 122 - (int_of_string h, int_of_string m, sec_int, frac) 123 - with Failure _ | Invalid_argument _ -> None) 124 - | _ -> None 125 - in 126 - (match (parse_date date, parse_time time) with 127 - | Some (y, mo, d), Some (h, mi, s, frac) -> 128 - let frac_ps = Int64.of_float (frac *. 1e12) in 129 - (match Ptime.of_date_time ((y, mo, d), ((h, mi, s), 0)) with 130 - | Some t -> 131 - (match Ptime.Span.of_d_ps (0, frac_ps) with 132 - | Some span -> 133 - Some (Ptime.add_span t span |> Option.value ~default:t) 134 - | None -> Some t) 135 - | None -> None) 136 - | _ -> None) 137 - | _ -> None 85 + match Ptime.of_rfc3339 ~strict:false rfc with 86 + | Ok (t, _, _) -> Some t 87 + | Error _ -> None 138 88 139 89 (* ------------------------------------------------------------------ *) 140 90 (* KVN Parser *)