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

Submitted by nigel on Sunday 6th December 2009

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
blog terms
Perl