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

Hi experts, I have created my own program to add and subtract (in this example Im subtraction 7 days from current day so basically a week ago) calendar dates. Is there an easier way to do this without having to update the code (in my validate_years and validate_month subs). It works but Im still unconfident about the outcome. Sorry the code is so long. Any advice would be appreciated. Thanks in advance... Fue
#!/usr/bin/perl #Variables for dates $year = qx(date +%Y); $month = qx(date +%b); $date = qx(date +%d); $date2 = $date; $month = uc($month); chop($month); chop($date); chop($date2); chop($year); #Opens template and write to SAS file to use die("Cannot open daily_pull_temp.sas to write to.") unless(open(WRITE, ">daily_pull_temp.sas")); die("Cannot open daily_pull.sas to read from.") unless(open(GETDATA, "<daily_pull.sas")); while($get = <GETDATA>) { if ($get =~ /<date2>/) { $get =~ s/<date2>/$date2/g; } if ($get =~ /<year><month>/) { if ($date <= 7) { $month = &minus_month($month); $newdate = &validate_Month($month); $date = ($newdate + $date); $date = $date - 7; # $year = &validate_Year($month); $month = &change_mo2num($month); $get =~ s/<year>/$year/g; $get =~ s/<month>/$month/g; $get =~ s/<date>/$date/g; print WRITE "$get"; } else { #month stays as current month $newdate = &validate_Month($month); # $year = &validate_Year($month); $month = &change_mo2num($month); $date = $date - 7; if ($date < 10) { $date = "0" . $date; } $get =~ s/<year>/$year/g; $get =~ s/<month>/$month/g; $get =~ s/<date>/$date/g; print WRITE "$get"; } } else { print WRITE "$get"; } } close(WRITE); close(GETDATA); ###### SUBS START HERE ############### sub validate_Year { my ($month) = @_; if ($month eq "jan") { $yr = qx(date +%Y); return $yr; } else { ####### Must Change ####### $yr = "2003"; } } sub validate_Month { my ($m) = @_; if (($m eq "JAN") xor($m eq "MAR") xor ($m eq "MAY") xor ($m eq "JUL") xor ($m eq "AUG") xor ($m eq "OCT") xor ($m eq "DEC")) { $day = 31; return $day; } elsif (($m eq "APR") xor($m eq "JUN") xor ($m eq "SEP") xor ($m eq "NOV")) { $day = 30; return $day; } elsif ($m = "FEB") { if (($testyear eq "2003") xor ($testyear eq "2005") xor ($tes +tyear eq "2007") xor ($testyear eq "2009")) { $day = 28; return $day; } else { $day = 29; return $day; } } } sub add_month { my ($month) = @_; if ($month eq "JAN") { $month = "FEB"; return $month; } elsif ($month eq "FEB") { $month = "MAR"; return $month; } elsif ($month eq "MAR") { $month = "APR"; return $month; } elsif ($month eq "APR") { $month = "MAY"; return $month; } elsif ($month eq "MAY") { $month = "JUN"; return $month; } elsif ($month eq "JUN") { $month = "JUL"; return $month; } elsif ($month eq "JUL") { $month = "AUG"; return $month; } elsif ($month eq "AUG") { $month = "SEP"; return $month; } elsif ($month eq "SEP") { $month = "OCT"; return $month; } elsif ($month eq "OCT") { $month = "NOV"; return $month; } elsif ($month eq "NOV") { $month = "DEC"; return $month; } elsif ($month eq "DEC") { $month = "JAN"; return $month; } } sub minus_month { my ($month) = @_; if ($month eq "JAN") { $month = "DEC"; return $month; } elsif ($month eq "FEB") { $month = "JAN"; return $month; } elsif ($month eq "MAR") { $month = "FEB"; return $month; } elsif ($month eq "APR") { $month = "MAR"; return $month; } elsif ($month eq "MAY") { $month = "APR"; return $month; } elsif ($month eq "JUN") { $month = "MAY"; return $month; } elsif ($month eq "JUL") { $month = "JUN"; return $month; } elsif ($month eq "AUG") { $month = "JUL"; return $month; } elsif ($month eq "SEP") { $month = "AUG"; return $month; } elsif ($month eq "OCT") { $month = "SEP"; return $month; } elsif ($month eq "NOV") { $month = "OCT"; return $month; } elsif ($month eq "DEC") { $month = "NOV"; return $month; } } #The 2 subroutines for date conversion sub change_num2mo{ my ($month) = @_; if ($month =~ /01/) { $num_month = "jan"; return $num_month; } if ($month =~ /02/) { $num_month = "feb"; return $num_month; } if ($month =~ /03/) { $num_month = "mar"; return $num_month; } if ($month =~ /04/) { $num_month = "apr"; return $num_month; } if ($month =~ /05/) { $num_month = "may"; return $num_month; } if ($month =~ /06/) { $num_month = "jun"; return $num_month; } if ($month =~ /07/) { $num_month = "jul"; return $num_month; } if ($month =~ /08/) { $num_month = "aug"; return $num_month; } if ($month =~ /09/) { $num_month = "sep"; return $num_month; } if ($month =~ /10/) { $num_month = "oct"; return $num_month; } if ($month =~ /11/) { $num_month = "nov"; return $num_month; } if ($month =~ /12/) { $num_month = "dec"; return $num_month; } } sub change_mo2num { my ($month) = @_; if ($month =~ /JAN/) { $num_month = "01"; return $num_month; } if ($month =~ /FEB/) { $num_month = "02"; return $num_month; } if ($month =~ /MAR/) { $num_month = "03"; return $num_month; } if ($month =~ /APR/) { $num_month = "04"; return $num_month; } if ($month =~ /MAY/) { $num_month = "05"; return $num_month; } if ($month =~ /JUN/) { $num_month = "06"; return $num_month; } if ($month =~ /JUL/) { $num_month = "07"; return $num_month; } if ($month =~ /AUG/) { $num_month = "08"; return $num_month; } if ($month =~ /SEP/) { $num_month = "09"; return $num_month; } if ($month =~ /OCT/) { $num_month = "10"; return $num_month; } if ($month =~ /NOV/) { $num_month = "11"; return $num_month; } if ($month =~ /DEC/) { $num_month = "12"; return $num_month; } }

