Beefy Boxes and Bandwidth Generously Provided by pair Networks Cowboy Neal with Hat
Do you know where your variables are?
 
PerlMonks  

Re: Happy unbirthday! (To all, with a challenge)

by ww (Bishop)
on Sep 25, 2011 at 05:24 UTC ( #927709=note: print w/ replies, xml ) Need Help??


in reply to Happy unbirthday!

Your perseverence wins plaudits. + +, Lady A!

FWIW, it's only a minor mod to make your program accept month_names. Wisdom says, do it with the capabilities already written into Date::Calc and tested repeatedly. Curiousity said, 'Do something different!'

Hence, this derivative - which is basicly your code with minor mods and a hash as the heart of the conversion (for all I know, this is what happens inside use Date::Calc qw/Decode_Month/ but the night's too far gone to check that):

#!/usr/bin/perl use strict; use warnings; use 5.012; # Lady Aleena's # 927621 use Data::Dumper; use Date::Calc qw(:all); use Lingua::EN::Inflect qw(ORD); #local $\ = "\n"; #commenting this out puts data entry on same li +ne as the relevant prompt; personal pref sub commify { local $_ = shift; 1 while s/^([-+]?\d+)(\d{3})/$1,$2/; return $_; } say "\n\tUsage: 4 digit year, Month as name -* OR *- as month_number, +1-12, day_of_month as number.\n"; print "What year were you born? "; chomp (my $birth_year = <>); print "What month were you born? "; chomp (my $birth_month = <>); print "What day were you born? "; chomp (my $birth_day = <>); if ( $birth_month =~ /[A-Z]+/i ) { $birth_month = convert($birth_month); # NOTE 1 in head, below } my $year = (localtime)[5] + 1900; my $month = (localtime)[4] + 1; my $day = (localtime)[3]; my @birth = ($birth_year,$birth_month,$birth_day); my @today = ($year,$month,$day); # Just figuring how many birthdays you have had. my $birthdays; if ($birth_month > $month) { $birthdays = $year - $birth_year - 1; } else { if ($birth_day > $day) { $birthdays = $year - $birth_year - 1; } else { $birthdays = $year - $birth_year; } } my $days_alive = Delta_Days(@birth,@today); my $unbirthdays = ORD(commify($days_alive - $birthdays)); print "Happy $unbirthdays unbirthday!"; sub convert # named months to Date::Calc numeric months { my $word_month=$_[0]; my %months = ('jan' => 1, # NOT zero-based - Date::Calc forma +t 'feb' => 2, 'mar' => 3, 'apr' => 4, 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8, 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12); my ($key, $value); while (($key, $value) = each(%months)) { if ( $key =~ /$word_month/i ) { return($value) } else { next; } } } =head NOTE 1 # Conversion with the sub here is for illustration only; in practice, +it's better done with '$birth_month=Decode_Month($Birth Month);' cf perl -e "use Date::Calc qw/Decode_Month/; $mon='Dec'; $mon_name = Deco +de_Month($mon); print '\$mon_name: ' . $mon_name;" \$mon_name: 12 =cut

Now, a fairly trivial challenge -- chiefly for newcomers, please -- Add error checking on the data entered for birth_year and birth_month... and for bonus points, code or narrative description of other enhancements. The effort may put you on the path Lady_A has followed with such success.


Comment on Re: Happy unbirthday! (To all, with a challenge)
Select or Download Code
Re^2: Happy unbirthday! (To all, with a challenge)
by Lady_Aleena (Chaplain) on Sep 27, 2011 at 06:07 UTC

    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

      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

      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?

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://927709]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (8)
As of 2014-04-18 22:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (472 votes), past polls