forked from HPR/hpr-tools
		
	
		
			
	
	
		
			485 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			485 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| 
								 | 
							
								#!/usr/bin/env 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 Getopt::Long, Data::ICal, Date::Parse and
							 | 
						||
| 
								 | 
							
								#               Date::Calc
							 | 
						||
| 
								 | 
							
								#         BUGS: ---
							 | 
						||
| 
								 | 
							
								#        NOTES: Based on a script distributed with the HPR episode "iCalendar
							 | 
						||
| 
								 | 
							
								#               Hacking"
							 | 
						||
| 
								 | 
							
								#       AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com
							 | 
						||
| 
								 | 
							
								#      LICENCE: Copyright (c) year 2012-2024 Dave Morriss
							 | 
						||
| 
								 | 
							
								#      VERSION: 0.2.2
							 | 
						||
| 
								 | 
							
								#      CREATED: 2012-10-13 15:34:01
							 | 
						||
| 
								 | 
							
								#     REVISION: 2024-05-24 22:45:56
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								#===============================================================================
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use 5.010;
							 | 
						||
| 
								 | 
							
								use strict;
							 | 
						||
| 
								 | 
							
								use warnings;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use Getopt::Long;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use Data::ICal;
							 | 
						||
| 
								 | 
							
								use Data::ICal::Entry::Event;
							 | 
						||
| 
								 | 
							
								use Data::ICal::Entry::Todo;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								use Date::Parse;
							 | 
						||
| 
								 | 
							
								use Date::Calc qw{:all};
							 | 
						||
| 
								 | 
							
								use Date::ICal;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#use Data::Dumper;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Version number (manually incremented)
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								our $VERSION = '0.2.2';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Script name
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								( my $PROG = $0 ) =~ s|.*/||mx;
							 | 
						||
| 
								 | 
							
								( my $DIR  = $0 ) =~ s|/?[^/]*$||mx;
							 | 
						||
| 
								 | 
							
								$DIR = '.' unless $DIR;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Enable Unicode mode
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								binmode STDOUT, ":encoding(UTF-8)";
							 | 
						||
| 
								 | 
							
								binmode STDERR, ":encoding(UTF-8)";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#-------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								# Declarations
							 | 
						||
| 
								 | 
							
								#-------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								my ( @startdate, @rdate, @events );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Attributes for the calendar message
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								#my $server = 'ch1.teamspeak.cc';
							 | 
						||
| 
								 | 
							
								#my $port   = 64747;
							 | 
						||
| 
								 | 
							
								my $server = 'chatter.skyehaven.net';
							 | 
						||
| 
								 | 
							
								my $port   = 64738;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#-------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								# Options and arguments
							 | 
						||
| 
								 | 
							
								#-------------------------------------------------------------------------------
							 | 
						||
| 
								 | 
							
								my $DEF_COUNT   = 12;
							 | 
						||
| 
								 | 
							
								#my $DEF_SUMMARY = 'Send out CNews email';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Process options
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								my %options;
							 | 
						||
| 
								 | 
							
								Options( \%options );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Default help
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								usage() if ( $options{'help'} );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Collect options
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								my $count = ( defined( $options{count} ) ? $options{count} : $DEF_COUNT );
							 | 
						||
| 
								 | 
							
								my $reminder = ( defined( $options{reminder} ) ? $options{reminder} : 0 );
							 | 
						||
