http://www.perlmonks.org?node_id=928016


in reply to Re: Happy unbirthday! (To all, with a challenge)
in thread Happy unbirthday!

Well, you put out your challenge for the newcomers, however, I could not resist trying. I have added error checking in for days, months, and years. I put a little personalization into the resulting messages. Also, I have added days until your next birthday. For the latter, there are still kinks I am still trying to work out regarding the previous two months which returns a negative number of days until the next birthday. Today is September 27, 2011. Any dates between July 28 and August 31 create the negative days until the next birthday.

So, here is the expanded code with error checking. I used the built in features of Date::Calc to do months instead of reinvention.

#!/usr/bin/perl use strict; use warnings; use Date::Calc qw(:all); use Lingua::EN::Inflect qw(ORD); sub commify { local $_ = shift; 1 while s/^([-+]?\d+)(\d{3})/$1,$2/; return $_; } sub birth_year { print "What year were you born? (YYYY) "; chomp(my $b_year = <>); # Checking for a 4 digit year. if ($b_year !~ /\d{4}/) { print "Sorry, please use the full year.\n"; $b_year = birth_year(); } return $b_year; } sub birth_month { print "What month were you born? (MM or name) "; chomp(my $b_month = <>); # This is language specific for English. if ($b_month =~ /[a-z]+/i) { $b_month = Decode_Month($b_month,1); # If the month doesn't match, it will try again. # Decode_Month had error checking in mind. if ($b_month =~ /0/) { print "Sorry, that is not a month.\n"; $b_month = birth_month(); } } return $b_month; } sub birth_day { my ($b_year,$b_month) = @_; my $month_name = Month_to_Text($b_month); print "What day were you born? (DD) "; chomp(my $b_day = <>); # Checking for 0. if (!$b_day or $b_day == 0) { print "Sorry, you did not specify a date.\n"; $b_day = birth_day($b_year,$b_month); } # Checking days for February first. elsif ($b_month =~ /^(0?[2])$/) { if ($b_year % 4 == 0 && $b_day > 29) { print "Sorry, $month_name in $b_year only had 29 days.\n"; $b_day = birth_day($b_year,$b_month); } elsif ($b_day > 28) { print "Sorry, $month_name in $b_year only had 28 days.\n"; $b_day = birth_day($b_year,$b_month); } } # Checking for the months with 31 days. elsif ($b_month =~ /^(0?[13578]|1[02])$/ && $b_day > 31) { print "Sorry, $month_name only has 31 days.\n"; $b_day = birth_day($b_year,$b_month); } # Checking for the months with 30 days. elsif ($b_month =~ /^(0?[469]|11)$/ && $b_day > 30) { print "Sorry, $month_name only has 30 days.\n"; $b_day = birth_day($b_year,$b_month); } return $b_day; } # Let's personalize it a bit. :) print "What's your name? "; chomp(my $name = <>); my $birth_year = birth_year(); my $birth_month = birth_month(); my $birth_day = birth_day($birth_year,$birth_month); my $year = (localtime)[5] + 1900; my $month = (localtime)[4] + 1; my $day = (localtime)[3]; # The following counts how many birthdays there has been. # It also figures out the next year for a birthday. # I'm still working on the kinks in the next birthday. my $birthdays; my $next_year; if ($birth_month > $month) { $birthdays = $year - $birth_year - 1; $next_year = $year; } else { if ($birth_day > $day) { $birthdays = $year - $birth_year - 1; $next_year = $year; } else { $birthdays = $year - $birth_year; $next_year = $year + 1; } } my @birth = ($birth_year,$birth_month,$birth_day); my @next_bday = ($next_year,$birth_month,$birth_day); my @today = ($year,$month,$day); my $days_alive = Delta_Days(@birth,@today); my $days_til_next_bday = Delta_Days(@today,@next_bday); my $unbirthdays = ORD(commify($days_alive - $birthdays)); print "\nHappy $unbirthdays unbirthday, $name!\n"; print "You have $days_til_next_bday days until your next birthday.\n";
Have a cookie and a very nice day!
Lady Aleena

Replies are listed 'Best First'.
Re^3: Happy unbirthday! (To all, with a challenge)
by Not_a_Number (Prior) on Sep 27, 2011 at 12:03 UTC

    I found a a couple of bugs in your code.

    First, when I use it today, it prints "Happy 22,023nd unbirthday, dave!" Change the antepenultimate line to:

    my $unbirthdays = commify( ORD($days_alive - $birthdays));

    Second, if somebody types in "October" for their month of birth, they'll be surprised to see "Sorry, this is not a month"! Change the line:

    if ($b_month =~ /0/) {

    to:

    if ($b_month != 0) {

    In fact, I would refactor that bit of code to avoid the programme dying ungraciously if the user enters a non-alphanummeric character:

    if ($b_month !~ /^\d+$/) { $b_month = Decode_Month($b_month,1); } if (! $b_month) { print "Sorry, that is not a month.\n"; $b_month = birth_month(); }

    Lastly, a minor niggle: 1900, for example, was not a leap year, despite being divisible by 4.

    Update: You also seem to have an off-by-one error. If I claim to have been born today, the output is:

    Happy 0th unbirthday, dave!

    Surely, today should be by first (not 0th) unbirthday?

Re^3: Happy unbirthday! (To all, with a challenge)
by Tux (Canon) on Sep 27, 2011 at 07:45 UTC

    As I had to install Lingua::EN::Inflect to make it run, the obvious next question is to make it localize

    Happy 18,119th unbirthday, Tux! You have 94 days until your next birthday.

    I never use the TRIADSEP in numerals, and in Dutch, 18,119th looks stupid.

    (I had a lot of fun back in the day when I still used Windows-NT on my desktop. Windows allows you to set virtually any specific locale entry, getting you weird combinations. I used to set RADIXSEP to "." (default en_US) and TRIADSEP to " " (that is a space :). Have a look how many import- and export filters for the M$ suite start failing.)


    Enjoy, Have FUN! H.Merijn

      I wrote this in English, my native language. I am open to translations, if you are willing to translate this to Dutch with all the Dutch grammar rules for ordinal numbers. The language code for Dutch in Date::Calc is 6. The only languages I speak are English and German, and my German is very rusty. I am also open to other suggestions for improvement.

      Have a cookie and a very nice day!
      Lady Aleena