]> pere.pagekite.me Git - homepage.git/blob - linux/plan2icalendar
Nytt bokforslag.
[homepage.git] / linux / plan2icalendar
1 #!/usr/bin/perl -w
2 #
3 # Author: Petter Reinholdtsen <pere@hungry.com>
4 # Date: 2002-01-10
5 #
6 # Convert plan calender data to/from icalender format.
7 #
8 # plan, <URL:http://www.bitrot.de/>
9 # icalendar, RFC 2445, <URL:http://www.faqs.org/rfcs/rfc2445.html">iCalendar>
10 #
11 # Depend on debian package libdate-ical-perl
12
13 use warnings;
14 use strict;
15
16 use Getopt::Std;
17 use Date::Parse;
18 use Date::Format;
19 use Date::ICal;
20 use iCal::Parser;
21 use Data::Dumper;
22
23 my @events;
24 my %opts;
25 unless (getopts('dif:o:', \%opts)) {
26 usage();
27 exit 1;
28 }
29
30 my $debug = $opts{d} || 0;
31 my $input = $opts{f};
32 my $output = $opts{o};
33
34 if ($opts{i}) { # Import
35 read_icalendar($input || "test.ical");
36 write_planfile($output || $ENV{HOME}."/.plan.dir/fromical");
37 } else { # Default is to export
38 read_planfile($input || $ENV{HOME}."/.plan.dir/dayplan");
39 write_icalendar($output || "-");
40 }
41 exit 0;
42
43 sub usage {
44 print <<EOF
45 Usage: $0 [-i] [-f infile] [-o outfile]
46
47 Imports or exports between plan and iCalendar.
48
49 -i import ical file
50 -f infile read input from 'infile'
51 -o outfile read output from 'outfile'
52 -d enable debug output
53 EOF
54 }
55 sub read_planfile {
56 my ($planfile) = @_;
57 open(PLAN, "<$planfile") || die "Unable to read $planfile";
58 my $planver = <PLAN>;
59 chomp $planver;
60 while (<PLAN>) {
61 chomp;
62 if (m%\d+/\d+/\d+\s%) {
63 my ($date, $start, $duration) = split(/\s+/);
64 my $info = <PLAN>;
65 chomp $info;
66 my ($dontknow, $msg) = $info =~ m/^(.)\t(.+)$/;
67 print STDERR "E: $date $start $duration $msg\n" if $debug;
68
69 my $timestamp = str2time("$date $start");
70 my $isostartstamp = time2str("%Y%m%dT%H%M%S", $timestamp);
71 my $icalstartstamp = Date::ICal->new( epoch => $timestamp );
72
73 my @f = split(/:/, $duration);
74 $timestamp += ($f[0]*60*60 + $f[1]*60 +$f[2]);
75 my $isoendstamp = time2str("%Y%m%dT%H%M%S", $timestamp);
76 my $icalendstamp = Date::ICal->new( epoch => $timestamp );
77
78 print STDERR "IE: $isostartstamp $isoendstamp\n" if $debug;
79 push(@events, {
80 summary => $msg,
81 dtstart => $icalstartstamp->ical,
82 dtend => $icalendstamp->ical,
83 });
84 }
85 }
86 close(PLAN);
87 }
88
89
90 # BEGIN:VCALENDAR
91 # PRODID
92 # :-//test environment//NONSGML plan2icalendar//EN
93 # VERSION
94 # :2.0
95 # BEGIN:VEVENT
96 # CREATED
97 # :20020110T153939
98 # UID
99 # :KOrganizer-141229514.660
100 # SEQUENCE
101 # :0
102 # LAST-MODIFIED
103 # :20020110T153939
104 # DTSTAMP
105 # :20020110T153952
106 # SUMMARY
107 # :SL-veiledning
108 # DTSTART
109 # :20020114T151500
110 # DTEND
111 # :20020114T160000
112 # END:VEVENT
113 # END:VCALENDAR
114 sub write_icalendar {
115 my $filename = shift;
116 open(PLAN, ">$filename") || die "Unable to write to $filename";
117 print PLAN <<EOF;
118 BEGIN:VCALENDAR
119 CALSCALE:GREGORIAN
120 PRODID:-//test environment//NONSGML plan2icalender//EN
121 VERSION:2.0
122 EOF
123 my $count = 0;
124 for my $event (@events) {
125 $event->{uid} = "plan2icalendar-$count" unless $event->{uid};
126 $event->{dtstamp} = "20020110T153952" unless $event->{dtstamp};
127 $event->{'last-modified'} = "20020110T153939" unless $event->{'last-modified'};
128 $event->{sequence} = $count unless $event->{sequence};
129 $event->{created} = "20020110T153939" unless $event->{created};
130 print PLAN <<EOF;
131 BEGIN:VEVENT
132 CREATED:$event->{created}
133 UID:$event->{uid}
134 SEQUENCE:$count
135 LAST-MODIFIED:$event->{'last-modified'}
136 DTSTAMP:$event->{dtstamp}
137 SUMMARY:$event->{summary}
138 DTSTART:$event->{dtstart}
139 DTEND:$event->{dtend}
140 END:VEVENT
141
142 EOF
143 $count++;
144 }
145 print PLAN "END:VCALENDAR\n";
146 close PLAN;
147 }
148
149 #
150 # Should probably leave this task to Net::ICal. But it is not
151 # available in debian/sarge, so I make a quick-n-dirty solution
152 # instead.
153 #
154 sub read_icalendar {
155 my $filename = shift;
156
157 if (1) {
158 my $parser = iCal::Parser->new();
159 my $hash = $parser->parse($filename);
160 # print Dumper($hash) if $debug;
161 # print Dumper($hash->{'events'});
162 for my $year (sort keys %{$hash->{'events'}}) {
163 for my $month (sort keys %{$hash->{'events'}->{$year}}) {
164 for my $day (sort keys %{$hash->{'events'}->{$year}->{$month}}) {
165 for my $evid (keys %{$hash->{'events'}->{$year}->{$month}->{$day}}) {
166 my $event =
167 $hash->{'events'}->{$year}->{$month}->{$day}->{$evid};
168 # print "$year-$month-$day $evid\n";
169 # print Dumper($event);
170
171 my %newevent;
172 $newevent{description} = $event->{'DESCRIPTION'};
173 $newevent{created} = $event->{'CREATED'};
174 $newevent{dtend} = $event->{'DTEND'};
175 $newevent{dtstamp} = $event->{'DTSTAMP'};
176 $newevent{dtstart} = $event->{'DTSTART'};
177 $newevent{'last-modified'} = $event->{'LAST-MODIFIED'};
178 $newevent{sequence} = $event->{'SEQUENCE'};
179 $newevent{summary} = $event->{'SUMMARY'};
180 $newevent{uid} = $event->{'UID'};
181 # print Dumper(%newevent);
182 push(@events, \%newevent);
183 print STDERR "Event pushed\n" if $debug;
184 }
185 }
186 }
187 }
188 } else {
189 open (ICALENDAR, "<$filename") or die "Unable to read from $filename";
190 my $oldval = $/;
191 $/ = "\r\n";
192 print STDERR "Loading $filename\n" if $debug;
193 while (<ICALENDAR>) {
194 chomp;
195 if (m/^BEGIN:VEVENT/) {
196 my %event;
197 while (<ICALENDAR>) {
198 chomp;
199 last if (m/END:VEVENT/);
200 $event{description} = $1 if (m/^DESCRIPTION\s*:\s*(.+)$/);
201 $event{created} = $1 if (m/^CREATED\s*:\s*(.+)$/);
202 $event{dtend} = $1 if (m/^DTEND\s*:\s*(.+)$/);
203 $event{dtstamp} = $1 if (m/^DTSTAMP\s*:\s*(.+)$/);
204 $event{dtstart} = $1 if (m/^DTSTART\s*:\s*(.+)$/);
205 $event{last-modified} = $1 if (m/^LAST-MODIFIED\s*:\s*(.+)$/);
206 $event{sequence} = $1 if (m/^SEQUENCE\s*:\s*(.+)$/);
207 $event{summary} = $1 if (m/^SUMMARY\s*:\s*(.+)$/);
208 $event{uid} = $1 if (m/^UID\s*:\s*(.+)$/);
209 }
210 push @events, \%event;
211 print STDERR "Event pushed\n" if $debug;
212 }
213 }
214 close (ICALENDAR);
215 $/ = $oldval;
216 }
217 }
218
219 # Simple version covering the chars I need.
220 sub utf8tolocalmap {
221 my $string = shift;
222 $string =~ s/ø/ø/g;
223 $string =~ s/æ/æ/g;
224 $string =~ s/Ã¥/å/g;
225 $string =~ s/Ã\85/Å/g;
226 $string =~ s/«/«/g;
227 $string =~ s/»/»/g;
228 return $string;
229 }
230
231 sub write_planfile {
232 my $filename = shift;
233 open(PLAN, ">$filename") or die "Unable to write to $filename";
234 for my $event (@events) {
235 # offset = 0 -- assume all timestamps in local time zone
236 my $ical;
237 if ("DateTime" eq ref $event->{dtstart}) {
238 $ical = Date::ICal->new( epoch => $event->{dtstart}->epoch );
239 } else {
240 $ical = Date::ICal->new( ical => $event->{dtstart}, offset => 0 );
241 }
242 my $date = join("/", $ical->month, $ical->day, $ical->year);
243 my $start = join(":", $ical->hour, $ical->min, $ical->sec);
244
245 my $icalstop;
246 if ("DateTime" eq ref $event->{dtend}) {
247 $icalstop = Date::ICal->new( epoch => $event->{dtend}->epoch );
248 } else {
249 $icalstop = Date::ICal->new( ical => $event->{dtend}, offset => 0 );
250 }
251 my $icalduration = $icalstop - $ical;
252 my $duration = join(":",
253 $icalduration->hours || "0",
254 $icalduration->minutes || "0",
255 $icalduration->seconds || "0");
256
257 my $code = "N";
258 my $summary = utf8tolocalmap($event->{summary});
259 print PLAN "$date $start $duration 0:0:0 0:0:0 ---------- 0 0\n";
260 print PLAN "$code\t$summary\n";
261 }
262 }