Subject: | Not a bug ... |
I had an implementation mostly written when I found your cron program. I ripped out my cron parsing routine and condensed and used yours.
I'm just submitting my code for your consideration. Please feel free to incorporate whatever you want.
Alan
##############################################
## Copyright (c) 2002-2004 - Brendan Fagan
##
## This package borrows *heavily* from the
## ParseCron package found on
## CPAN and copyrighted by the above named
##
## If you're wondering why I reinvented the
## wheel, I already had most of this done
## when I found the Cron::Simple package
## and I needed something that wasn't
## dependent on any other module.
##
## Alan Young (aka HarleyPig)
##############################################
package ParseCron;
use strict;
use vars qw( $VERSION );
$VERSION = '0.01';
sub new {
my ( $class, $cron ) = @_;
my $self = {
'elements' => [qw( minute hour dom month dow )],
};
bless $self, ref $class || $class;
$self->cron( $cron ) if $cron;
return $self;
}
sub cron {
$_[0]->{'cron'} = $_[1];
$_[0]->_parse;
}
# expecting array of elements same as $self->{'elements'}
sub now {
my ( $self, @now ) = @_;
my %now; @now{@{$self->{'elements'}}} = @now || ( localtime )[1..4,6];
for my $element ( @{$self->{'elements'}} ) {
return undef unless $self->{$element}->{$now{$element}};
}
return 1;
}
# expects a reference to an array of cron strings
# returns a reference to an array of cron strings that validate
sub check_array {
my ( $self, $crons ) = @_;
return undef unless ref $crons eq 'ARRAY';
my @now;
for my $n ( @$crons ) {
$self->cron( $n );
push @now, $n if $self->now;
}
return \@now;
}
sub _parse {
my $self = shift;
# This supports names in month and dow
my %equiv = (
'month' => {
'jan' => 0,
'feb' => 1,
'mar' => 2,
'apr' => 3,
'may' => 4,
'jun' => 5,
'jul' => 6,
'aug' => 7,
'sep' => 8,
'oct' => 9,
'nov' => 10,
'dec' => 11,
},
'dow' => {
'sun' => 0,
'mon' => 1,
'tue' => 2,
'wed' => 3,
'thu' => 4,
'fri' => 5,
'sat' => 6,
},
);
my %elements; @elements{@{$self->{'elements'}}} = split /\s+/, $self->{'cron'};
my %ranges; @ranges{@{$self->{'elements'}}} = qw( 59 23 31 11 6 );
for my $element ( @{$self->{'elements'}} ) {
$self->{$element}->{$_} = undef for 0 .. $ranges{$element};
# This allows for case insensitivity and long or short names in
# month and dow
$elements{$element} =~ s!([^\d,/-]{3})(?:[^\d,/-]*)?!$equiv{$element}{lc $1}!gi
if grep { /$element/ } keys %equiv;
for my $entry ( split /,/, $elements{$element} ) {
my ( $num, $interval ) = split /\//, $entry;
my ( $num1, $num2 ) = split /\-/, $num;
$num2 ||= $num1 eq '*' ? $ranges{$element} : $num1;
$num1 = 0 if $num1 eq '*';
$interval ||= 1;
for ( my $i = $num1 ; $i <= $num2 ; $i = $i + $interval ) {
$self->{$element}->{$i} = 1;
}
}
}
}
1;
__END__
=pod
=head1 NAME
ParseCron - Parse a cron entry and check against a time
=head1 SYNOPSIS
use ParseCron
$c = ParseCron->new( $cron );
if ( $c->now ) {
# perform some timed operation
}
$c->cron( $newcron );
=head1 DESCRIPTION
ParseCron checks a crontab entry against a date to see if it matches the given date.
A cron entry follows the cron format from crontab(5).
=head1 EXAMPLE
use ParseCron;
my $c = ParseCron->new('0-59/2 10,12 * * 5');
if ( $c->now ) {
# perform whatever operation is needed
}
# Check another cron entry
$c->cron('* * 1 * 0');
if( $c->now ) {
# perform next operation
}
my $crons = [ #entries ];
my @now = $c->check_array( $crons );
for my $now ( @now ) {
# perform operations
}
=head1 CHANGES
Please see the CHANGES file in the module distribution.
=head1 TO-DO
- currently does not handle ! and > or < in cron entries
- better code implementation
=head1 AUTHOR
Brendan Fagan <suburbanantihero (at) yahoo (dot) com>. Comments, bug reports, patches and flames are appreciated.
=head1 COPYRIGHT
Copyright (c) 2004 - Brendan Fagan
=cut