http://qs321.pair.com?node_id=546372
Category: fun stuff
Author/Contact Info
Description: This is a little function to calculate a person's age, the number of days to their next birthday and the number of days from their last birthday.

If the birthdate is today, then the number of days to and from the last birthday = 0.

I can't say for certain that it's accurate for all situations (leap years and what not), but it's quite simple and doesn't rely on any additonal modules other than Time::Local which should be standard.

Updates:
- changed the name to 'birth_date_age_today' so there's no confusion as to it's purpose.
- Removed current day and month as these are not used.
- Used timelocal_nocheck to account for birthdates on Feb 29 - Added condition to leap year check so script continues to operate in the year 2100
- Added more comments (so people don't get the wrong idea about leap years). - fixed missing brackets on leap year test - added test code to check results
#!/usr/bin/perl

use strict;
use warnings;

use Time::Local qw(timelocal_nocheck);

# calculate Age, days from last birthday and days to next birthday for
# a given birth date.
# birthdate is a string of the format 'YYYY-MM-DD'
#  - good for accepting dates from databases
# If the birthdate is today, then days from = days to = 0
# returns age, days from, days to

# NOTE: this script does no date validation
# NOTE: this script uses the current year to calculate age.
# NOTE: if you modify this script to use anything other than current y
+ear,
#       there may be leap year implications

sub birth_date_age_today {
    my $birthdate = shift || return 0;
    $birthdate =~ m/^(\d\d\d\d)-(\d\d)-(\d\d)$/ or return 0;

    my ($byyyy,$bmm,$bdd) = ($1,$2,$3);
        --$bmm;
    my ($yyyy) = (localtime)[5];
        $yyyy += 1900;

    my $cur_day_of_year = (localtime)[7];
        # nocheck or feb 29 birthdays will have problems
    my $birth_day_of_year = (localtime timelocal_nocheck(0, 0, 0, $bdd
+, $bmm, $yyyy - 1900))[7];

    # calculate age
    my $age = $yyyy - $byyyy;
    my $daysto;
    my $daysfrom;
    if ($cur_day_of_year < $birth_day_of_year) {
        # haven't hit birthday yet this year
        $age--;
        $daysto = $birth_day_of_year - $cur_day_of_year;
        # last year's birthday
        $yyyy--;
        $birth_day_of_year = (localtime timelocal_nocheck(0, 0, 0, $bd
+d, $bmm, $yyyy - 1900))[7];
        # correct for days (2100 isn't a leap year)
        $daysfrom = (($yyyy % 4 or $yyyy == 2100) ? 365 : 366) - $birt
+h_day_of_year + $cur_day_of_year;
    }
    elsif ($cur_day_of_year > $birth_day_of_year) {
        # passed birthday this year
        $daysfrom = $cur_day_of_year - $birth_day_of_year;
        # next year's birthday
        # Note: we get the number of days to the birthday in the next 
+year ($yyyy - 1899)
        # but we use the current year for checking leap year because w
+e need to know how
        # many days are left in this year
        $birth_day_of_year = (localtime timelocal_nocheck(0, 0, 0, $bd
+d, $bmm, $yyyy - 1899))[7];
                # correct for leap days (2100 isn't a leap year)
        $daysto = (($yyyy % 4 or $yyyy == 2100) ? 365 : 366) - $cur_da
+y_of_year + $birth_day_of_year;
    }
    else { $daysfrom = $daysto = 0 }

    return $age, $daysfrom, $daysto;
}

# a simple test script
# run through every date (birthdate) of a given year
# print the results (relative to current date)
my %days = (
    1 => 31,
    2 => 28,
    3 => 31,
    4 => 30,
    5 => 31,
    6 => 30,
    7 => 31,
    8 => 31,
    9 => 30,
    10 => 31,
    11 => 30,
    12 => 31,
    );

my $yyyy = 1965;
# This is not totally correct!  Just simplified for this test
$days{2} = ($yyyy % 4 or $yyyy == 1900 or $yyyy == 2100) ? 28 : 29;

for (my $mm = 1; $mm <= 12; ++$mm) {
    for (my $dd = 1; $dd <= $days{$mm}; ++$dd) {
        my $testdate = sprintf("$yyyy-%02d-%02d", $mm, $dd);
        my ($age, $daysfrom, $daysto) = birth_date_age_today($testdate
+);
        print "birthdate $testdate  age $age  from last $daysfrom  day
+s to $daysto\n";
    }
}