Swizec Teller - a geek with a hatswizec.com

    Yet another hard lesson about time

    Yo Swiz, some users complained their exam ends immediately.

    Hm… that's weird ?

    So began my two day hunt for a bug.

    To become a Yup tutor, you have to pass a subject exam: 25 questions about math, chemistry, or physics in 45 minutes. It's hard as hell; I tried.

    There's a timer in the corner ticking down. 45 minutes, and then you're done. A dialog shows up telling you time’s up. Easy peasy to build, right? Get a start time, add 45 minutes, count down. When timer hits zero, stop. 90% of the time, it works every time.

    And then there’re those 7 people. For them, the timer hits zero 1 second after it starts. 45 minutes. Tick. Zero.


    I looked through the code. No race conditions. You click Start, the server creates an exam, makes a started_at timestamp, adds 45 minutes, and tells the frontend when your exam should end. On every Clock tick, the counter updates ,and a MobX observer checks if you're out of time.

    Very straightforward.

    We're using React and MobX, so the frontend behaves like a state machine. There's no way we're rendering stale views, no way for poorly garbage collected views to cause problems, and the more I played with the frontend state machine, the sturdier it looked.

    Here's an excerpt:

    class SubjectExam {
    constructor(user, subject) {
    () => this.isOutOfTime,
    () => {
    if (!this.finished) {
    @computed get isOutOfTime() {
    return !_.isNull(this.timeLeft) && this.timeLeft <= 0;
    @computed get timeLeft() {
    if (this.should_end_by) {
    return moment.duration(moment.unix(this.should_end_by).diff(Clock.time));
    } else {
    return null;

    MobX's when creates an autorun that checks the condition function, () => this.isOutOfTime, every time its value changes. When it evaluates to true, it runs the given function. In this case, that’s an action that finishes the subject exam.

    The reason this works is hidden in @computed timeLeft(). It depends on Clock.time, which is a global MobX store that updates every second. It’s based on my Modeling time in React & MobX approach.

    Diffing the server-provided should_end_by timestamp and Clock.time tells us how much time is left. It works great and produces a countdown timer when you add some rendering.

    The nice feature of this approach is that the timer is stable. You can refresh and navigate the page as much as you want. The server calculates the time, so it doesn't care.

    But… those 7 people… why was the time up after 1 second? Can you guess?

    I found no pattern in who the applicants were, when they took the exam, what timezone they were in, or which browser they used. It's not a bug in MomentJS. None of the cases happened around a DST, day, month, or year boundary.

    It should work.


    The only remaining explanation is that their system time was waaaaaayyy in the future. More than 45 minutes. I don't know why their computers thought they were in the future, but it's the only explanation that works.

    How do you fix it?

    You don't rely on user time. Instead of calculating a should_end_by timestamp on the server, calculate the delta. Look only at the delta when doing a countdown.

    Like this:

    The exam starts, the server looks at current time and returns 45min. User reloads, server looks at current time, calculates delta from start time, returns new time remaining.

    On the frontend, you create a local shouldEndAt timestamp when you load the exam – time + delta_from_server – then count down from there.

    Lesson learned. Time is so hard that you can't even rely on people's clocks being accurate to within 1 hour. Add it to the Falsehoods Programmers Believe About Time pile.


    Did you enjoy this article?

    Published on January 19th, 2017 in Learning, Technical

    Learned something new?
    Want to become an expert?

    Here's how it works 👇

    Leave your email and I'll send you thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

    Join Swizec's Newsletter

    And get thoughtful letters 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.

    "Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"

    ~ Ashish Kumar

    Join over 14,000 engineers just like you already improving their careers with my letters, workshops, courses, and talks. ✌️

    Have a burning question that you think I can answer? I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

    Ready to Stop copy pasting D3 examples and create data visualizations of your own?  Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

    Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

    Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first 💰 on the side with ServerlessReact.Dev

    Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️

    Created by Swizec with ❤️