1#!/usr/bin/perl
2use strict;
3use warnings;
4
5use Time::Moment;
6
7# Calculates age of a person in calendar years.
8#
9# Where a person has been born on February 29th in a leap year, the
10# anniversary in a non-leap year can be taken to be February 28th or
11# March 1st. Some countries have laws defining which date a person
12# born on February 29th comes of age in legal terms. In England and
13# Wales, for example, the legal age of a leapling is March 1st in
14# common years. The same applies in Hong Kong. In Taiwan and in
15# New Zealand, the legal age of a leapling is February 28th in
16# common years.
17# https://en.wikipedia.org/wiki/February_29#Births
18sub calculate_age {
19    @_ == 2 or @_ == 3 or die q/Usage: calculate_age($birth, $event [, $march])/;
20    my ($birth, $event, $march) = @_;
21
22    my $years = $birth->delta_years($event);
23
24    unless ($march) {
25        # Increment if birth is 02-29 and event is 02-28 in a non-leap year
26        ++$years if $birth->day_of_year == 31 + 29 &&  $birth->is_leap_year
27                 && $event->day_of_year == 31 + 28 && !$event->is_leap_year;
28    }
29    return $years;
30}
31
32my @tests = (
33    [ '2008-02-28T00Z', '2015-02-27T00Z', 0, 6 ],
34    [ '2008-02-28T00Z', '2015-02-28T00Z', 0, 7 ],
35    [ '2008-02-28T00Z', '2015-03-01T00Z', 0, 7 ],
36    [ '2008-02-29T00Z', '2015-02-27T00Z', 0, 6 ],
37    [ '2008-02-29T00Z', '2015-02-28T00Z', 0, 7 ],
38    [ '2008-02-29T00Z', '2015-03-01T00Z', 0, 7 ],
39    [ '2008-03-01T00Z', '2015-02-27T00Z', 0, 6 ],
40    [ '2008-03-01T00Z', '2015-02-28T00Z', 0, 6 ],
41    [ '2008-03-01T00Z', '2015-03-01T00Z', 0, 7 ],
42    [ '2008-02-29T00Z', '2016-02-27T00Z', 0, 7 ],
43    [ '2008-02-29T00Z', '2016-02-28T00Z', 0, 7 ],
44    [ '2008-02-29T00Z', '2016-02-29T00Z', 0, 8 ],
45    [ '2008-02-29T00Z', '2016-03-01T00Z', 0, 8 ],
46
47    [ '2008-02-28T00Z', '2015-02-27T00Z', 1, 6 ],
48    [ '2008-02-28T00Z', '2015-02-28T00Z', 1, 7 ],
49    [ '2008-02-28T00Z', '2015-03-01T00Z', 1, 7 ],
50    [ '2008-02-29T00Z', '2015-02-27T00Z', 1, 6 ],
51    [ '2008-02-29T00Z', '2015-02-28T00Z', 1, 6 ],
52    [ '2008-02-29T00Z', '2015-03-01T00Z', 1, 7 ],
53    [ '2008-03-01T00Z', '2015-02-27T00Z', 1, 6 ],
54    [ '2008-03-01T00Z', '2015-02-28T00Z', 1, 6 ],
55    [ '2008-03-01T00Z', '2015-03-01T00Z', 1, 7 ],
56    [ '2008-02-29T00Z', '2016-02-27T00Z', 1, 7 ],
57    [ '2008-02-29T00Z', '2016-02-28T00Z', 1, 7 ],
58    [ '2008-02-29T00Z', '2016-02-29T00Z', 1, 8 ],
59    [ '2008-02-29T00Z', '2016-03-01T00Z', 1, 8 ],
60);
61
62use Test::More 0.88;
63
64foreach my $test (@tests) {
65    my ($birth, $event, $march, $age) = @$test;
66    my $got = calculate_age(Time::Moment->from_string($birth),
67                            Time::Moment->from_string($event),
68                            $march);
69    is($got, $age, "calculate_age($birth, $event, $march)");
70}
71
72done_testing();
73