RORK LABJP
DEADLINE — From August 31, every new app and update on Google Play must target Android 16 (API level 36). Six days to goIOS27 — The seventh developer betas of iOS 27 and macOS 27 landed on August 24, with the public releases due next month. They include Apple Intelligence changes, so it is time to check your own buildsANDROID17 — The Android 17 QPR1 beta fixed a bug that prevented swiping the bottom gesture bar to switch apps after using Circle to SearchFUNDING — On April 9, 2026, Rork announced a $15M seed round led by Left Lane Capital. It also acquired app builder Paperline and signalled it will keep acquiring to bring in engineering talentTRAFFIC — Rork reports over 743,000 monthly visits with 85 percent growth, which suggests its narrow focus on native mobile is paying off among AI app buildersFORECAST — Gartner expects 75 percent of new applications to be built with low-code or no-code in 2026, up from under 25 percent in 2020DEADLINE — From August 31, every new app and update on Google Play must target Android 16 (API level 36). Six days to goIOS27 — The seventh developer betas of iOS 27 and macOS 27 landed on August 24, with the public releases due next month. They include Apple Intelligence changes, so it is time to check your own buildsANDROID17 — The Android 17 QPR1 beta fixed a bug that prevented swiping the bottom gesture bar to switch apps after using Circle to SearchFUNDING — On April 9, 2026, Rork announced a $15M seed round led by Left Lane Capital. It also acquired app builder Paperline and signalled it will keep acquiring to bring in engineering talentTRAFFIC — Rork reports over 743,000 monthly visits with 85 percent growth, which suggests its narrow focus on native mobile is paying off among AI app buildersFORECAST — Gartner expects 75 percent of new applications to be built with low-code or no-code in 2026, up from under 25 percent in 2020
Articles/Dev Tools
Dev Tools/2026-07-11Advanced

The Device Clock Can Be Moved — Protecting Daily Features and Trial Logic from Time Tampering

Advancing the device clock by one day was enough to claim tomorrow's daily wallpaper today. Starting from that log entry, this article lays out a three-layer time model — wall clock, monotonic clock, server time — and shows how to build daily-reward and trial logic that survives clock rollbacks.

Rork544App Development33Architecture22Time SyncSubscriptions15

Premium Article

While reading through the delivery logs for a daily-wallpaper feature, I noticed something odd: the same device had claimed "today's wallpaper" three times in a single day.

The cause turned out to be embarrassingly simple. Move the device date forward one day in Settings, and the app happily concludes a new day has arrived and hands over tomorrow's image. Roll it back, move it forward again, and you can collect as many as you like.

I doubt there was much malice involved. But leave this behavior in place and then build "7-day login streaks" or "free trial expiration" on the same clock, and the damage stops being a few wallpapers claimed early. Having operated several apps as a solo developer, I now treat distrust of the device clock as something worth designing in from day one.

Apps generated with Rork are no exception. The generated date handling uses Date.now() and new Date() in the straightforward way, which means it follows whatever the user sets in the device settings. This article works through the time-handling architecture I use to close that gap.

Four Ways the Device Clock Drifts, and What Breaks

Start with the paths by which a device's wall clock ends up away from the true current time.

PathTypical driftWhat breaks
Manual change by the userMinutes to years (deliberate)Daily rewards, login streaks, time-limited coupons
Timezone travel and DST±1 to 14 hours"Today" boundary logic, notification scheduling
Failed or delayed NTP syncSeconds to minutesSignature timestamp checks, log ordering
Dead battery or factory resetDays to years (backward)Certificate validation, cache expiry checks

Measuring this across apps in production, devices more than 5 minutes away from server time consistently hovered just under 1% of the fleet. Devices off by more than 24 hours showed up at a rate of dozens per month. The number never reaches zero. The design assumption has to be that skewed devices are always present.

For deliberate changes specifically, the motive is almost always the same: skipping a wait. Daily drops, rewarded-ad cooldowns, stamina recovery. The more an app slices value by time, the more its users move their clocks.

A Three-Layer Time Model: Wall Clock, Monotonic Clock, Server Time

The backbone of the fix is assigning a different time source to each job. I think of it as three layers.

SourceHow to read itPropertiesRight for
Wall clockDate.now()Freely movable by the user; correct for displayOn-screen times, local notification fire times
Monotonic clockperformance.now() (per process)Elapsed time since launch; never rewinds, resets on restartDuration measurement, in-session cooldowns
Server timeHTTP Date header on API responsesTamper-proof, but unavailable offlineDaily boundaries, expirations, granting value

The key insight is that the wall clock is not the villain. For anything shown to the user, or for local notifications that should follow the device's lived time, the wall clock is exactly right. Things only break when granting value or judging expiration is delegated to it.

The monotonic clock sits in between. React Native's performance.now() never moves backward within a process, but a full app kill resets it. That makes it fine for short cooldowns like "once per 60 seconds within a session" and useless for anything that crosses a day boundary.

Which leaves one conclusion: any judgment about value that spans days has to lean on server time. So let's build that.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A three-layer time model (wall clock, monotonic clock, server time) with a decision table mapping each feature to the right time source
A trusted-clock provider for Expo that manages a server-time offset using nothing but the HTTP Date header from a Cloudflare Worker
Rollback-resistant daily-claim logic, plus the reasons a locally implemented free trial is a design mistake rather than a coding one
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

Related Articles

Dev Tools2026-08-01
Every Experiment Kept Landing on the Same Devices: Hash Choice and Salt Position, Measured Across a Million IDs
On-device experiment assignment looked fine in isolation, then collapsed the moment two experiments ran side by side. Here is what one million IDs revealed about four hash functions, why the culprit was the concatenation order rather than hash quality, and the implementation I settled on, with the measured numbers.
Dev Tools2026-07-24
Collapsing Duplicate Requests Into One: A Reference-Counted Single-Flight Layer
When several components fire the same API call at launch, you get a burst of identical requests. Here is a single-flight layer that shares one in-flight promise instead: how to build the key that decides the folding, the trap of handing out a failed promise forever, the trap of one caller's abort cancelling everyone, and the test that keeps it all from regressing, with the real network numbers alongside.
Dev Tools2026-07-24
Resolving App Config in Three Layers: Merging Defaults, User, and Remote With Bounded Overrides
A single type-safe layer that merges compiled defaults, user preferences, and remote config. So a broken remote value never takes your app down, each key gets its own override strength, plus schema validation and range clamping, built from a real production incident.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →