Perl Generation of .ics iCalendar File for FIFA World Cup 2010 Schedule

The code to generate the .ics iCalendar file is reproduced below. I can't make any guarantees as to whether it will compile on your rig; it has been tested on my openSUSE 11.2 box running Perl v5.10.0. In addition, you will need to install various cpan packages - these are clearly identified at the top of the listing in the use statements.

It's not my intention to provide line-by-line analysis in this instance due to the length of the code, but if there's anything you are unsure about, feel free to register on the site and leave a comment.

#!/usr/bin/perl

# routine to parse the FIFA World Cup 2010 fixtures list and create iCalendar file
# Copyright www.badzilla.co.uk
#
Licence GPL. This program may be distributed as per the terms of GPL and all credits
# must be retained
#
# If you find this script useful, please consider a donation to help me fund my web presence
# and encourage me to develop more products to be placed under the terms of GPL
# To donate, go to http://www.badzilla.co.uk and click on the donation button
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

use warnings;
use strict;

use Data::ICal;
use Data::ICal::Entry::Event;
use Date::ICal;
use DateTime;
use Date::Calc qw(Add_Delta_DHMS);
use Time::HiRes;

my $splash = "Downloaded from http://www.badzilla.co.uk\n\n";
my $categories = "http://www.badzilla.co.uk;Sport;Football;FIFA World Cup 2010";
my $fixturedate = "";
my $round = "";
my $location = "";
my $team1 = "";
my $team2 = "";
my %events = ();



# main loop to process the individual scraped lines
while (<>) {
        chomp($_);

        # if the line contains a date it is a new fixture
        if (/^(\w+) (\d{1,2}), 2010 @ (\d{2}):(\d{2})/) {
            $fixturedate = sprintf("%02d,%02d,%02d,%02d", $1 eq "June" ? 6 : 7, $2, $3, $4);
           
            # undefine the fixture information
            undef $location;
            undef $round;
            undef $team1;
            undef $team2;
        } else {
            if (!$round) {
                $round = $_;
            } elsif (!$location) {
                s/at //;
                $location = $_;
            } elsif (!$team1) {
                $team1 = $_;
            } elsif (!$team2) {
                $team2 = $_;
               
                # now we have all the fixture information, create hash
                my $key = sprintf("%s,%s,%s", $fixturedate, $round, $location);
                # and save into a hash
                $events{$key} = $team1 . " v " . $team2;
             }
        }
}


# create datestamp information
my @stamp = localtime();
my $dstamp = sprintf("%d%02d%02dT%02d%02d%02dZ",
        $stamp[5] + 1900,
        $stamp[4] + 1,
        $stamp[3],
        $stamp[2],
        $stamp[1],
        $stamp[0]);

# get the timezone information.
my $tz = DateTime::TimeZone->new(name => 'Europe/London');

# now loop through each of the hashes which represent a fixture each and create
# the events.
my $calendar = Data::ICal->new();
my $count = 0;
for my $k (keys %events) {

    # get the respective date and time components and fixture information
    my ($startmonth, $startday, $starthour, $startmin, $round, $location) = split(/,/, $k);

    # now calculate the date time start point
    my $startyear = 2010;
    my $dt = DateTime->new(
        year => $startyear,
        month => $startmonth,
        day => $startday,
        hour => $starthour,
        minute => $startmin,
        second => 0, nanosecond => 0, time_zone => 'UTC');
    my $offset = $tz->offset_for_datetime($dt);

    # got the offset for the timezone so now apply to starttime
    my $startsec;
    ($startyear, $startmonth, $startday, $starthour, $startmin, $startsec) =
        Add_Delta_DHMS($startyear, $startmonth, $startday, $starthour,
                        $startmin, 0, 0, 0, 0, -$offset);

    # and add 2 hours for the end of the game
    my ($endyear, $endmonth, $endday, $endhour, $endmin, $endsec)
        = Add_Delta_DHMS(2010, $startmonth, $startday, $starthour, $startmin, 0, 0, 2, 0, 0);

    # create the event
    my $event = Data::ICal::Entry::Event->new();

    # set the UID
    my @tm = localtime();
    my $uid = sprintf("%d%02d%02d%02d%02d%02d%s%02d\@badzilla.co.uk",
                    $tm[5] + 1900, $tm[4] + 1, $tm[3], $tm[2],
                    $tm[1], $tm[0], scalar(Time::HiRes::gettimeofday()), $count);

    $event->add_properties(
            uid => $uid,
            summary => "FIFA World Cup 2010: $round: $events{$k}",
            description => $splash . "South Africa FIFA World Cup 2010\n\n$round\n$events{$k}" ,
            categories => $categories,
            location => $location,
            dtstamp => $dstamp,
            dtstart => Date::ICal->new(
                            year => $startyear,
                            month => $startmonth,
                            day => $startday,
                            hour => $starthour,
                            min => $startmin,
                            sec => 0,
                            offset => "+0000")->ical,
            dtend => Date::ICal->new(
                            year => $endyear,
                            month => $endmonth,
                            day => $endday,
                            hour => $endhour,
                            min => $endmin,
                            sec => 0,
                            offset => "+0000")->ical,
            );

    $calendar->add_entry($event);
    $count++;
}

$calendar->add_properties(
        calscale => 'GREGORIAN',
        method => 'PUBLISH',
        'X-WR-CALNAME' => 'FIFA World Cup 2010'
        );

print $calendar->as_string;

To execute this script, we are going to use the screenscraper shell script from the previous exercise - at the end we pipe the output of the screenscraped fixtures into this script (called wc2010) and then redirect the output to a file called WorldCup2010.ics which is of course our iCalendar file.
#!/bin/sh

# URL to screenscrape
wc2010http='http://soccernet.espn.go.com/world-cup/fixtures?cc=5739&ver=global'

# Screenscrape
wget -q -O - $wc2010http | sed -n '/June 11, 2010 @ 15:00UK/,/Table/p' | sed 's/<[^>]*>//g;s/^[ \t]*//;/^$/d' | awk '$0 !~ /Table/ && $0 !~ /Home/ && $0 !~ /nbsp/ {print}' | ./wc2010 > WorldCup2010.ics