···
let calendar = t.calendar in
{ collection; file; event; calendar }
+
type format = [ `Text | `Entries | `Json | `Csv | `Ics | `Sexp ]
+
let format_date ?tz date =
+
let dt = Date.ptime_to_timedesc ?tz date in
+
let y = Timedesc.year dt in
+
let m = Timedesc.month dt in
+
let d = Timedesc.day dt in
+
match Timedesc.weekday dt with
+
Printf.sprintf "%04d-%02d-%02d %s" y m d weekday
+
let format_time ?tz date =
+
let dt = Date.ptime_to_timedesc ?tz date in
+
let h = Timedesc.hour dt in
+
let m = Timedesc.minute dt in
+
Printf.sprintf "%02d:%02d" h m
+
let format_datetime ?tz date =
+
| Some tz -> Printf.sprintf "(%s)" (Timedesc.Time_zone.name tz)
+
Printf.sprintf "%s %s%s" (format_date ?tz date) (format_time ?tz date) tz_str
+
let next_day day ~next =
+
let y1, m1, d1 = Ptime.to_date day in
+
let y2, m2, d2 = Ptime.to_date next in
+
y1 == y2 && m1 == m2 && d1 == d2 - 1
+
(* exosed from icalendar *)
+
(`Minutely, "MINUTELY");
+
(`Secondly, "SECONDLY");
+
let date_to_str (y, m, d) = Printf.sprintf "%04d%02d%02d" y m d
+
let datetime_to_str ptime utc =
+
let date, ((hh, mm, ss), _) = Ptime.to_date_time ptime in
+
Printf.sprintf "%sT%02d%02d%02d%s" (date_to_str date) hh mm ss
+
(if utc then "Z" else "")
+
let timestamp_to_ics ts buf =
+
| `Utc ts -> datetime_to_str ts true
+
| `Local ts -> datetime_to_str ts false
+
| `With_tzid (ts, _str) -> (* TODO *) datetime_to_str ts false
+
let recurs_to_ics (freq, count_or_until, interval, l) buf =
+
let write_rulepart key value =
+
Buffer.add_string buf key;
+
Buffer.add_char buf '=';
+
Buffer.add_string buf value
+
let int_list l = String.concat "," @@ List.map string_of_int l in
+
let recur_to_ics = function
+
| `Byminute byminlist -> write_rulepart "BYMINUTE" (int_list byminlist)
+
let wday (weeknumber, weekday) =
+
(if weeknumber = 0 then "" else string_of_int weeknumber)
+
^ List.assoc weekday weekday_strings
+
write_rulepart "BYDAY" (String.concat "," @@ List.map wday bywdaylist)
+
| `Byhour byhrlist -> write_rulepart "BYHOUR" (int_list byhrlist)
+
| `Bymonth bymolist -> write_rulepart "BYMONTH" (int_list bymolist)
+
| `Bymonthday bymodaylist ->
+
write_rulepart "BYMONTHDAY" (int_list bymodaylist)
+
| `Bysecond byseclist -> write_rulepart "BYSECOND" (int_list byseclist)
+
| `Bysetposday bysplist -> write_rulepart "BYSETPOS" (int_list bysplist)
+
| `Byweek bywknolist -> write_rulepart "BYWEEKNO" (int_list bywknolist)
+
| `Byyearday byyrdaylist ->
+
write_rulepart "BYYEARDAY" (int_list byyrdaylist)
+
write_rulepart "WKST" (List.assoc weekday weekday_strings)
+
write_rulepart "FREQ" (List.assoc freq freq_strings);
+
(match count_or_until with
+
Buffer.add_char buf ';';
+
| `Count c -> write_rulepart "COUNT" (string_of_int c)
+
Buffer.add_string buf "UNTIL=";
+
timestamp_to_ics enddate buf));
+
Buffer.add_char buf ';';
+
write_rulepart "INTERVAL" (string_of_int i));
+
Buffer.add_char buf ';';
+
let format_event ?(format = `Text) ?tz event =
+
let start = get_start event in
+
let end_ = get_end event in
+
let id = get_id event in
+
let start_date = " " ^ format_date ?tz start in
+
match is_date event with
+
| false -> " " ^ format_time ?tz start
+
let end_date, end_time =
+
match (is_date event, next_day start ~next:end_) with
+
| true, true -> ("", "")
+
| true, _ -> (" - " ^ format_date ?tz end_, "")
+
| false, true -> ("", " - " ^ format_time ?tz end_)
+
(" - " ^ format_date ?tz end_, " " ^ format_time ?tz end_))
+
match get_summary event with
+
| Some summary when summary <> "" -> " " ^ summary
+
match get_location event with
+
| Some loc when loc <> "" -> " @" ^ loc
+
Printf.sprintf "%-45s%s%s%s%s%s%s" id start_date start_time end_date
+
end_time summary location
+
let format_opt label f opt =
+
Option.map (fun x -> Printf.sprintf "%s: %s\n" label (f x)) opt
+
|> Option.value ~default:""
+
let format timezone datetime =
+
match is_date event with
+
| true -> format_date ?tz datetime
+
format_datetime ?tz datetime
+
^ match timezone with None -> "" | Some t -> " (" ^ t ^ ")")
+
format_opt "Start" (format (get_start_timezone event)) (Some start)
+
let end_str = format_opt "End" (format (get_end_timezone event)) end_ in
+
let location_str = format_opt "Location" Fun.id (get_location event) in
+
format_opt "Description" Fun.id (get_description event)
+
let buf = Buffer.create 128 in
+
Printf.sprintf "%s: %s\n" "Reccurence" (Buffer.contents buf))
+
|> Option.value ~default:""
+
let summary_str = format_opt "Summary" Fun.id (get_summary event) in
+
let file_str = format_opt "File" Fun.id (Some (snd (get_file event))) in
+
Printf.sprintf "%s%s%s%s%s%s%s" summary_str start_str end_str location_str
+
description_str rrule_str file_str
+
let open Yojson.Safe in
+
("id", `String (get_id event));
+
match get_summary event with
+
| Some summary -> `String summary
+
("start", `String (format_datetime ?tz start));
+
| Some e -> `String (format_datetime ?tz e)
+
match get_location event with
+
| Some loc -> `String loc
+
match get_description event with
+
| Some desc -> `String desc
+
match get_collection event with
+
| Collection.Col cal -> `String cal );
+
match get_summary event with Some summary -> summary | None -> ""
+
let start = format_datetime ?tz start in
+
match end_ with Some e -> format_datetime ?tz e | None -> ""
+
match get_location event with Some loc -> loc | None -> ""
+
match get_collection event with Collection.Col cal -> cal
+
Printf.sprintf "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"" summary start end_str
+
let calendar = to_ical_calendar event in
+
Icalendar.to_ics ~cr:true calendar
+
match get_summary event with Some summary -> summary | None -> ""
+
let start_date, start_time =
+
let dt = Date.ptime_to_timedesc ?tz start in
+
let y = Timedesc.year dt in
+
let m = Timedesc.month dt in
+
let d = Timedesc.day dt in
+
let h = Timedesc.hour dt in
+
let min = Timedesc.minute dt in
+
let s = Timedesc.second dt in
+
match Timedesc.weekday dt with
+
( Printf.sprintf "(%04d %02d %02d %s)" y m d dow,
+
Printf.sprintf "(%02d %02d %02d)" h min s )
+
let dt = Date.ptime_to_timedesc ?tz end_date in
+
let y = Timedesc.year dt in
+
let m = Timedesc.month dt in
+
let d = Timedesc.day dt in
+
let h = Timedesc.hour dt in
+
let min = Timedesc.minute dt in
+
let s = Timedesc.second dt in
+
match Timedesc.weekday dt with
+
Printf.sprintf "((%04d %02d %02d %s) (%02d %02d %02d))" y m d dow h
+
match get_location event with
+
| Some loc -> Printf.sprintf "\"%s\"" (String.escaped loc)
+
match get_description event with
+
| Some desc -> Printf.sprintf "\"%s\"" (String.escaped desc)
+
match get_collection event with
+
| Collection.Col cal -> Printf.sprintf "\"%s\"" (String.escaped cal)
+
let id = get_id event in
+
"((:id \"%s\" :summary \"%s\" :start (%s %s) :end %s :location %s \
+
:description %s :calendar %s))"
+
(String.escaped id) (String.escaped summary) start_date start_time
+
end_str location description calendar
+
let format_events ?(format = `Text) ?tz events =
+
(fun e -> Yojson.Safe.from_string (format_event ~format:`Json ?tz e))
+
Yojson.Safe.to_string (`List json_events)
+
"\"Summary\",\"Start\",\"End\",\"Location\",\"Calendar\"\n"
+
^ String.concat "\n" (List.map (format_event ~format:`Csv ?tz) events)
+
(List.map (fun e -> format_event ~format:`Sexp ?tz e) events)
+
String.concat "\n" (List.map (fun e -> format_event ~format ?tz e) events)
let expand_recurrences ~from ~to_ event =
let rule = get_recurrence event in