RORK LABJP
MAX — Rork Max handles code signing and provisioning so you can submit to the App Store in two clicks, with no local Mac requiredSIMULATOR — Try your app in an in-browser streaming simulator, then scan a QR code to install straight to your device without TestFlightNATIVE — Coverage reaches SwiftUI, ARKit, HealthKit, HomeKit, Core ML, and Metal, going where React Native cannotSEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z SpeedrunGROWTH — Reported at 743,000 monthly visits with 85% growth, widening access to AI-built mobile appsNOCODE — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026MAX — Rork Max handles code signing and provisioning so you can submit to the App Store in two clicks, with no local Mac requiredSIMULATOR — Try your app in an in-browser streaming simulator, then scan a QR code to install straight to your device without TestFlightNATIVE — Coverage reaches SwiftUI, ARKit, HealthKit, HomeKit, Core ML, and Metal, going where React Native cannotSEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z SpeedrunGROWTH — Reported at 743,000 monthly visits with 85% growth, widening access to AI-built mobile appsNOCODE — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026
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.

Rork501App Development33Architecture15Time SyncSubscriptions14

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 $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-07-09
Holding Layer Boundaries in a Rork-Generated Expo App with ESLint and dependency-cruiser
Long-lived Rork-generated Expo apps quietly accumulate screens that import the API client directly. Here is how I froze 214 existing violations as a baseline, eliminated 17 circular dependencies, and made CI reject anything new for 38 extra seconds.
Dev Tools2026-06-25
Why Paying Members See a Paywall in Airplane Mode — Keeping RevenueCat Entitlements Alive Offline
Open the app on a weak connection and a paying subscriber sees a paywall flash for a second. Here is how RevenueCat's customerInfo wavers on an offline launch, and a cache design that keeps entitlements valid with a trust window — written as working code for an Expo app.
Dev Tools2026-06-22
When a Poisoned Cache Crashes Your App on Every Launch — Designing a Safe-Mode Boot Your Users Can Escape On Their Own
When a persisted cache goes bad and the app crashes at the same spot on every launch, the only option left to the user is to reinstall. This article designs a safe-mode boot for Expo (React Native): the app counts its own early crashes, confirms a launch only once it becomes interactive, and resets just the dangerous state in graduated steps.
📚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 →