DEV Community

Multigrid
Multigrid

Posted on • Originally published at multigrid.ai

Extracting Renewal and Effective Dates From an Insurance Policy

The declarations page prints five dates in a block two inches tall, three of which are frequently the same value and two of which are never interchangeable. Almost every date bug on this document is a confident extraction of the wrong one of the five.

Five dates, one label each

They mean different things and they answer different questions:

  • Effective date (also inception date) — when the policy period begins. Paired with an expiration date, the two together are the policy period.
  • Expiration date — when the period ends. Distinct from a cancellation date, which is a date the period was cut short and which appears on a cancellation endorsement rather than on the original declarations.
  • Issue date — when the carrier produced the document. Frequently after the effective date, because a policy bound on the phone is issued days later. Extracted as the effective date it produces a period that starts after the losses it covers.
  • Renewal date — the date the next period would begin, ordinarily the same instant as the current expiration. On a policy written to renew continuously, this is where the anniversary lives even though the period does not end.
  • Retroactive date — on a claims-made form only, and it can be years before the effective date. See below.

The reason a model confuses them is not that the labels are unclear. It is that on a first-term policy issued on time, effective, issue and the start of the printed period are all the same value, so the extraction looks correct on every sample you check by hand and diverges only on renewals and on backdated bindings — which is to say, on the documents where the answer matters. Extract all five as separate fields even when they agree, and let equality be an observation rather than an assumption.

The policy period does not start at midnight

This is the specific, checkable thing about the document. Standard US commercial declarations state the policy period as running from one date to another at 12:01 A.M. standard time at the address of the named insured, and that convention is printed on the page in small type next to the dates.

Two consequences that a date-only extraction cannot represent. First, a policy expiring on a given date expires at one minute past midnight that morning, not at the end of that day, so a containment check written as “date of loss is on or before the expiration date” wrongly includes the whole of the final day — nearly twenty-four hours of exposure the policy did not carry. Second, the time zone is the insured’s address, not yours and not UTC, so a loss recorded with a timestamp in another zone can land on the wrong side of the boundary.

Store the period as an interval that is closed at the start and open at the end, with the time and the governing location attached, and do the comparison in the insured’s local zone. If you have only a date for the loss and not a time, say so in the output rather than assuming noon; the ambiguity is real and the honest representation of it is a flag, not an invented hour.

The 12:01 convention is not universal. Some forms and some jurisdictions state 12:00 A.M. or midnight, and a manuscript form can say anything. Extract the printed time expression rather than assuming the convention, and treat this page’s description as the common case to verify against the form in front of you.

The retroactive date has no neighbours

On a claims-made form, cover responds to claims first made during the policy period, and the retroactive date bounds how far back the underlying events may reach. It is a date on the declarations page that is unrelated to the policy period, often unrelated to any other date on the document, and frequently printed as a single line in a block of otherwise period-related fields.

For extraction the point is that it is orthogonal. Every heuristic that works on the other four dates — proximity to the period block, plausibility relative to the issue date, being within a year of the effective date — is wrong for this one. A retroactive date eight years before the effective date is completely ordinary on a policy that has been renewed continuously since it was first written, and a validator that rejects dates far from the policy period will reject exactly the correct ones.

Three values it can take, and all three need representing: a specific date, the word “NONE” (which means something quite different from an absent field), and “FULL PRIOR ACTS” or similar wording. A schema with a nullable date field cannot tell those apart. Use a small object with a kind enumeration and an optional date, and record the raw printed text alongside. The same document may also carry a continuity date or an extended reporting period, both separate fields with separate meanings; extract them under their own labels or not at all, and never map an unrecognised date label onto the nearest recognised one.

Endorsements carry their own dates

An endorsement issued mid-term has its own effective date, which is not the policy’s. The endorsement form typically carries a small header block with the policy number, the endorsement effective date and the endorsement number, and that block is easy to miss because it looks like a repeat of the declarations header.

So a policy with a mid-term change has a limit or an exclusion that was one thing for four months and another for eight. Any extraction that produces a single value per field for the policy has thrown that away. The representation that survives it is a list of observations, each with the form number and the effective date it takes hold on — the same structure conflicting limits need, for the same reason, and the same manifest-versus-found join that resolving endorsed exclusions depends on.

Formats, ambiguity, and the missing date

US carrier documents overwhelmingly print month-day-year, and the trouble is the minority that do not, plus every day of the month up to the twelfth being ambiguous in isolation. A batch containing documents from more than one source cannot be parsed with a single format string. Three practical rules:

  • Parse a batch, not a value. A policy prints several dates. If one of them is unambiguous — a day above twelve — it fixes the order for all of them on that document. This resolves the great majority of cases without a guess.
  • Keep the raw string. Always. A stored ISO date with no raw text is unrecoverable when you discover the order was wrong; with the raw text you re-derive the whole corpus in one pass.
  • Two-digit years are a separate problem. An older form printing a two-digit year needs a pivot rule, and the right pivot is the policy’s own context rather than a fixed century boundary.

Finally, the absent date. Some policies are written to continue until cancelled and carry no expiration date at all; on those, an extraction that returns null is correct and a pipeline that requires an expiration date will either reject the document or invent one. Make the field nullable and add a separate boolean for a continuous policy, so that “no expiration date because there is none” and “no expiration date because the extraction failed” are different states in your data. They are different findings, and only one of them needs a person.

Related

Top comments (0)