from .dates_add_days import add_days, days_between, parse_iso_date from .health_gestational_age_types import GestationalAge #: Naegele's rule: 280 days (40+0 weeks) from the LMP for a 28-day cycle. _TERM_DAYS = 280 #: 44+0 weeks: beyond this the dates are almost certainly wrong. _MAX_DAYS = 308 def gestational_age(lmp_date: str, on_date: str, cycle_length_days: int) -> GestationalAge: """Gestational age and estimated due date from the last menstrual period. A cycle longer or shorter than 28 days moves ovulation, so both are shifted by the difference, as the NHS due date calculator does. """ c = cycle_length_days if isinstance(c, bool) or not isinstance(c, int) or c < 20 or c > 44: raise ValueError("cycleLengthDays must be a whole number from 20 to 44, received %r" % (c,)) parse_iso_date(lmp_date) parse_iso_date(on_date) shift = c - 28 total_days = days_between(lmp_date, on_date) - shift if total_days < 0: raise ValueError("onDate %s is before the adjusted start of pregnancy" % (on_date,)) if total_days > _MAX_DAYS: raise ValueError("gestational age of %d days is more than 44+0 weeks: check the dates" % (total_days,)) return GestationalAge( weeks=total_days // 7, days=total_days % 7, total_days=total_days, due_date=add_days(lmp_date, _TERM_DAYS + shift), )