Module 6: Freshness Volume And Lineage

A fixed reference clock, never `datetime.now()`

Description

Lesson 2 confirmed freshness needs a single question, asked once, about the whole file: how much time passed between the most recent data and the moment of review? This lesson stops on that question's second half — "the moment of review" — because it hides a design decision that, if made badly, breaks something much bigger than the freshness check: it breaks the possibility of reproducing any result in this guide. PIPELINE_RUN_AT isn't a style detail. It's the difference between a check you can trust and one that lies differently every time it runs.

Connection to the module. Lesson 4 is going to write check_freshness(df, run_at=PIPELINE_RUN_AT, sla_hours=24) — notice the run_at parameter. This lesson explains, with executed evidence, why that parameter exists, why it always receives a literal constant, and what exactly would break if a call to datetime.now() sat there instead.

A complete analogy: the postmark's date, not the moment you opened the envelope

When a certified letter arrives late, how do you decide whether it really arrived late? You don't look at the time the recipient finally opened it — that time depends on whether they were on vacation, whether they checked the mail that day, whether the letter sat unopened in a drawer for a week. You look at the postmark: the date the post office stamped on the envelope at the exact moment it was sent, printed permanently, which doesn't change no matter when someone finally decides to open the envelope. The postmark is a fixed fact of the past. The time the letter gets opened is a present-tense variable, depending on who looks and when.

A freshness check that uses datetime.now() is, without realizing it, looking at the envelope's opening time instead of the postmark — and worse, it's looking at that time every time someone reopens the same envelope. Run the check today, and it compares against "right now, today." Run the exact same check tomorrow, on the same file, with no change to code or data, and it compares against "right now, tomorrow" — a different number, a possibly different verdict, with nothing in the data having changed by a single bit. PIPELINE_RUN_AT, by contrast, is the postmark: a fixed date, decided once, that doesn't move no matter when — or how many times — someone reruns the check on the same file.

Worked example: the same function, three different fixed references

Before building lesson 4's complete check_freshness(), it's worth confirming the central principle with a simpler function — the same arithmetic freshness_preview.py already used in module 1, lesson 5 — but this time fed with three different run_at values, each a literal constant, none taken from the system clock.

# clock_injection_demo.py
from datetime import datetime

LATEST_ORDER_TS = "2026-08-14T09:25:00"  # S04's most recent order_ts, fixed
SLA_HOURS = 24


def hours_since(latest_ts: str, run_at: str) -> float:
    """Difference in hours between run_at and latest_ts. run_at always arrives as an argument -- never datetime.now()."""
    latest = datetime.fromisoformat(latest_ts)
    run_at_dt = datetime.fromisoformat(run_at)
    return round((run_at_dt - latest).total_seconds() / 3600, 2)


scenarios = {
    "if the pipeline had run the same day, at 20:00": "2026-08-14T20:00:00",
    "right at the 24-hour SLA's limit": "2026-08-15T09:25:00",
    "the real moment Kiosko reviewed it (PIPELINE_RUN_AT)": "2026-08-16T09:00:00",
}

for label, run_at in scenarios.items():
    h = hours_since(LATEST_ORDER_TS, run_at)
    status = "PASS" if h <= SLA_HOURS else "FAIL"
    print(label)
    print(f"  run_at={run_at} -> hours_since={h} -> {status}\n")

What to expect. Running python3 clock_injection_demo.py, the output is exactly this:

if the pipeline had run the same day, at 20:00
  run_at=2026-08-14T20:00:00 -> hours_since=10.58 -> PASS

right at the 24-hour SLA's limit
  run_at=2026-08-15T09:25:00 -> hours_since=24.0 -> PASS

the real moment Kiosko reviewed it (PIPELINE_RUN_AT)
  run_at=2026-08-16T09:00:00 -> hours_since=47.58 -> FAIL

Notice something important about this result: hours_since(), the function itself, never changed. No line of its code knows anything about which scenario is "the real one" and which is hypothetical — it only receives run_at as an argument and does a subtraction. The three different results — 10.58 hours and PASS, 24.0 exact hours and PASS, 47.58 hours and FAIL — don't come from the function behaving differently. They come, exclusively, from which value you passed as run_at. This is the property that makes a check trustworthy: the result depends only on its arguments, never on the actual calendar moment someone decided to run the script.

The anti-pattern, shown (and never executed)

It's worth seeing, in code, exactly what this guide forbids — not as an abstract rule, but as a concrete line that would break everything else:

# ANTI-PATTERN -- this fragment NEVER runs in this guide.
# Shown only so it's clear, in code, exactly what "datetime.now() forbidden" means.
from datetime import datetime

def check_freshness_BROKEN(latest_ts: str, sla_hours: int) -> str:
    now = datetime.now()  # <-- the problem lives here
    latest = datetime.fromisoformat(latest_ts)
    hours = (now - latest).total_seconds() / 3600
    return "PASS" if hours <= sla_hours else "FAIL"

