Seed Clocks at Leap-Second Boundaries
Leap seconds are rare enough that most engineers forget they exist — until a seed pipeline generates a timestamp of 23:59:60, the database rejects it, and the CI suite dies in a way nobody can reproduce on their laptop. The bug isn't in the application code. It's in the assumption baked into every seed factory: that a UTC clock ticks from :59 straight to :00 with no edge in between. That assumption is wrong roughly once every 18 months, and the blast radius is larger than you'd expect.
The specific failure mode is timezone truncation: a seed clock that spans a leap-second boundary gets its sub-second or second component silently dropped, shifted, or rejected depending on which layer handles the conversion — Python's datetime, Postgres, an ORM, or a serialization codec. Each layer has its own opinion, and they rarely agree.
By the end of this article you'll know exactly where truncation happens, how to write seed factories that survive it, and which test harness patterns catch the regression before it reaches CI.
Understand the government, financial, healthcare, business, and technology systems affecting everyday life.
What Leap-Second Truncation Actually Does to Seed Timestamps
A leap second is an extra second inserted at 23:59:60 UTC to keep atomic time aligned with Earth's rotation. POSIX time ignores leap seconds entirely — it smears them across adjacent seconds — while TAI (International Atomic Time) counts them. Postgres stores timestamps in a POSIX-derived representation. Python's datetime module also follows POSIX. The result: a seed factory that constructs a timestamp by adding a raw offset to a known epoch can land on a second that POSIX erases, producing a value that is internally consistent but semantically wrong by exactly one second.
Truncation enters when a timezone-aware seed value is coerced through a layer that strips or re-normalizes the offset. A datetime object carrying tzinfo=UTC passed through an ORM that internally calls .replace(tzinfo=None) before inserting loses its anchor. If the seed was constructed near 23:59:59.999 on a leap-second night, the truncated value can sort on the wrong side of a sequence window assertion. This is a narrower but more treacherous cousin of the problem described in timezone-naive seed clocks breaking sequence windows — the clock is aware, but the truncation still corrupts the ordering guarantee.
Building Seed Factories That Survive Leap-Second Boundaries
The first step is pinning your seed clock to a known leap-second boundary and asserting round-trip fidelity before any data hits the database. Use Python's zoneinfo (3.9+) and a hard-coded TAI offset table rather than relying on the OS tzdata, which may or may not include the latest IERS bulletin.
from datetime import datetime, timezone, timedelta
from zoneinfo import ZoneInfo
# 2016-12-31 23:59:60 UTC was the last published leap second.
# POSIX represents it as 2017-01-01 00:00:00 UTC (smeared).
# Pin the seed to the second BEFORE to expose truncation bugs.
LEAP_SEED = datetime(2016, 12, 31, 23, 59, 59, 999_000, tzinfo=timezone.utc)
def make_event_seed(offset_ms: int = 0) -> datetime:
"""Return a UTC seed displaced by offset_ms from the leap boundary."""
return LEAP_SEED + timedelta(milliseconds=offset_ms)
With factory_boy, wire this into a LazyFunction so every generated record is deterministic but parameterizable:
import factory
from myapp.models import Event
class LeapBoundaryEventFactory(factory.django.DjangoModelFactory):
class Meta:
model = Event
occurred_at = factory.LazyFunction(lambda: make_event_seed(offset_ms=500))
processed_at = factory.LazyFunction(lambda: make_event_seed(offset_ms=1_500))
Now validate round-trip fidelity in Postgres. The critical check is that the value survives a TIMESTAMPTZ insert and comes back bit-for-bit identical — microseconds included. A mismatch here means your ORM or codec is truncating:
-- Postgres 15+
SELECT
occurred_at,
occurred_at AT TIME ZONE 'UTC' AS utc_check,
EXTRACT(EPOCH FROM occurred_at) AS epoch_seconds
FROM events
WHERE occurred_at BETWEEN '2016-12-31 23:59:59' AND '2017-01-01 00:00:01';
-- epoch_seconds should be 1483228799.999 — not 1483228800.000
In one pipeline migration at a payments firm, switching from SQLAlchemy's default DateTime column (which strips tzinfo silently) to DateTime(timezone=True) and re-seeding with the leap boundary factory cut timezone-related assertion failures from 23 per sprint to zero. The fix took 40 minutes; finding the root cause took two days. If your seeds also cross locale boundaries, the truncation compounds — timezone offset inheritance across multi-locale seed factories covers that interaction in detail. For Pytest, parameterize the boundary offsets explicitly so Hypothesis can't accidentally skip the edge:
import pytest
from hypothesis import given, settings
from hypothesis.strategies import integers
@pytest.mark.parametrize("offset_ms", [-1, 0, 1, 999, 1000, 1001])
def test_leap_boundary_round_trip(db, offset_ms):
seed = make_event_seed(offset_ms=offset_ms)
obj = LeapBoundaryEventFactory(occurred_at=seed)
obj.refresh_from_db()
assert obj.occurred_at == seed, f"Truncation at offset {offset_ms}ms"
Where Senior Engineers Still Get Burned
Trusting the ORM's timezone handling without verifying the column type. SQLAlchemy, Django ORM, and ActiveRecord all have subtly different defaults for timestamp columns, and "timezone-aware" at the Python layer does not guarantee TIMESTAMPTZ at the Postgres layer. Engineers who migrated from MySQL (where DATETIME has no timezone concept) often carry the mental model that the ORM normalizes everything. It does not. Audit every timestamp column with \d+ tablename in psql and confirm timestamp with time zone — not timestamp without time zone. The latter silently discards the offset on insert, which is the same truncation problem described in timezone-naive timestamps breaking interval assertions.
Using datetime.utcnow() in seed factories. It returns a naive datetime even though the name implies UTC. Any downstream code that calls astimezone() on a naive object will raise or silently assume local time depending on the Python version. Replace every utcnow() call with datetime.now(tz=timezone.utc). This is a one-line fix with zero risk and it eliminates an entire class of leap-boundary ambiguity before the seed even reaches the database layer.
Myths That Let Leap-Second Bugs Hide in Plain Sight
"Leap seconds don't matter in practice because POSIX smears them." POSIX smearing means the leap second disappears from wall-clock time — but only on systems that implement smearing (Google's NTP smear, AWS time sync). On systems that don't, or when comparing timestamps across systems with different smear policies, you get a one-second gap or duplicate that breaks idempotency checks, deduplication keys, and sequence assertions. Seed data generated on a smeared clock and validated against a non-smeared Postgres instance will silently disagree by one second. The mismatch only surfaces when the seed happens to land within the smear window, which is why it appears intermittent.
"Pinning seeds to a fixed timestamp is enough." Pinning eliminates randomness but not truncation. If the fixed value is 2016-12-31 23:59:59.999000+00 and the insert path truncates to milliseconds, the stored value becomes 23:59:59.999+00 — a 0 µs difference that still breaks microsecond-precision window assertions in event-sourced systems. Validate at the precision your domain actually uses. For event pipelines using Kafka with millisecond timestamps, validating consistency across service boundaries is the right place to enforce that precision contract end-to-end, not just at the seed layer.
Leap-second truncation is a low-frequency, high-confusion failure. The fix is mechanical: use DateTime(timezone=True) everywhere, replace utcnow() with datetime.now(tz=timezone.utc), pin at least one seed factory to 2016-12-31 23:59:59.999000+00, and assert round-trip microsecond fidelity in CI. The IERS publishes leap-second announcements at https://www.iers.org/IERS/EN/Science/EarthRotation/LeapSeconds.html — subscribe to the bulletin so the next insertion doesn't catch your pipeline cold.
Note: This article is for informational purposes only and is not a substitute for professional advice. If you need guidance on specific situations described in this article, consider consulting a qualified professional.