make_meeting


#!/usr/bin/perl 
#===============================================================================
#
#         FILE: make_meeting
#
#        USAGE: ./make_meeting
#
#  DESCRIPTION: Makes a recurrent iCalendar meeting to be loaded into
#               a calendar. This is apparently necessary when the 'RRULE'
#               recurrence description is not adequate.
#
#      OPTIONS: None
# REQUIREMENTS: Needs modules Data::ICal and Date::Calc
#         BUGS: ---
#        NOTES: Distributed with the HPR episode "iCalendar Hacking"
#       AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com
#      LICENCE: Copyright (c) year 2012, Dave Morriss
#      VERSION: 1.0
#      CREATED: 13/10/2012 15:34:01
#     REVISION: 16/11/2012 16:04:37
#
#===============================================================================
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# 
# 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.  See the GNU General Public License for
# more details.
# 
# You should have received a copy of the GNU General Public License along with
# this program.  If not, see <http://www.gnu.org/licenses/>.
#===============================================================================

use 5.010;
use strict;
use warnings;

use Data::ICal;
use Data::ICal::Entry::Event;

use Date::Calc qw{
    Today Day_of_Year Add_Delta_YMD Nth_Weekday_of_Month_Year
};

#
# Date and time values
#
my @today = Today();
my @startdate;
my @rdate;
my $monday = 1;    # Day of week number 1-7, Monday-Sunday

my @starttime = ( 19, 00, 00 );
my @endtime   = ( 21, 00, 00 );

#
# Format of an ISO UTC datetime
#
my $fmt = "%02d%02d%02dT%02d%02d%02dZ";

#
# Constants for the event
#
my $calname     = 'Hacker Public Radio';
my $timezone    = 'Europe/London';
my $location    = 'mumble.openspeak.cc port: 64747';
my $summary     = 'HPR Community News';
my $description = <<ENDDESC;
This is a test, building an iCalendar file and loading it into Thunderbird.
-----------------------------------------
Mumble settings
Server Name: Anything you like
Server Address: mumble.openspeak.cc 
Port: 64747
Name: Your name or alias is fine

Don't have mumble, setup instructions can be found on our wiki -
http://linuxbasix.com/tiki-index.php?page=Linux+Basix+Mumble
ENDDESC

#
# Compute the next meeting date from now
#
@startdate = make_date( \@today, $monday, 1, -2 );

#
# Create the calendar object
#
my $calendar = Data::ICal->new();

#
# Some calendar properties
#
$calendar->add_properties(
    'X-WR-CALNAME'  => $calname,
    'X-WR-TIMEZONE' => $timezone,
);

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

#
# Add some event properties
#
$vevent->add_properties(
    summary     => $summary,
    location    => $location,
    description => $description,
    dtstart     => sprintf( $fmt, @startdate, @starttime ),
    dtend       => sprintf( $fmt, @startdate, @endtime ),
);

#
# Add 12 recurring dates. (Note that this generates 12 RDATE entries rather
# than 1 entry with multiple dates; this is because this module doesn't seem
# to have the ability to generated the concatenated entry. The two modes of
# expressing the repeated dates seem to be equivalent.)
#
for my $i ( 1 .. 12 ) {
    @today = Add_Delta_YMD( @today, 0, 1, 0 );
    @rdate = make_date( \@today, $monday, 1, -2 );
    $vevent->add_property( rdate =>
            [ sprintf( $fmt, @rdate, @starttime ), { value => 'DATE-TIME' } ],
    );
}

#
# Add the event into the calendar
#
$calendar->add_entry($vevent);

#
# Print the result
#
print $calendar->as_string;

exit;

#===  FUNCTION  ================================================================
#         NAME: make_date
#      PURPOSE: Make the event date for recurrence
#   PARAMETERS: $refdate
#                       An arrayref to the reference date array (usually
#                       today's date)
#               $dow    Day of week for the event date (1-7, 1=Monday)
#               $n      The nth day of the week in the given month required
#                       for the event date
#               $offset Number of days to offset the computed date
#      RETURNS: The resulting date as a list for Date::Calc
#  DESCRIPTION: We want to compute a simple date with an offset, such as
#               "the Saturday before the first Monday of the month". We do
#               this my computing a pre-offset date (first Monday of month)
#               then apply the offset (Saturday before).
#       THROWS: No exceptions
#     COMMENTS: TODO Needs more testing to be considered truly universal
#     SEE ALSO:
#===============================================================================
sub make_date {
    my ( $refdate, $dow, $n, $offset ) = @_;

    #
    # Compute the required date: the nth day of week in this year and month
    #
    my @date = Nth_Weekday_of_Month_Year( @$refdate[ 0, 1 ], $dow, $n );

    #
    # If the computed date is before the base date advance a month
    #
    if ( Day_of_Year(@date) <= Day_of_Year(@$refdate) ) {
        #
        # Add a month and recompute
        #
        @date = Add_Delta_YMD( @date, 0, 1, 0 );
        @date = Nth_Weekday_of_Month_Year( @date[ 0, 1 ], $dow, $n );
    }

    #
    # Apply the day offset
    #
    @date = Add_Delta_YMD( @date, 0, 0, $offset );

    #
    # Return a list
    #
    return (@date);
}

# vim: syntax=perl:ts=8:sw=4:et:ai:tw=78:fo=tcrqn21:fdm=marker