This function has no syntax error, and in a superficial sense, "works" — it runs with no exception raised. The problem isn't that it fails: it's that its output can't be predicted without knowing, to the second, the exact instant someone runs it. There's no possible "What to expect" block for this function, because its result today and its result an hour from now, run on the same latest_ts, can be different — and will be, for sure, as soon as enough time passes between one run and the next. This entire guide rests on the promise that any code block, run by any person, at any moment, produces exactly the same output — that promise becomes impossible to keep the moment datetime.now() (or time.time(), its seconds-since-1970 equivalent) enters any function feeding a result shown in this guide.

Diagram: two ways of getting "now"

flowchart TD
    subgraph BAD["datetime.now() -- forbidden in this guide"]
        B1["check_freshness_BROKEN(latest_ts, sla_hours)"]
        B2["now = datetime.now()"]
        B3["result depends on WHEN\nthe script runs"]
        B1 --> B2 --> B3
    end
    subgraph GOOD["injected run_at -- this guide's pattern"]
        G1["check_freshness(df, run_at, sla_hours)"]
        G2["run_at is an argument --\nPIPELINE_RUN_AT, a constant"]
        G3["result depends ONLY\non the arguments received"]
        G1 --> G2 --> G3
    end
    B3 -.->|"impossible to reproduce\nbyte for byte"| X["No possible\n'What to expect' block"]
    G3 -.->|"reproducible\nbyte for byte, always"| Y["Lesson 4's\n'What to expect' block"]

Going deeper: why this matters well beyond this guide

This lesson's principle has a name in software engineering broader than this guide: injecting the "clock" as a parameter, instead of a function reading it directly from the operating system, is what makes any time-dependent function testable. Think about what would happen if check_freshness() called datetime.now() internally, and someone on Kiosko's team wanted to write an automated test confirming "this check must fail when the file arrives late." That test, written today, would pass today — and could fail with no apparent reason six months later, not because the code changed, but because real time moved forward and shifted datetime.now()'s answer. A team debugging that failure would waste hours hunting for a bug that doesn't exist: the code never broke, the real clock just kept running.

This also explains something worth previewing from lesson 4: run_at isn't just "easier to test" — it's the only honest way to answer a real audit question. If someone at Kiosko asks, months after the incident, "did S04's file violate the SLA on the day we reviewed it?", the answer needs to be reconstructible exactly the same, no matter when that question gets asked. With PIPELINE_RUN_AT = "2026-08-16T09:00:00" as a constant, today's answer and next year's answer are identical: 47.58 hours, FAIL. With datetime.now(), there wouldn't even be a fixed answer to reconstruct — every person who asked, at a different moment, would get a different number for the same historical event.

Common mistakes

Thinking datetime.now()'s problem is "too little precision," and that rounding to the date (with no time) fixes it. What happens: someone, seeing the reproducibility problem, proposes using datetime.now().date() instead of the full datetime.now(), assuming dropping the time makes the result more stable. Why it happens: it seems like the problem is "too much changing precision," and less precision feels like a reasonable fix. How to spot it: ask yourself whether datetime.now().date() gives the same result run twice at different moments — yes, as long as both runs happen on the same calendar day, but not if they happen on different days, which is exactly the same problem, just with a wider time window before it shows up. How to fix it: the problem was never precision — it was depending on the operating system's clock, at any granularity. The solution is always the same: replace reading the clock with an injected parameter, whether you need seconds, hours, or just the calendar day.

Using an environment variable or a config file read at runtime, and thinking that already counts as a "fixed clock." What happens: someone replaces datetime.now() with, say, os.environ["RUN_AT"], read from the operating system's environment at the moment the script runs, and considers the problem solved because the literal words datetime.now no longer appear in the code. Why it happens: the literal absence of the forbidden function feels like compliance with the rule. How to spot it: ask yourself whether two runs of the same script, at different moments, with nothing in the code changed, could produce different results — if nobody fixes that environment variable's value beforehand, and something updates it automatically with the real time before every run, the problem is exactly the same, just with one more layer of indirection. How to fix it: what makes PIPELINE_RUN_AT trustworthy isn't that it's a variable instead of a function call — it's that its value is a literal constant, written in the code or in a versioned file, decided once, never recalculated automatically from the real clock at any point along the way.

Applying this rule to timestamps that come inside the data too (order_ts). What happens: someone, excited by the "never use the real clock" rule, starts questioning why order_ts — a real column in S04's file — is allowed to have specific dates and times, if "everything related to time is forbidden." Why it happens: the rule gets over-generalized, without distinguishing two very different things. How to spot it: ask yourself exactly what's forbidden — it isn't dates and times themselves (order_ts, PIPELINE_RUN_AT, SLA_DEADLINE are all dates and times, and all appear freely throughout this guide), it's reading the operating system's clock at the moment the code runs (datetime.now(), time.time()). How to fix it: any date or time that's data (something that already happened, stored in a file or declared as a constant) is perfectly fine. What's forbidden is specifically the source: never asking the operating system "what time is it right now?" inside a function whose result this guide needs to reproduce.