Replies are listed 'Best First'.
Re: adding/subtracting calender dates
by tlm (Prior) on Jun 02, 2005 at 19:00 UTC

    I haven't read your code (which, BTW, you should put between <readmore></readmore> tags), but it looks to me like you are re-inventing a thoroughly invented wheel. Look at Date::Calc, for example.

    the lowliest monk

      Thanks guys... Duh, why didnt I think of that.. Anyways I tried the readmore, I just forgot the /readmore... Sorry guys... I will take all ur advice and try the date modules. Everyone gets Votes for being alot smarter than me... :)
Re: adding/subtracting calender dates
by ysth (Canon) on Jun 02, 2005 at 19:02 UTC
    A good way to comfort yourself is to design some tests, but in this case, you are laboriously reinventing a wheel that's likely to leave you with buggy code. Try DateTime or Date::Manip or Date::Calc.

    By the way, I don't think xor does what you think it does.

      Hmmm well it seemed to work like I wanted. Please let me know what you think it does. It would be great to know what it really does as to what I think it does. Thanks again... Fue
      xor =
      by hubb0r (Pilgrim) on Jun 02, 2005 at 20:50 UTC
        FYI - xor is an "exclusive or". It has the following rules:
        (1 xor 1) == 0 (1 xor 0) == 1 (0 xor 1) == 1 (0 xor 0) == 0 (0 xor 1 xor 1) == 0 (0 xor 1 xor 0) == 1 etc etc
        Meaning that it will return true if and only if exactly 1 of the xor'd variables returns true.
Re: adding/subtracting calender dates
by kirbyk (Friar) on Jun 02, 2005 at 19:13 UTC
    First off, there are some perl modules that do all of this stuff for you. Date::Calc and Date::Manip are both good starts. (I'm sure someone will have posted links before I finish typing this.)

    But, in the interest of learning a programming trick or two, though, consider doing your month math something more like:

    my @months = qw(JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC); sub change_num_to_month { my $num_month = shift; return $months[$num_month - 1]; } sub add_month { my $month = shift; my $month_num = change_month_to_num($month); $month_num++; $month_num = 1 if $month_num > 12; return change_num_to_month($month_num); } # And very similarly for subtract month
    Getting the reverse lookup is slightly trickier. I'd probably convert the array into a hash, something like: (I'm sure you can do better with a map, but let's go for readibilty first.)
    my %month_nums; for (my $i = 0; $i <= $#months; $i++) { $month_nums{$month[$i]} = $i; } # This creates a hash of 'Jan' => 0, 'Feb' => 1, etc. sub change_month_to_num { my $month = shift; return $month_nums{$month} + 1; }
    One has to be careful about the array index starting at 0, if you want your month nums to range from 1 to 12, but that's the only really tricky bit.

    -- Kirby, WhitePages.com

Re: adding/subtracting calender dates
by fishbot_v2 (Chaplain) on Jun 02, 2005 at 19:19 UTC

    In addition to agreeing with the previous posters that there are excellent modules out there that solve this problem better, I have a general comment about your code:

    Any time you have that many if-elses or ifs in a row, program your brain to think of either a loop, or a hash, or both. Your month-to-num and the reverse could have been implemented better with an array or hash-reversed solution. Adding to month can then be a switch to a number, then a normal add, then a switch back, etc.

    I can't make any sense out of the validate_Month subroutine... (it returns days? why? And the leap year code is mystifying...) but that would be a lot easier with a hash.

    Not for this code, which should be replaced with an existing module, but for next time.

Re: adding/subtracting calender dates
by dave0 (Friar) on Jun 02, 2005 at 19:01 UTC
    You should really have a look at Date::Calc instead.
Re: adding/subtracting calender dates
by TedPride (Priest) on Jun 02, 2005 at 21:01 UTC
    Pardon me for asking stupid questions, but what's wrong with timestamps and localtime()? For instance, to get the date / time of 7 days ago:
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(t +ime() - 7*86400);
    If you're going to give an example of what your code can do, it needs to be something that can't be achieved just as easily using built-in Perl functions.
      It would require some care during DST change, because one day would last a hour more/less.

      Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')

      Don't fool yourself.