Crontab

Tag Handling

Time Tag and components

Languages

In the following code you will see some ways how to handle tags. I was also thinking about the possibility of internationalization. I know this topic is handled in a standard way. I will probably check that standard way somtimes. But for now I had a bit of fun with that. What you see below is a snipet with the definitions.

# language codes come from here http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes (I took the 639-3 codes)
my %month_in_language=(
    'eng' => [ 'Jan', 'Feb', 'Mar',    'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov',    'Dec', ],
    'slk' => [ 'Jan', 'Feb', 'Mar',    'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov',    'Dec', ],
    'ces' => [ 'Led', 'Unr', 'Brz', 'Dub', 'Kvt', 'Cer', 'Cvc', 'Srp', 'Zar', 'Rij', 'Lis', 'Pro', ],
    'hun' => [ 'Jan', 'Feb', 'Mar',    'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sze', 'Okt', 'Nov',    'Dec', ],
    'deu' => [ 'Jan', 'Feb', 'Mar',    'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov',    'Dez', ],
);
 
# the first day of week is Monday
my %dayofweek_in_language=(
    'eng' => [ 'Mon', 'Tue', 'Wed',    'Thu', 'Fri', 'Sat', 'Sun', ],
    'slk' => [ 'Po' , 'Ut' , 'Str',    'Stv', 'Pi' , 'So' , 'Ne' , ],
    'ces' => [ 'Po' , 'Ut' , 'St' ,    'Ct' , 'Pa' , 'So' , 'Ne' , ],
    'hun' => [ 'Het', 'Ked', 'Sze' ,'Csu', 'Pen', 'Szo', 'Vas', ],
    'deu' => [ 'Mo' , 'Di' , 'Mi' , 'Do' , 'Fr' , 'Sa' , 'So' , ],    
);

Month and day of week conversion

The next step for me was to do the conversion of month names and the names of the week days into numbers. I use 7 for Sunday. There is no particular reason for that. You could add a random generator which chooses 0 or 7 for Sunday. And also I have a global variable called $setup_lang which holds the language code.

sub replace_month_name ($)
{
    my $replaced_string=shift;
    my %month2number=(
        $month_in_language{$setup_lang}[0] => 1,
        $month_in_language{$setup_lang}[1] => 2,
        $month_in_language{$setup_lang}[2] => 3,
        $month_in_language{$setup_lang}[3] => 4,
        $month_in_language{$setup_lang}[4] => 5,
        $month_in_language{$setup_lang}[5] => 6,
        $month_in_language{$setup_lang}[6] => 7,
        $month_in_language{$setup_lang}[7] => 8,
        $month_in_language{$setup_lang}[8] => 9,
        $month_in_language{$setup_lang}[9] => 10,
        $month_in_language{$setup_lang}[10] => 11,
        $month_in_language{$setup_lang}[11] => 12,
    );
 
    foreach my $month_name ( keys %month2number )
    { 
        $replaced_string=~s/$month_name/$month2number{$month_name}/g;
    }
 
    return $replaced_string;
}
 
sub replace_dayofweek_name ($)
{
    my $replaced_string=shift;
    my %dayofweek2number=(
        $dayofweek_in_language{$setup_lang}[0] => 1,
        $dayofweek_in_language{$setup_lang}[1] => 2,
        $dayofweek_in_language{$setup_lang}[2] => 3,
        $dayofweek_in_language{$setup_lang}[3] => 4,
        $dayofweek_in_language{$setup_lang}[4] => 5,
        $dayofweek_in_language{$setup_lang}[5] => 6,
        $dayofweek_in_language{$setup_lang}[6] => 7,
    );
 
    foreach my $dayofweek_name ( keys %dayofweek2number )
    { 
        $replaced_string=~s/$dayofweek_name/$dayofweek2number{$dayofweek_name}/g;
    }
 
    return $replaced_string;
}

Range expansion

So now I am able to replace month names and week day names. The next step is to expand those ranges into single values. After that checking of the resulting string should be easy. The resulting string must contain only digits and commas. (ToDo add a routine which replaces consecutive commas, commas at the start of the string and commas at the end of the string, add a check directly at the start to check for allowed chracters maybe)

  • The expand_range will convert the given three number into a range. First parameter is the start of a range, second is the end of the range and the third one represents the increment step.
  • The replace_range will expand all occurences of a range in the input string. Maybe I coud change that test on element $element=~m/(\d+)-(\d+)$/ and make it something like $element=~m/(\d+)-(\d+)[^\/]/ ?
