Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Happy unbirthday!

by Lady_Aleena (Chaplain)
on Sep 23, 2011 at 21:24 UTC ( #927621=CUFP: print w/ replies, xml ) Need Help??

Here is a silly little script that will tell you your unbirthday when run.

#!/usr/bin/perl use strict; use warnings; use Date::Calc qw(:all); use Lingua::EN::Inflect qw(ORD); local $\ = "\n"; sub commify { local $_ = shift; 1 while s/^([-+]?\d+)(\d{3})/$1,$2/; return $_; } print "What year were you born?"; my $birth_year = <>; print "What month were you born?"; my $birth_month = <>; print "What day were you born?"; my $birth_day = <>; my $year = (localtime)[5] + 1900; my $month = (localtime)[4] + 1; my $day = (localtime)[3]; chomp($birth_year,$birth_month,$birth_day); 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!";
Have a cookie and a very nice day!
Lady Aleena

Comment on Happy unbirthday!
Download Code
Re: Happy unbirthday!
by CountZero (Bishop) on Sep 24, 2011 at 13:33 UTC
    What an original idea!

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Happy unbirthday! (To all, with a challenge)
by ww (Bishop) on Sep 25, 2011 at 05:24 UTC
    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.

      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 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: CUFP [id://927621]
Approved by ww
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (4)
As of 2014-09-18 07:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (109 votes), past polls