1+ #!/usr/bin/env python3
12"""
2- Algorithm to calculate the absolute age difference between two dates (YYYY-MM-DD) .
3+ Age difference calculator .
34
4- Wikipedia: https://en.wikipedia.org/wiki/Chronological_age
5+ This module provides functions to compute the absolute age difference between
6+ two birthdates in years, months, and days.
7+
8+ Doctest usage:
9+ python3 -m doctest -v maths/age_difference.py
510"""
611
712from __future__ import annotations
813
914from datetime import date
15+ from typing import Tuple
1016
1117
1218def parse_date (dob : str ) -> date :
@@ -25,45 +31,60 @@ def parse_date(dob: str) -> date:
2531 try :
2632 year , month , day = map (int , dob .split ("-" ))
2733 return date (year , month , day )
28- except Exception as e :
29- raise ValueError (f"Invalid date format or value: { dob } " ) from e
34+ except Exception as e : # noqa: BLE001
35+ msg = f"Invalid date format or value: { dob } "
36+ raise ValueError (msg ) from e
37+
3038
39+ def _days_in_previous_month (ref : date ) -> int :
40+ """
41+ Return the number of days in the month before the given date's month.
3142
32- def absolute_age_difference (dob1 : str , dob2 : str ) -> tuple [int , int , int ]:
43+ >>> _days_in_previous_month(date(2020, 3, 5)) # Feb in leap year
44+ 29
45+ >>> _days_in_previous_month(date(2021, 3, 5)) # Feb non-leap
46+ 28
3347 """
34- Returns the absolute age difference as (years, months, days).
48+ first_of_month = ref .replace (day = 1 )
49+ previous_month_last_day = first_of_month - date .resolution
50+ return previous_month_last_day .day
51+
52+
53+ def absolute_age_difference (d1 : str , d2 : str ) -> Tuple [int , int , int ]:
54+ """
55+ Return the absolute difference between two dates as (years, months, days).
56+
57+ Rules:
58+ - Order does not matter
59+ - Uses calendar-accurate borrowing of months and days
60+ - Handles leap years
3561
62+ >>> absolute_age_difference("2000-01-01", "2000-01-01")
63+ (0, 0, 0)
3664 >>> absolute_age_difference("1990-05-10", "1995-07-15")
3765 (5, 2, 5)
66+ >>> absolute_age_difference("1995-07-15", "1990-05-10")
67+ (5, 2, 5)
3868 >>> absolute_age_difference("2000-02-29", "2001-02-28")
3969 (0, 11, 30)
40- >>> absolute_age_difference("2000-01-01", "2000-01-01")
41- (0, 0, 0)
4270 """
43- d1 , d2 = parse_date (dob1 ), parse_date (dob2 )
44-
45- if d1 == d2 :
46- return (0 , 0 , 0 )
71+ a = parse_date (d1 )
72+ b = parse_date (d2 )
4773
48- # Order dates so d1 is always the earlier date
49- if d1 > d2 :
50- d1 , d2 = d2 , d1
74+ # Ensure a <= b
75+ if a > b :
76+ a , b = b , a
5177
52- years = d2 .year - d1 .year
53- months = d2 .month - d1 .month
54- days = d2 .day - d1 .day
78+ years = b .year - a .year
79+ months = b .month - a .month
80+ days = b .day - a .day
5581
56- # Adjust for negative days
82+ # Borrow days if needed
5783 if days < 0 :
5884 months -= 1
59- previous_month = d2 .month - 1 or 12
60- previous_year = d2 .year if d2 .month != 1 else d2 .year - 1
61- days += (
62- date (previous_year , previous_month + 1 , 1 )
63- - date (previous_year , previous_month , 1 )
64- ).days
65-
66- # Adjust for negative months
85+ days += _days_in_previous_month (b )
86+
87+ # Borrow months if needed
6788 if months < 0 :
6889 years -= 1
6990 months += 12
0 commit comments