# Author: Petter Reinholdtsen <pere@hungry.com>
# Date: 2002-01-10
#
-# Convert plan calender data to icalender format.
+# Convert plan calender data to/from icalender format.
#
# plan, <URL:http://www.bitrot.de/>
# icalendar, RFC 2445, <URL:http://www.faqs.org/rfcs/rfc2445.html">iCalendar>
+use warnings;
+use strict;
+
+use Getopt::Std;
use Date::Parse;
use Date::Format;
+use Date::ICal;
my @events;
+my %opts;
+my $debug = 0;
+unless (getopts('if:o:', \%opts)) {
+ usage();
+ exit 1;
+}
-my $mailto = "pere\@hungry.com";
+my $input = $opts{f};
+my $output = $opts{o};
-read_planfile(".plan.dir/dayplan");
-write_icalendar();
+if ($opts{i}) { # Import
+ read_icalendar($input || "test.ical");
+ write_planfile($output || $ENV{HOME}."/.plan.dir/fromical");
+} else { # Default is to export
+ read_planfile($input || $ENV{HOME}."/.plan.dir/dayplan");
+ write_icalendar($output || "-");
+}
+exit 0;
+sub usage {
+ print <<EOF
+EOF
+}
sub read_planfile {
my ($planfile) = @_;
open(PLAN, "<$planfile") || die "Unable to read $planfile";
my $info = <PLAN>;
chomp $info;
my ($dontknow, $msg) = $info =~ m/^(.)\t(.+)$/;
- print STDERR "E: $date $start $duration $msg\n";
+ print STDERR "E: $date $start $duration $msg\n" if $debug;
my $timestamp = str2time("$date $start");
- $isostartstamp = time2str("%Y%m%dT%H%M%S", $timestamp);
+ my $isostartstamp = time2str("%Y%m%dT%H%M%S", $timestamp);
+ my $icalstartstamp = Date::ICal->new( epoch => $timestamp );
my @f = split(/:/, $duration);
$timestamp += ($f[0]*60*60 + $f[1]*60 +$f[2]);
- $isoendstamp = time2str("%Y%m%dT%H%M%S", $timestamp);
+ my $isoendstamp = time2str("%Y%m%dT%H%M%S", $timestamp);
+ my $icalendstamp = Date::ICal->new( epoch => $timestamp );
- print STDERR "IE: $isostartstamp $isoendstamp\n";
+ print STDERR "IE: $isostartstamp $isoendstamp\n" if $debug;
push(@events, {
- organizer => "$mailto",
summary => $msg,
- dtstart => $isostartstamp,
- dtend => $isoendstamp,
- priority => '1'
+ dtstart => $icalstartstamp->ical,
+ dtend => $icalendstamp->ical,
});
}
}
# BEGIN:VCALENDAR
# PRODID
-# :-//K Desktop Environment//NONSGML KOrganizer//EN
+# :-//test environment//NONSGML plan2icalendar//EN
# VERSION
# :2.0
# BEGIN:VEVENT
# :20020110T153939
# DTSTAMP
# :20020110T153952
-# ORGANIZER
-# :MAILTO:pre@diskless.uio.no
# SUMMARY
# :SL-veiledning
-# PRIORITY
-# :1
# DTSTART
# :20020114T151500
# DTEND
# END:VEVENT
# END:VCALENDAR
sub write_icalendar {
- print <<EOF;
+ my $filename = shift;
+ open(PLAN, ">$filename") || die "Unable to write to $filename";
+ print PLAN <<EOF;
BEGIN:VCALENDAR
-PRODID
- :-//K Desktop Environment//NONSGML KOrganizer//EN
-VERSION
- :2.0
+CALSCALE:GREGORIAN
+PRODID:-//test environment//NONSGML plan2icalender//EN
+VERSION:2.0
EOF
my $count = 0;
- my $event;
- for $event (@events) {
- print <<EOF;
+ for my $event (@events) {
+ $event->{uid} = "plan2icalendar-$count" unless $event->{uid};
+ $event->{dtstamp} = "20020110T153952" unless $event->{dtstamp};
+ $event->{'last-modified'} = "20020110T153939" unless $event->{'last-modified'};
+ $event->{sequence} = $count unless $event->{sequence};
+ $event->{created} = "20020110T153939" unless $event->{created};
+ print PLAN <<EOF;
BEGIN:VEVENT
-CREATED
- :20020110T153939
-UID
- :plan2icalendar-$count
-SEQUENCE
- :$count
-LAST-MODIFIED
- :20020110T153939
-DTSTAMP
- :20020110T153952
-ORGANIZER
- :MAILTO:$event->{organizer}
-SUMMARY
- :$event->{summary}
-PRIORITY
- :$event->{priority}
-DTSTART
- :$event->{dtstart}
-DTEND
- :$event->{dtend}
+CREATED:$event->{created}
+UID:$event->{uid}
+SEQUENCE:$count
+LAST-MODIFIED:$event->{'last-modified'}
+DTSTAMP:$event->{dtstamp}
+SUMMARY:$event->{summary}
+DTSTART:$event->{dtstart}
+DTEND:$event->{dtend}
END:VEVENT
+
EOF
$count++;
}
- print "END:VCALENDAR\n";
+ print PLAN "END:VCALENDAR\n";
+ close PLAN;
+}
+
+#
+# Should probably leave this task to Net::ICal. But it is not
+# available in debian/sarge, so I make a quick-n-dirty solution
+# instead.
+#
+sub read_icalendar {
+ my $filename = shift;
+ open (ICALENDAR, "<$filename") or die "Unable to read from $filename";
+ my $oldval = $/;
+ $/ = "\r\n";
+ while (<ICALENDAR>) {
+ chomp;
+ if (m/^BEGIN:VEVENT/) {
+ my %event;
+ while (<ICALENDAR>) {
+ chomp;
+ last if (m/END:VEVENT/);
+ $event{description} = $1 if (m/^DESCRIPTION\s*:\s*(.+)$/);
+ $event{created} = $1 if (m/^CREATED\s*:\s*(.+)$/);
+ $event{dtend} = $1 if (m/^DTEND\s*:\s*(.+)$/);
+ $event{dtstamp} = $1 if (m/^DTSTAMP\s*:\s*(.+)$/);
+ $event{dtstart} = $1 if (m/^DTSTART\s*:\s*(.+)$/);
+ $event{last-modified} = $1 if (m/^LAST-MODIFIED\s*:\s*(.+)$/);
+ $event{sequence} = $1 if (m/^SEQUENCE\s*:\s*(.+)$/);
+ $event{summary} = $1 if (m/^SUMMARY\s*:\s*(.+)$/);
+ $event{uid} = $1 if (m/^UID\s*:\s*(.+)$/);
+ }
+ push @events, \%event;
+ }
+ }
+ close (ICALENDAR);
+ $/ = $oldval;
+}
+
+# Simple version covering the chars I need.
+sub utf8tolocalmap {
+ my $string = shift;
+ $string =~ s/ø/ø/g;
+ $string =~ s/æ/æ/g;
+ $string =~ s/Ã¥/å/g;
+ return $string;
+}
+
+sub write_planfile {
+ my $filename = shift;
+ open(PLAN, ">$filename") or die "Unable to write to $filename";
+ for my $event (@events) {
+ # offset = 0 -- assume all timestamps in local time zone
+ my $ical = Date::ICal->new( ical => $event->{dtstart}, offset => 0 );
+ my $date = join("/", $ical->month, $ical->day, $ical->year);
+ my $start = join(":", $ical->hour, $ical->min, $ical->sec);
+
+ my $icalstop = Date::ICal->new( ical => $event->{dtend}, offset => 0 );
+ my $icalduration = $icalstop - $ical;
+ my $duration = join(":",
+ $icalduration->hours || "0",
+ $icalduration->minutes || "0",
+ $icalduration->seconds || "0");
+
+ my $code = "N";
+ my $summary = utf8tolocalmap($event->{summary});
+ print PLAN "$date $start $duration 0:0:0 0:0:0 ---------- 0 0\n";
+ print PLAN "$code\t$summary\n";
+ }
}