| 
								 | 
							
								my $force    = ( defined( $options{force} )    ? $options{force}    : 0 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#my $reminder_summary = ( defined( $options{summary} ) ? $options{summary} :
							 | 
						||
| 
								 | 
							
								#    $DEF_SUMMARY );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Two reminders: 8 days ahead reminder to check with Ken, 5 days ahead
							 | 
						||
| 
								 | 
							
								# reminder to send out the email.
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								my %reminders = (
							 | 
						||
| 
								 | 
							
								    email => [ -5, 'Send out CNews email' ],
							 | 
						||
| 
								 | 
							
								    check => [ -8, 'Check CNews date with Ken' ],
							 | 
						||
| 
								 | 
							
								);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Use the date provided or the default
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								if ( defined( $options{from} ) ) {
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Parse the date, convert to start of month and (optionally) validate it
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    @startdate = convert_date( $options{from}, $force );
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								else {
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Use the current date
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    @startdate = Today();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Date and time values
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# TODO: These should be in a configuration file, and should ideally be capable
							 | 
						||
| 
								 | 
							
								# of having a time zone defined (default UTC, as now).
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								my $monday = 1;    # Day of week number 1-7, Monday-Sunday
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								my @starttime = ( 13, 00, 00 );    # UTC
							 | 
						||
| 
								 | 
							
								my @endtime   = ( 15, 00, 00 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								my @todostart = ( 9,  00, 00 );    # UTC
							 | 
						||
| 
								 | 
							
								my @todoend   = ( 17, 00, 00 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Format of an ISO UTC datetime
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								my $fmt = "%02d%02d%02dT%02d%02d%02dZ";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Constants for the event
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								my $calname     = 'HPR Community News';
							 | 
						||
| 
								 | 
							
								my $timezone    = 'UTC';
							 | 
						||
| 
								 | 
							
								my $location    = "$server port: $port";
							 | 
						||
| 
								 | 
							
								my $summary     = 'HPR Community News Recording Dates';
							 | 
						||
| 
								 | 
							
								my $description = <<ENDDESC;
							 | 
						||
| 
								 | 
							
								Mumble settings
							 | 
						||
| 
								 | 
							
								-------------------
							 | 
						||
| 
								 | 
							
								Server Name:    Anything you like
							 | 
						||
| 
								 | 
							
								Server Address: $server
							 | 
						||
| 
								 | 
							
								Port:           $port
							 | 
						||
| 
								 | 
							
								Name:           Your name or alias is fine
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Information about Mumble can be found here:
							 | 
						||
| 
								 | 
							
								http://hackerpublicradio.org/recording.php
							 | 
						||
| 
								 | 
							
								ENDDESC
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Compute the next recording date from the starting date (@startdate will be
							 | 
						||
| 
								 | 
							
								# today's date or the start of the explicitly selected month provided via
							 | 
						||
| 
								 | 
							
								# -from=DATE. We want day of the week to be Monday, the first in the month,
							 | 
						||
| 
								 | 
							
								# then to go back 1 day from that to get to the Sunday! Simple)
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								@startdate = make_date( \@startdate, $monday, 1, -1 );
							 | 
						||
| 
								 | 
							
								@rdate = @startdate;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# 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 recurring dates. (Note that this generates RDATE entries rather than
							 | 
						||
| 
								 | 
							
								# 1 entry with multiple dates; this is because this module doesn't seem to
							 | 
						||
| 
								 | 
							
								# have the ability to generate the concatenated entry. The two modes of
							 | 
						||
| 
								 | 
							
								# expressing the repeated dates seem to be equivalent.)
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								for my $i ( 1 .. $count ) {
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Recording date computation from the start of the month
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    @rdate = make_date( \@rdate, $monday, 1, -1 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Save the current recording date to make an array of arrayrefs
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    push( @events, [@rdate] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Add this date to the multi-date event
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    $vevent->add_property( rdate =>
							 | 
						||
| 
								 | 
							
								            [ sprintf( $fmt, @rdate, @starttime ), { value => 'DATE-TIME' } ],
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Increment the meeting date for the next one. If we're early in the month
							 | 
						||
| 
								 | 
							
								    # by one day otherwise to the beginning of the next month. This is
							 | 
						||
| 
								 | 
							
								    # necessary because otherwise make_date will skip months.
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    if ( $rdate[2] < 7 ) {
							 | 
						||
| 
								 | 
							
								        @rdate = Add_Delta_Days( @rdate, 1 );
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else {
							 | 
						||
| 
								 | 
							
								        @rdate = ( ( Add_Delta_YM( @rdate, 0, 1 ) )[ 0 .. 1 ], 1 );
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Add the event into the calendar
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								$calendar->add_entry($vevent);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Are we to add reminders?
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								if ($reminder) {
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Loop through the cache of recording dates
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    for my $i ( 0 .. $count - 1 ) {
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # Loop through the reminders hash
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        for my $key (keys(%reminders)) {
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            # A new Todo entry each iteration
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            my $vtodo = Data::ICal::Entry::Todo->new();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            # Get a recording date from the cache and subtract 5 days from it to
							 | 
						||
| 
								 | 
							
								            # get the preceding Monday
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            @rdate = @{ $events[$i] };
							 | 
						||
| 
								 | 
							
								            @rdate = Add_Delta_Days( @rdate, $reminders{$key}->[0] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            # Add the date as the date part of the Todo
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            $vtodo->add_properties(
							 | 
						||
| 
								 | 
							
								                summary => $reminders{$key}->[1],
							 | 
						||
| 
								 | 
							
								                status  => 'INCOMPLETE',
							 | 
						||
| 
								 | 
							
								                dtstart => Date::ICal->new(
							 | 
						||
| 
								 | 
							
								                    ical => sprintf( $fmt, @rdate, @todostart )
							 | 
						||
| 
								 | 
							
								                )->ical,
							 | 
						||
| 
								 | 
							
								                due => Date::ICal->new(
							 | 
						||
| 
								 | 
							
								                    ical => sprintf( $fmt, @rdate, @todoend )
							 | 
						||
| 
								 | 
							
								                )->ical,
							 | 
						||
| 
								 | 
							
								            );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            # Add to the calendar
							 | 
						||
| 
								 | 
							
								            #
							 | 
						||
| 
								 | 
							
								            $calendar->add_entry($vtodo);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# Print the result
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								print $calendar->as_string;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								exit;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#===  FUNCTION  ================================================================
							 | 
						||
| 
								 | 
							
								#         NAME: convert_date
							 | 
						||
| 
								 | 
							
								#      PURPOSE: Convert a textual date (ideally YYYY-MM-DD) to a Date::Calc
							 | 
						||
| 
								 | 
							
								#               date for the start of the given month.
							 | 
						||
| 
								 | 
							
								#   PARAMETERS: $textdate       date in text form
							 | 
						||
| 
								 | 
							
								#               $force          Boolean defining whether to skip validating
							 | 
						||
| 
								 | 
							
								#                               the date
							 | 
						||
| 
								 | 
							
								#      RETURNS: The start of the month in the textual date in Date::Calc
							 | 
						||
| 
								 | 
							
								#               format
							 | 
						||
| 
								 | 
							
								#  DESCRIPTION: Parses the date string and makes a Date::Calc date from the
							 | 
						||
| 
								 | 
							
								#               result where the day part is 1. Optionally checks that the
							 | 
						||
| 
								 | 
							
								#               date isn't in the past, though $force = 1 ignores this check.
							 | 
						||
| 
								 | 
							
								#       THROWS: No exceptions
							 | 
						||
| 
								 | 
							
								#     COMMENTS: Requires Date::Calc and Date::Parse
							 | 
						||
| 
								 | 
							
								#               Note the validation 'die' has a non-generic message
							 | 
						||
| 
								 | 
							
								#     SEE ALSO: N/A
							 | 
						||
| 
								 | 
							
								#===============================================================================
							 | 
						||
| 
								 | 
							
								sub convert_date {
							 | 
						||
| 
								 | 
							
								    my ( $textdate, $force ) = @_;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    my ( @today, @parsed, @startdate );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Reference date
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    @today = Today();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Parse and perform rudimentary validation on the $textdate date. Function
							 | 
						||
| 
								 | 
							
								    # 'strptime' returns "($ss,$mm,$hh,$day,$month,$year,$zone,$century)".
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # The Date::Calc date $startdate[0] gets the returned year or the current
							 | 
						||
| 
								 | 
							
								    # year if no year was parsed, $startdate[1] gets the parsed month or the
							 | 
						||
| 
								 | 
							
								    # current month if no month was parsed, and $startdate[2] gets a day of 1.
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    @parsed    = strptime($textdate);
							 | 
						||
| 
								 | 
							
								    die "Unable to parse date '$textdate'\n" unless @parsed;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @startdate = (
							 | 
						||
| 
								 | 
							
								        ( defined( $parsed[5] ) ? $parsed[5] + 1900 : $today[0] ),    # year
							 | 
						||
| 
								 | 
							
								        ( defined( $parsed[4] ) ? $parsed[4] + 1 : $today[1] ), 1
							 | 
						||
| 
								 | 
							
								    );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Unless we've overridden the check there should be a positive or zero
							 | 
						||
| 
								 | 
							
								    # difference in days between the target date and today's date to prevent
							 | 
						||
| 
								 | 
							
								    # going backwards in time.
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    unless ($force) {
							 | 
						||
| 
								 | 
							
								        unless ( Delta_Days( @today[ 0, 1 ], 1, @startdate ) ge 0 ) {
							 | 
						||
| 
								 | 
							
								            warn "Invalid date $textdate (in the past)\n";
							 | 
						||
| 
								 | 
							
								            die "Use -force to create a back-dated calendar\n";
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return @startdate;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#===  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 ($dow) in the given
							 | 
						||
| 
								 | 
							
								#                               month required for the event date ($dow=1,
							 | 
						||
| 
								 | 
							
								#                               $n=1 means first Monday)
							 | 
						||
| 
								 | 
							
								#               $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 Sunday before the first Monday of the month". We do
							 | 
						||
| 
								 | 
							
								#               this by computing a pre-offset date (first Monday of month)
							 | 
						||
| 
								 | 
							
								#               then apply the offset (Sunday 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 "$n"th day of week "$dow" in the year and
							 | 
						||
| 
								 | 
							
								    # month in @$refdate. This could be a date in the past.
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    my @date = Nth_Weekday_of_Month_Year( @$refdate[ 0, 1 ], $dow, $n );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # If the computed date plus the offset is before the base date advance
							 | 
						||
| 
								 | 
							
								    # a month
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    if ( Day_of_Year(@date) + $offset < Day_of_Year(@$refdate) ) {
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # Add a month and recompute
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        @date = Add_Delta_YM( @date, 0, 1 );
							 | 
						||
| 
								 | 
							
								        @date = Nth_Weekday_of_Month_Year( @date[ 0, 1 ], $dow, $n );
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Apply the day offset
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    @date = Add_Delta_Days( @date, $offset ) if $offset;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    # Return a list
							 | 
						||
| 
								 | 
							
								    #
							 | 
						||
| 
								 | 
							
								    return (@date);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#===  FUNCTION  ================================================================
							 | 
						||
| 
								 | 
							
								#         NAME: ISO8601_Date
							 | 
						||
| 
								 | 
							
								#      PURPOSE: Format a Date::Calc date in ISO8601 format
							 | 
						||
| 
								 | 
							
								#   PARAMETERS: @date   - a date in the Date::Calc format
							 | 
						||
| 
								 | 
							
								#      RETURNS: Text string containing a YYYY-MM-DD date
							 | 
						||
| 
								 | 
							
								#  DESCRIPTION: Just a convenience to allow a simple call like
							 | 
						||
| 
								 | 
							
								#               $str = ISO8601_Date(@date)
							 | 
						||
| 
								 | 
							
								#       THROWS: No exceptions
							 | 
						||
| 
								 | 
							
								#     COMMENTS: None
							 | 
						||
| 
								 | 
							
								#     SEE ALSO: N/A
							 | 
						||
| 
								 | 
							
								#===============================================================================
							 | 
						||
| 
								 | 
							
								sub ISO8601_Date {
							 | 
						||
| 
								 | 
							
								    my (@date) = (@_)[ 0, 1, 2 ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ( check_date(@date) ) {
							 | 
						||
| 
								 | 
							
								        return sprintf( "%04d-%02d-%02d", @date );
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else {
							 | 
						||
| 
								 | 
							
								        return "*Invalid Date*";
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#===  FUNCTION  ================================================================
							 | 
						||
| 
								 | 
							
								#         NAME: usage
							 | 
						||
| 
								 | 
							
								#      PURPOSE: Display a usage message and exit
							 | 
						||
| 
								 | 
							
								#   PARAMETERS: None
							 | 
						||
| 
								 | 
							
								#      RETURNS: To command line level with exit value 1
							 | 
						||
| 
								 | 
							
								#  DESCRIPTION: Builds the usage message using global values
							 | 
						||
| 
								 | 
							
								#       THROWS: no exceptions
							 | 
						||
| 
								 | 
							
								#     COMMENTS: none
							 | 
						||
| 
								 | 
							
								#     SEE ALSO: n/a
							 | 
						||
| 
								 | 
							
								#===============================================================================
							 | 
						||
| 
								 | 
							
								sub usage {
							 | 
						||
| 
								 | 
							
								    print STDERR <<EOD;
							 | 
						||
| 
								 | 
							
								Usage: $PROG [options] [FILE...]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								$PROG v$VERSION
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Makes a recurrent iCalendar meeting to be loaded into a calendar. Optionally
							 | 
						||
| 
								 | 
							
								adds reminders in the form of TODO items in relation to each meeting.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    -help               Display this information
							 | 
						||
| 
								 | 
							
								    -from=DATE          Start date for the calendar
							 | 
						||
| 
								 | 
							
								    -count=N            Number of entries; default 12
							 | 
						||
| 
								 | 
							
								    -[no]force          Allow a -from=DATE date before today; default not
							 | 
						||
| 
								 | 
							
								    -[no]reminder       Add a reminder TODO item; default no
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								EOD
							 | 
						||
| 
								 | 
							
								#    -summary=TEXT       Alternative text for the reminder (default 'Send out
							 | 
						||
| 
								 | 
							
								#                        CNews email')
							 | 
						||
| 
								 | 
							
								    exit(1);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#===  FUNCTION  ================================================================
							 | 
						||
| 
								 | 
							
								#         NAME: Options
							 | 
						||
| 
								 | 
							
								#      PURPOSE: Processes command-line options
							 | 
						||
| 
								 | 
							
								#   PARAMETERS: $optref     Hash reference to hold the options
							 | 
						||
| 
								 | 
							
								#      RETURNS: Undef
							 | 
						||
| 
								 | 
							
								#  DESCRIPTION:
							 | 
						||
| 
								 | 
							
								#       THROWS: no exceptions
							 | 
						||
| 
								 | 
							
								#     COMMENTS: none
							 | 
						||
| 
								 | 
							
								#     SEE ALSO: n/a
							 | 
						||
| 
								 | 
							
								#===============================================================================
							 | 
						||
| 
								 | 
							
								sub Options {
							 | 
						||
| 
								 | 
							
								    my ($optref) = @_;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    my @options = ( "help", "from=s", "count=i", "force!", "reminder!");
							 | 
						||
| 
								 | 
							
								#        "summary|rs=s" );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ( !GetOptions( $optref, @options ) ) {
							 | 
						||
| 
								 | 
							
								        usage();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# vim: syntax=perl:ts=8:sw=4:et:ai:tw=78:fo=tcrqn21:fdm=marker
							 |