from typing import Sequence from .dates_age import age from .insurance_age_banding_types import AgeBand, AgeBanding def _is_int(value: object) -> bool: return isinstance(value, int) and not isinstance(value, bool) def age_band(birth_date: str, inception_date: str, bands: Sequence[AgeBand]) -> AgeBanding: """The policyholder's age on the inception date and the band it falls in. Age is completed years by dates.age, so a 29 February birthday ages on 1 March in other years. The bands are checked before use: youngest first, inclusive at both ends, no overlaps, and only the last may be open-ended. An age no band covers is an error, not the nearest band. """ if len(bands) == 0: raise ValueError("bands must not be empty") previous_max = -1 for i, band in enumerate(bands): if not _is_int(band.min_age) or band.min_age < 0: raise ValueError("minAge must be a whole number of 0 or more, received %s" % (band.min_age,)) is_open = band.max_age is None if is_open and i != len(bands) - 1: raise ValueError('only the last band may have no maxAge: "%s" is not last' % (band.label,)) if not is_open and (not _is_int(band.max_age) or band.max_age < band.min_age): raise ValueError('maxAge must be a whole number no less than minAge in band "%s"' % (band.label,)) if band.min_age <= previous_max: raise ValueError( 'bands must be youngest first and not overlap: "%s" starts at %d' % (band.label, band.min_age) ) previous_max = 2 ** 62 if is_open else band.max_age years = age(birth_date, inception_date) for band in bands: if years >= band.min_age and (band.max_age is None or years <= band.max_age): return AgeBanding(age=years, label=band.label) raise ValueError("no band for age %d" % (years,))