sub expand_range ($$$)
{
    my $start_from=shift;
    my $end_at=shift;
    my $single_step=shift;
    my $range_string;
 
    for ( my $i=$start_from; $i<=$end_at; $i+=$single_step )
    {
        $range_string.=$i.",";
    }
    chop $range_string;
 
    return $range_string;
}
 
sub replace_range ($)
{
    my $replaced_string=shift;
 
    my @elements=split ( /,/, $replaced_string );
    $replaced_string="";
    foreach my $element ( @elements )
    {
        if ( $element=~m/^(\d+)-(\d+)$/ )
        {
            $element=expand_range($1,$2,1);
        } 
        elsif ( $element=~m/^(\d+)-(\d+)\/(\d+)$/ )
        {
            $element=expand_range($1,$2,$3);
        }
        $replaced_string.=$element.",";
    }
    chop $replaced_string;
 
    return $replaced_string;
}

As you can see it does not handles invalid entries. I would like to do the validation in a separate subroutine if possible. For that see the Validation chapter.

Validation

This is to come in the next days/month/years.

Mailto Tag

Ok, seems the email validation is an art for itself. Neverthless a good read.

Testing

Well of course the tests for this small collection of subroutines is also present. I won't comment on it much. Just take a look at it. I have entered test data only for the english language. I use Test::More for testing but probably Test::Simple would suffice too. All of these tests are positive tests (no errorneous cases).

#test data
my %_test_replace_dayofweek_name=(
    "eng" => {
        "Mon,Tue,Wed,Thu,Fri,Sat,Sun" => [ "1,2,3,4,5,6,7", "replace_dayofweek_name all single value" ],
        "Mon-Fri/2"                   => [ "1-5/2", "replace_dayofweek_name range with interval" ],
        "Tue-Sat"                     => [ "2-6", "replace_dayofweek_name simple range" ],
    },
);
 
my %_test_replace_month_name=(
    "eng" => {
        "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec" => [ "1,2,3,4,5,6,7,8,9,10,11,12", "replace_month_name all single value" ],
        "Jan-Dec" => [ "1-12", "replace_month_name simple range" ],
        "Jun-Aug/2" => [ "6-8/2", "replace_month_name range with interval" ],
    },
);
 
my %_test_replace_range=(
    "1-3"            => [ "1,2,3"            , "replace_simple_range range" ],
    "5-7,3-8"        => [ "5,6,7,3,4,5,6,7,8", "replace_simple_range range,range" ],
    "1-8/2"          => [ "1,3,5,7"          , "replace_simple_range range/interval" ],
    "4-5,6,7-8"      => [ "4,5,6,7,8"        , "replace_simple_range range,single,range" ],
    "1-5/3,6,9-15/4" => [ "1,4,6,9,13"       , "replace_simple_range range/interval,single,range/interval" ],
    "1-5,6,9-15/4"   => [ "1,2,3,4,5,6,9,13" , "replace_simple_range range,single,range/interval" ],
);
 
# test execution
foreach my $test_key ( keys %{ $_test_replace_dayofweek_name{$setup_lang} } )
{
    is (     replace_dayofweek_name( $test_key ),
            $_test_replace_dayofweek_name{$setup_lang}{$test_key}[0], 
            $_test_replace_dayofweek_name{$setup_lang}{$test_key}[1] );
}
 
foreach my $test_key ( keys %{ $_test_replace_month_name{$setup_lang} } )
{
    is (     replace_month_name( $test_key ),
            $_test_replace_month_name{$setup_lang}{$test_key}[0], 
            $_test_replace_month_name{$setup_lang}{$test_key}[1] );
}
 
foreach my $test_key ( keys %_test_replace_range )
{
    is (     replace_range( $test_key ),
            $_test_replace_range{$test_key}[0], 
            $_test_replace_range{$test_key}[1] );
}

ToDo's

  • the star needs to be expanded too…
Add a New Comment
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-Share Alike 2.5 License.