Let's talk
product

Certificate expiry: the one ending today was on no list

A certificate expiring today was on no dashboard list because three rules treated the boundary differently. How expiry tracking should work, and a test.

· · updated

Diagram: A credential ending today was on no list. Seven-day and thirty-day tiles: ending after today; Expired counter: ended before today; Action centre: from today, next fifteen days; Credential list: user's window, no status check; Today is counted nowhere

Certificate expiry tracking should treat the day a certificate ends the same way on every screen, and the answer that matches how people think is that today counts as expiring, not expired, because the person is covered until the end of the day. In the system we describe, a certificate ending today was counted on none of the dashboard’s numbers, because each rule compared dates differently. The first thing to check is to create a certificate that ends today and see where it appears.

Here is what happened.

A certificate that runs out today is the single most urgent line in a compliance system. It is the one where somebody is about to walk onto a shift without a valid credential, and it is the one the software exists to surface.

In this system it appeared on no counter at all. The two panels on the same screen also gave numbers that could not be reconciled, and nobody reported that as a fault, because a compliance dashboard is where people assume a discrepancy means something they have not understood yet. The cost is exactly the risk the screen exists to remove: a person working without cover, unflagged.

What was actually going on

The dashboard has a tile for credentials expiring within seven days and another for thirty. Both counted only credentials ending after today, so today was excluded. The expired counter counted only those that ended before today, so today was excluded there too. Today is neither before today nor after it, so a credential ending today satisfied none of the three and vanished from every number on the page.

A separate panel on the same screen, the action centre, used its own rule: from today, for the next fifteen days. The credential list had a fourth, where the user supplies the window and today is included. Unlike the other three, it never checked the status of the credential, so one already revoked, with a future end date, still appeared in the list of things expiring soon.

The window lengths of seven, fifteen and thirty days are fine, because they answer different questions for different people: what is urgent this week, what needs booking onto training, what the manager should know this month. The treatment of the boundary is not a legitimate difference. It is one decision made three times by three people who each typed whichever comparison came to hand. Two typed “after today” and one typed “today or after”.

Underneath sat a bigger question: what does “today” mean? Every rule used the database server’s idea of the date. Each site has a time zone stored on its record and nothing reads it. The date-range reader hard-codes a fixed offset, so a user in one part of the world asking for “today” gets a different day than a colleague in another, on the same screen.

The demonstration data hid all of it. The seeding script creates its “expiring soon” credentials with end dates between seven and fifteen days ahead. That is exactly the action centre’s window and exactly outside the seven-day tile’s window, so in every demonstration the action centre looked full and the tile read zero or close to it. Nobody planned that. Whoever tuned the data made the action centre look populated, saw it populated and stopped.

What we changed

We fixed the boundary rules, which took an afternoon. For expiry, the answer that matches how people think is that today counts as expiring, not expired, because you are still covered until the end of the day.

The rule we set is to write each of these definitions once, name it and reuse it, and to make the choice visible to the next person so it is identical in every query. Test and demonstration data should be generated from the real range of dates, including today, yesterday and tomorrow, so that screens disagree in front of you instead of hiding it.

What it did not fix

The time zone on each site is still not read. Making expiry boundaries local to a site means deciding what a plain date means for an organisation that works in two countries, and that is a product decision we do not yet have an answer to.

How to check your own expiry tracking

  1. Create a certificate with today’s date as its end date and watch where it appears on every screen. It should be on an expiring list, and on none of the expired ones.
  2. Ask what happens on the day a credential ends: expiring, expired, or nothing.
  3. Ask whose “today” the system uses. A person in another time zone may be covered, or not, for the shift they are about to start.
  4. Ask whether every list and counter uses one named definition of “expiring”, or whether each was written separately.
  5. Distrust a demonstration where every panel looks comfortably full.

Where this ends up

Expiring and expired are the two states Sazinga Comply exists to keep straight, and the row that runs out today is the one somebody is about to walk onto a shift without.

This came out of building Sazinga Comply

Know what is due, what is evidenced and what has expired. The problem above is one we met while building it, and what we did about it is in the product.

If you run something like this, there is one thing you can do without a call: send one inspection checklist you use today.