Exercises

Exercise 1 — Find the exact point where the verdict changes. Using this lesson's hours_since(), search, by trial and error (testing different run_at values on the same 2026-08-15), for the exact minute the verdict flips from PASS to FAIL for SLA_HOURS = 24.

See solution
for hour in [9, 10]:
    for minute in [0, 24, 25, 30]:
        run_at = f"2026-08-15T{hour:02d}:{minute:02d}:00"
        h = hours_since(LATEST_ORDER_TS, run_at)
        status = "PASS" if h <= SLA_HOURS else "FAIL"
        print(f"{run_at} -> hours_since={h} -> {status}")

The exact point is 2026-08-15T09:25:00 (hours_since=24.0, still PASS, because the check uses <=) — one minute later, 2026-08-15T09:26:00, already gives hours_since=24.02 and FAIL. This confirms, with evidence, that the SLA's limit is inclusive: exactly 24 hours still counts as on time, and the verdict only flips to FAIL the instant that mark gets exceeded, not a second before.

Exercise 2 — Rewrite hours_since() so it receives datetime objects instead of ISO text strings. Modify the function's signature so it receives latest_ts: datetime and run_at: datetime directly, with no internal datetime.fromisoformat(). Is it still reproducible, with this lesson's same guarantee?

See solution
def hours_since_v2(latest_ts: datetime, run_at: datetime) -> float:
    return round((run_at - latest_ts).total_seconds() / 3600, 2)

result = hours_since_v2(
    datetime.fromisoformat(LATEST_ORDER_TS),
    datetime.fromisoformat("2026-08-16T09:00:00"),
)
print(result)

Expected output: 47.58 — identical to the original result. Yes, it's still just as reproducible: what guarantees reproducibility was never the parameter's data type (str versus datetime), it was that both values arrive as explicit arguments, with the function itself never consulting the system clock anywhere in its own body. check_freshness(), in lesson 4, in fact receives run_at as an ISO text string, just like this original version — a convenience decision (PIPELINE_RUN_AT is already defined as text since module 1), not a requirement of the reproducibility rule itself.

Exercise 3 — Argue why a fixed PIPELINE_RUN_AT is still useful even in a real production system, where the pipeline really does run "now." In 2-3 sentences, considering that a real production data system does need, at some point, to know what time it really is, explain where that real-clock reading should live, if not inside check_freshness().

See solution

In a real production system, the real clock does get read — but in a single place, at the pipeline execution's start (for example, an orchestrator like Airflow recording the exact time a run started), never scattered inside each individual business function. That time gets captured once, gets stored as a fixed value (exactly the role PIPELINE_RUN_AT plays in this guide), and that fixed value is what gets passed as an argument to check_freshness() and to any other function that needs to know "what time is it now." The difference isn't "never read the real clock" — that would be impossible in a system that really runs in production — it's reading it once, at a single entry point, and from then on treating it as a fixed piece of data passed explicitly, the exact same principle this lesson's Going deeper section already made about automated testing and auditing.

Summary and next step

In this lesson you confirmed, with three really-executed scenarios, that a well-designed freshness function produces results that depend only on its arguments — never on the real moment someone runs it. You saw, in code deliberately never run, exactly what datetime.now() would break if it appeared inside any function in this guide: the very possibility of writing a trustworthy "What to expect" block. And you connected this principle to something broader than this guide: why injecting the clock as a parameter is what makes any time-dependent system testable and auditable, in any software engineering context.

Before moving on you should be able to: explain, in your own words, why this lesson's hours_since() produced three different results with its code never changing; and name, without looking back, exactly what's forbidden (reading the operating system's clock) versus what's allowed (any date or time that's fixed data, like order_ts or PIPELINE_RUN_AT).

You have both complete arguments: freshness is a file-level property (lesson 2), and its "now" reference has to be an injected constant (this lesson). Lesson 4 brings both together into a single real function, written with Polars, really run on S04 — and for the first time in this guide, you're going to see a file-level check fail with executed evidence, not just calculated by hand.

Resources

  • Python — official datetime documentation (datetime.now(), datetime.fromisoformat(), and the distinction between reading the system clock and building an object from a fixed value). docs.python.org/3/library/datetime.html. In English.
  • Module 1, lesson 5, of this same guide — the source of the original freshness_preview.py pattern, the first time this guide calculated delay hours with fixed dates. src/guides/data-reliability-and-governance-guide/workbook/module-01-when-green-does-not-mean-correct/en/05-meet-s04-kioskos-fourth-store.md. In English.
  • This guide's DESIGN — the hard rule of "random, datetime.now(), time.time() forbidden" in any code that feeds a "What to expect" block, in effect since this ecosystem's nine guides were designed. src/guides/data-reliability-and-governance-guide/DISENO.md. In Spanish.