Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks

I'm trying to add a section of my script to schedule emails, like I want to send an email out once a month and I'm unsure how to approach this in perl.

I will have a date in the database, and a cron script which runs nightly to check if there is one due to be run, like if a signup was the 5th - it will run the 5th of the next month.
I would like to avoid using any perl modules if possible :(


Replies are listed 'Best First'.
Re: Compare Date
by rob_au (Abbot) on Mar 05, 2002 at 12:37 UTC
    I will have a date in the database, and a cron script which runs nightly to check if there is one due to be run, like if a signup was the 5th - it will run the 5th of the next month.


    There is a major flaw with this application logic - What if the signup was performed on the 31st of a month? Using this approach to this problem, the emails would be sent out to the user seven times over the year, that is, only those months with 31 days. In short - it doesn't work.

    When faced with this application requirement previously, I approached this problem somewhat differently. The approach taken was to have the script check the number of days since the sign up was performed and send out the email if thirty days have passed - Or more specifically, the number of days within the year, divided by the number of emails which you wish to send to the user over the year.

    To implement this application logic, the Date::Calc module proved to offer all of the date-related functions required - A snippet of the code employed follows:

    use Date::Calc qw/:all/; . . my @date = (Localtime)[0..2]; . . # iterate through system accounts from the database while (my $account = $accounts->fetchrow_hashref) { my $plan = $plans[ $account->{'planid'} ]; # calculate the delta day count between the current date and the + # date of account activation my $delta = Delta_Days((split '-', $account->{'activationdate'}), +@date); if ($delta == 0) { # the delta day count will equal zero on the same day as the + # account activation - this is useful for when you also want + to # incorporate the sending of a welcome email or alike } else { # within this system each billing plans has a period value w +hich # represents the number of billing systems over a single ann +ual # period - if you are wanting to send out these emails on a # monthly basis, this variable can be replaced with the numb +er # twelve if ($plan->{'fee'} && $plan->{'period'}) { # here is the actual calculation itself, the modulus res +ult # of which will equal zero on days when an email should +be # sent to the user my $calc = $delta % int (Days_in_Year((@date)[0..1]) / $pl +an->{'period'}); if ($calc == 0) { # send the email to the owner of this account } } } }

    For this project, I did also look at other date-related modules on CPAN but found the scope and module documentation of Date::Calc by far the most complete and inviting to work with.


    perl -e 's&&[@.]/&&s&.com.&_&&&print'

Re: Compare Date
by shotgunefx (Parson) on Mar 05, 2002 at 11:59 UTC
    Date modules are there for a reason IMHO. There's many subtle things to go wrong. If you wanted to avoid modules totally, maybe think about doing it with the output of localtime, but I wouldn't exactly suggest it.


    "To be civilized is to deny one's nature."
      ok, thanks Lee - any recommendations on a module to achieve this?
        Date::Calc is a good module. A search for Date on CPAN returns a lot of results. Another one that looks interesting is Class::Date (has relative date functions). I've never used that later but it's worth looking into.

        I think one thing you have to figure out right off the bat is how to handle messages that should be sent on dates that don't occur every month.


        "To be civilized is to deny one's nature."

        Date::Calc looks like it will do what you want.
        ($year,$month,$day) = Add_Delta_YMD($year,$month,$day, $Dy,$Dm,$Dd);
        From the docs...
        If the resulting date happens to fall on a day beyond the end of the resulting month, like the 31st of April or the 29th of February (in non-leap years), then the day is replaced by the last valid day of that month in that year (e.g., the 30th of April or 28th of February). BEWARE that this behaviour differs from that of previous versions of this module!
Re: Compare Date
by mojotoad (Monsignor) on Mar 05, 2002 at 18:23 UTC
    The Date::Manip module provides some easy methods for dealing with this sort of thing on a pretty high level. It's slower than Date::Calc but for your problem it sounds like a one-shot calculation up front. For example you could do this:
    $next_date = DateCalc($cur_date,"+ 1 month");
    and it just does the right thing.

    In your case, though, it sounds like you'd be looking backwards, so you might be using more along the lines of:

    $activation_date = DateCalc('today', '- 1 month');
    then checking your database for folks that signed up then.


Re: Compare Date
by OzzyOsbourne (Chaplain) on Mar 07, 2002 at 21:31 UTC
    This may be too simplistic, but can't you use this to give you the current day and just compare it to your database via an if statement?: