Splitting an .ics file up #debian #radicale #calendar #perl

As I mentioned when I upgraded my server to Debian 10 (buster), the version of radicale included no longer supports calendar (.ics) files with more than one event. Instead each event is supposed to be in a file by itself, the file name being the UID of the event.
I have finally taken the time to write a little program that will take one of the .ics-files I used to use, and splits it out to a file per event.
Getting around to that took me, what, ¾ of a year? Oh well, here is is:
#!/usr/bin/perl
# split_ics - take an .ics file and a directory and write a file per event
# Copyright (C) 2020 by Adam Sjøgren <asjo@koldfront.dk> Under the GPLv2.
use strict;
use warnings;
use utf8;
use v5.12;
use Data::ICal;
use File::Slurp;
my $ics_file=$ARGV[0];
my $dir=$ARGV[1];
my $ics_data=File::Slurp::read_file($ics_file, { binmode=>':utf8' });
my $input=Data::ICal->new(data=>$ics_data);
foreach my $event (@{$input->entries}) {
my $uid=$event->property('UID');
my $filename=$dir . '/' . $uid->[0]->value;
my $calendar=Data::ICal->new();
$calendar->add_entry($event);
open my $fh, '>:utf8', $filename || die $!;
print $fh $calendar->as_string;
close $fh;
}
The only tricky part was avoiding the Data::ICal
-module splitting
lines in the middle of multi-octet utf-8 characters.
A tip: you can run radicale --verify-storage
to have any problems with
the generated files reported. Do remember to run it as the radicale
user, though.
One thing that is slightly different when going from regenerating one file to splitting it up, is that any calendar entry that has been deleted is now left as a stale file.
To remedy that, I'm using
find(1)
to remove the stale files - so I have a cron job doing something like this:I.e. I have a script that writes a single calendar file in /tmp/, I use
split_ics
on it, and then I delete all files that are older than the temporary single calendar file, before cleaning that file up as well.- Adam Sjøgren 🕞︎ - 2020-08-04
Thank you for that script. I needed to monkey-patch Data::ICal to ignore the REFRESH-INTERVAL in my input file. Also, my calendar included time zone information, which I wanted to duplicate into each output file, which made my main loop look like this:
- Uli Martens 🕧︎ - 2025-03-27
I've documented the monkey-patch here: https://github.com/bestpractical/data-ical/issues/3#issuecomment-2757546565
- Uli Martens 🕐︎ - 2025-03-27
Cool, thanks for sharing back!
- Adam Sjøgren 🕥︎ - 2025-03-27