Brett Owers
← All Projects

Timer

Archived

July 1, 2019

A second attempt at the interval timer concept — better than the original Intervolition Flutter prototype but still mid. The real lesson: the difference between framework-level timers (convenient, inaccurate) and timestamp-based timing against a reliable clock (correct, harder). Why most timer apps are quietly wrong, and what real accuracy requires.

Purpose

Another swing at the interval timer idea that kept living in my head. This version was cleaner than the original Flutter Intervolition attempt but still did not reach the bar for shipping. The value was not the app — it was finally understanding why timers are harder than they look.

Stack

FlutterDartTimer APIDateTimeMobile

What I Learned

  • Most timer implementations are subtly wrong. The naive approach — start a Timer.periodic(Duration(seconds: 1)) and increment a counter — drifts. Each tick has overhead (function call, UI rebuild, thread scheduling). Over 10 minutes, a 1-second periodic timer can drift by several seconds. Over an hour, it is noticeably wrong. Users doing interval training, cooking, or meditation will notice.
  • The correct approach is timestamp diffing: record DateTime.now() (or better, a monotonic clock) when the timer starts, and on every tick, calculate elapsed = now - startTime. Display the difference, not an accumulated counter. The tick is just a trigger to re-render — the actual time measurement comes from the clock, not from counting callbacks. This is immune to drift because you are reading the real time on every frame.
  • System clocks (DateTime.now()) can jump — NTP corrections, timezone changes, user manually adjusting the clock. Monotonic clocks (Stopwatch in Dart, clock_gettime(CLOCK_MONOTONIC) on Linux, mach_absolute_time on macOS/iOS) only move forward and are not affected by system time adjustments. For timers, you want the monotonic clock for elapsed time and the system clock only for display purposes like "started at 3:42 PM."
  • Atomic clocks and NTP (Network Time Protocol) are how devices stay accurate. Your phone synchronizes its system clock with NTP servers, which themselves synchronize with atomic clocks (cesium-133 oscillation frequency, accurate to 1 second per 100 million years). When you diff timestamps, you are indirectly measuring against atomic time. The chain is: your timer → system monotonic clock → NTP-disciplined system clock → NTP server → atomic clock. Understanding this chain explains why your timer works, how accurate it really is, and what can go wrong.
  • Background state is the other killer. On mobile, when the app goes to the background, the OS can suspend your process — timers stop ticking, periodic callbacks stop firing. When the app resumes, a counter-based timer has lost all the background time. A timestamp-based timer just reads the clock and calculates the correct elapsed time as if nothing happened. This is the single biggest reason to use timestamp diffing over counter incrementing.
  • The framework-level timer (setTimeout, setInterval, Timer.periodic) is a convenience API, not a precision tool. It guarantees minimum delay, not exact delay. A 1000ms interval might fire at 1003ms, 1007ms, 998ms — the scheduler does its best but makes no promises. For UI animation timing, this is fine. For a stopwatch or interval timer that users are relying on for accuracy, it is not.

Key Insights

  • The reason this was attempt number two (and still mid) connects to the Intervolition pattern: some ideas need multiple drafts across multiple years before they are ready to ship. The Flutter version taught the data model. This version taught timing accuracy. The final Swift version shipped clean because it inherited the lessons of both failures. Mid is not wasted — mid is research.
  • Most consumer timer apps are quietly using the naive approach and getting away with it because users do not time their timer against another timer. But the ones that do it right — the ones that use timestamp diffing and monotonic clocks — earn a subtle trust. The user cannot articulate why the app feels reliable, but the reliability is there in every second that does not drift.
  • The timestamp-vs-counter pattern is a specific instance of a universal engineering principle: derive state from the source of truth, do not accumulate it from events. In timers, the source of truth is the clock. In UIs, the source of truth is the data model. In distributed systems, the source of truth is the database. Every time you accumulate state from events instead of reading it from the source, you are introducing drift.
#Flutter#Dart#timer#interval-timer#timestamp#monotonic-clock#NTP#atomic-clock#accuracy#drift#mobile#background-state

This post was composed through a conversation between Brett Owers and Claude Code (Anthropic). The content reflects Brett's recollection of each project and the lessons drawn from it. Some details may be approximate or omitted — the purpose is to paint an honest picture of a software engineer's development over time, not to serve as a precise historical record.