leap-second-reference
Pure-reference catalog of leap-second mechanics and the bugs they cause: the 23:59:60 UTC insertion (announced ~6 months ahead by IERS Bulletin C; 27 inserted 1972-2016; abolished by 2035 per CGPM 2022), the Google/AWS leap-smear alternative, and the four bug classes a real insertion exposes - time_t non-monotonicity, negative durations, NTP cascading, and cross-node clock skew - each with a monotonic-clock fix and a freezegun simulation. Use when auditing time-sensitive code (financial timestamping, distributed logs, NTP-driven schedulers) for second-by-second progress assumptions; for the far more common daylight-saving-time transition hazards, use dst-transition-reference instead.
Install with skills.sh (any agent)
npx skills add testland/qa --skill leap-second-referenceleap-second-reference
Overview
A leap second is an extra second (23:59:60 UTC) inserted into the day to keep UTC within 0.9 seconds of UT1 (astronomical time). Per IERS Bulletin C (datacenter.iers.org/data/latestVersion/bulletinC.txt (opens in new window)), Bulletin C is issued every six months, either to announce a time step in UTC or to confirm that there will be no step at the next possible date - so a leap second gets roughly six months of notice.
Important - 2035 abolition: Per the 27th CGPM resolution (2022), leap seconds will be abolished by 2035, with the gap between UTC and UT1 allowed to grow. Existing leap seconds (27 inserted between 1972 and 2026) remain in the historical record.
How to use this reference
When to use
The mechanics
| Property | Detail |
|---|---|
| Frequency | Irregular; announced by IERS Bulletin C |
| Insertion point | Last second of UTC June 30 or December 31 |
| Direction | Almost always +1 (insert); the spec allows -1 but never used |
| Wire format | 23:59:60 UTC (a real 61st second of the minute) |
| POSIX time_t | Does not include leap seconds; time_t jumps backward by 1 or stalls |
| NTP | NTP messages signal upcoming leap; clients handle it differently |
Absorption strategy and history
Whether a host inserts a real 23:59:60 or smears the second over 24 hours (Google / AWS), plus the full record of the 27 leap seconds inserted between 1972 and 2026 (most recent 2016-12-31), lives in references/smear-strategies-and-history.md. The smear is operationally invisible to applications; a real insertion exposes the discontinuity that the bug classes below exploit.
Bug classes
time_t non-monotonicity
POSIX time_t is defined as seconds since epoch with 86400 seconds per day - no leap seconds. On a leap-second insertion, the system clock either:
Code that relies on "1 second of CPU time = 1 second of clock" breaks.
Negative durations
start = time.time()
do_work() # crosses a leap second
elapsed = time.time() - start
assert elapsed >= 0 # FAILS on a real-inserted leap secondUse monotonic clocks (time.monotonic(), clock_gettime(CLOCK_MONOTONIC)) for duration measurement. Monotonic clocks ignore wall-clock leap-second insertions.
NTP cascading
NTP messages carry a leap-second indicator. Different OS versions handle the indicator differently - Linux historically had bugs where the leap insertion caused kernel hangs (2012 incident).
Distributed-systems clock skew
If different nodes handle leap differently (one steps, one smears), clock skew between them temporarily exceeds 1 second. Per AWS docs (aws.amazon.com/blogs/aws/look-before-you-leap-the-coming-leap-second-and-aws (opens in new window)), AWS uses leap-smear specifically to avoid this.
Testable behaviours
| Behaviour | Test |
|---|---|
| Duration calculation uses monotonic clock | time.monotonic() consistent across leap |
| Sortable timestamps don't collide | Even with stalled time_t, sequence-numbers / nanosecond resolution avoids equal timestamps |
| Log timestamps don't skew | Compare logs across services during a known leap event |
Cron jobs at 00:00:00 UTC of leap day | Fires exactly once |
| Financial timestamping | Per-trade microsecond resolution + monotonic counter |
Simulating a leap second in tests
# Override the system clock to simulate
import freezegun
@freezegun.freeze_time('2016-12-31 23:59:59 UTC')
def test_leap_handling():
t1 = time.time()
time_pass_one_second() # mock
t2 = time.time()
assert t2 - t1 == 1 # ideal; on real leap = 0 (stalled)Note: most test libraries don't simulate actual leap-second mechanics - they're a real OS-level event. Production tests require an OS test that replays NTP leap-second indication.
Worked example - a negative-duration assertion
Goal: prove a duration-measurement code path stays non-negative across a real-inserted leap second.
The caveat from "Simulating a leap second in tests" still applies: freezegun can't replay the OS-level leap indication, so this asserts the code's clock choice, not the kernel's leap behaviour - a real leap needs an OS-level test.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
time.time() - start for duration | Wall-clock; affected by leap | Use time.monotonic() |
Asserting time.time() < time.time() adjacent calls | Trips on stalled time_t | Use sequence numbers + monotonic |
| Treating UNIX time_t as continuous | Historical leap insertions broke this | Per |
| IANA leap-seconds.list (opens in new window) | ||
| No monitoring during announced leap | Latent bugs surface in prod | Pre-leap rehearsal + monitoring |
| Per-second metrics with stale timestamps | Loss of one second of data | Sub-second granularity |
| Hardcoding 86400 in "seconds-per-day" | True only sometimes | Calendar arithmetic |
| Assuming all servers smear | Some don't | Verify per-host strategy |
Limitations
References
Leap-smear strategies and the historical leap-second record
View source (opens in new window)Leap-smear strategies and the historical leap-second record
Deep reference for leap-second-reference SKILL.md. Consult when comparing how platforms absorb a leap second and when you need the factual insertion history.
Leap-smear
Per Google's "Time, technology and leaping seconds": googleblog.blogspot.com/2011/09/time-technology-and-leaping-seconds.html (opens in new window), Google "smears" the leap second rather than stepping the clock. The published standard is a "24-hour linear smear from noon to noon UTC" (Google Public NTP: Leap Smear (opens in new window)), adding a small fraction to each second so the total adds up to 1 second of slowdown, with no actual 23:59:60.
Leap second strategy comparison:
| Approach | What happens |
|-----------------------|------------------------------------|
| IERS spec | 23:59:60 UTC inserted (real second)|
| Linux kernel default | Real insertion; time_t stalls 1s |
| Google leap-smear | Distributed over 24h |
| AWS leap-smear | Linear over 24h |
| NTP "step" | Jump 1s; subsequent time_t differs |
The smear is operationally invisible to applications; the spec exposes the discontinuity.
Historical leap seconds
Per IERS, 27 leap seconds were inserted between 1972 and 2026. Most recent: 2016-12-31 23:59:60 UTC. None have been added since: IERS Bulletin C 72 (6 July 2026) states "from 2017 January 1, 0h UTC, until further notice : UTC-TAI = -37 s" and that "NO leap second will be introduced at the end of December 2026" (datacenter.iers.org, Bulletin C (opens in new window)). None expected before 2035 abolition.
Related skills
dotnet-faketime
Wraps .NET's TimeProvider abstraction (System.TimeProvider, introduced .NET 8) and FakeTimeProvider from Microsoft.Extensions.TimeProvider.Testing: SetUtcNow, Advance, AutoAdvanceAmount, CreateTimer, Delay, and the pre-.NET-8 ISystemClock migration path. Use when testing C# or F# code that reads the current time, uses timers, or awaits Task.Delay.
dst-transition-reference
Pure-reference catalog of Daylight Saving Time (DST) transition patterns and their canonical bug classes. Covers the spring-forward (skipped hour: 02:00 → 03:00 local) and fall-back (repeated hour: 02:00 → 01:00 local) transitions, the historical irregularity of DST (different jurisdictions, transitions on different dates, some regions abolish DST or never adopted it), the IANA timezone database (tz / Olson DB) as the canonical source, and the testable behaviors DST creates (duplicate / missing local timestamps, cron jobs that fire 0 or 2 times, billing periods that miss / double-count, recurring meetings on transition days). Per-jurisdiction DST-rule tables and refreshable per-region test-data fixtures live in references/. Use when designing or auditing time-handling code or test cases.
freezegun-python
Wraps freezegun (github.com/spulec/freezegun), the Python time-mocking library: @freeze_time decorator / context manager, freeze_time(...).start() + stop(), tick / move_to / tz_offset, and integration with datetime.now / time.time / time.localtime. Use when testing Python code that calls datetime / time.
iso-8601-vs-rfc-3339-reference
Pure-reference catalog of the ISO 8601 vs RFC 3339 distinction. Covers the relationship (RFC 3339 is a strict subset of ISO 8601 designed for internet protocols), the syntactic differences (RFC 3339 disallows ISO 8601's '+02' offset short-form requires '+02:00'; RFC 3339 mandates a date-time separator T or space; ISO 8601 allows much more), the canonical date-time string format (YYYY-MM-DDTHH:MM:SS[.fff]±HH:MM or Z), per-language parser behaviour (Python isoformat, Java Instant.parse, JS Date.parse non-spec), and serialisation rules for APIs. Use when choosing a wire format, parsing third-party datetimes, or auditing time-string handling.
jest-fake-timers
Wraps Jest's built-in modern fake-timers (built on @sinonjs/fake-timers since Jest 27): jest.useFakeTimers(), jest.setSystemTime(), jest.advanceTimersByTime(), jest.runAllTimers(), and jest.useRealTimers() for selective restoration. Use when testing JS/TS code in Jest where setTimeout / setInterval / Date / Date.now need deterministic control.
libfaketime-c
Wraps libfaketime (github.com/wolfcw/libfaketime), the LD_PRELOAD library that fakes the clock for any binary by intercepting time() / gettimeofday() / clock_gettime(). Covers absolute-date mode (FAKETIME='2026-12-31 23:59:00'), relative offset (FAKETIME='-1d'), advance-rate (FAKETIME='@2026-12-31 23:59:00 x5' for 5x speed), per-process scope via LD_PRELOAD, and FAKETIME_NO_CACHE for high-resolution mocking. Use when you need to fake time, mock the clock, or freeze time for C/C++ or any native binary that needs deterministic wall-clock time.
mockclock-jvm
Wraps Java's java.time.Clock + InstantSource dependency-injection pattern for testing time-sensitive code. Covers Clock.fixed(instant, zone), Clock.offset(baseClock, duration), Clock.systemDefaultZone() for production, the InstantSource interface (Java 17+), and the recommended dependency-injection pattern (constructor-inject Clock instead of calling Instant.now() directly). Use when you need to wire the clock-injection pattern (Clock.fixed, Clock.offset, MutableClock, InstantSource, Spring @Bean) into JVM (Java / Kotlin / Scala) production or test code. For pure DST transition reference (skipped or repeated hours, IANA DB, cron-double-fire bug classes) without a clock-injection need, use dst-transition-reference instead.
sinon-fake-timers-js
Wraps Sinon's standalone @sinonjs/fake-timers library for JS/TS testing: install(), tick() / tickAsync(), setSystemTime(), restore(); covers timers (setTimeout / setInterval / requestAnimationFrame), Date / performance.now() / hrtime, and the toFake option for selective override. Runner-agnostic - drives the clock directly in Mocha, AVA, Jasmine, node:test, or the browser. Use when JS/TS code needs deterministic timer or clock control and the test runner does not already expose this library behind its own built-in fake-timer API.
timecop-ruby
Wraps timecop (github.com/travisjeffery/timecop), the Ruby time-mocking gem: Timecop.freeze, Timecop.travel, Timecop.scale (time-speedup), Timecop.return (cleanup), and RSpec-friendly helpers. Use when testing Ruby/Rails code that calls Time / Date / DateTime.
timezone-test-matrix-builder
Builds a timezone, daylight saving time (DST), and leap year / leap second test matrix from wherever a codebase reads or formats dates and times. Finds time-handling code (grep for datetime / Date / Instant / time.time / timezone), sorts each spot into storage, business-logic, display, cron, or billing, picks the edge cases that matter (DST spring-forward / fall-back, ambiguous local time, leap day Feb 29, ISO 8601 / RFC 3339 round-trip, zone-database updates), and emits per-spot test stubs wired to the language's fake-clock (mock-time) library. Use when a codebase needs timezone, DST, and leap-year test coverage derived from its own date/time usage.