The Week That Belonged to Next Year — Java Bug Hunt

Modelled on Twitter for Android, 29 December 2014: many Android users were signed out of the app and could not sign back in.

  • Language: Java
  • Layer: Backend
  • Difficulty: Medium
  • Concepts: Time, Dates, Auth
  • Modelled on: Twitter · 2014
  • Visible tests: a mid-December instant; 29 December 2014 is still 2014
  • Reward: 50 XP for a complete fix

Briefing

Modelled on Twitter for Android, 29 December 2014: many Android users were signed out of the app and could not sign back in. The cause, as Twitter's engineers explained, was a date format written with YYYY — the week-based year — instead of yyyy, the calendar year. In the last days of December the week that contains 1 January already belongs to the next week-based year, so the app believed it was 2015 a few days early.

This reconstruction stamps session times as strings and compares them to decide whether a session has expired.

Fix Stamps so a stamp always shows the calendar year.

Bug report

BUG-YYYY · Priority: Critical · Reported by: mobile on-call

Stamps.stamp(epochMillis) formats an instant in UTC as "yyyy-MM-dd HH:mm" using the CALENDAR year (Locale.US), e.g.

  • 2014-12-15T09:30Z -> "2014-12-15 09:30"
  • 2014-12-29T00:00Z -> "2014-12-29 00:00"
  • 2014-12-31T23:59Z -> "2014-12-31 23:59"
  • 2015-01-01T00:00Z -> "2015-01-01 00:00"

SessionGuard (locked) compares these strings, so a stamp that jumps a year ahead makes every session look expired.

Observed: from 28 December onwards every stamp reads 2015 and users are signed out on launch.

Logs

[session] now=2015-12-29 00:00 expires=2015-01-11 12:00 -> EXPIRED, signing out
[session] 1 of 1 sessions expired on launch (device clock 2014-12-29)

The code as shipped

src/session/Stamps.java (editable)

import java.text.SimpleDateFormat;

class Stamps {
    static final String PATTERN = "YYYY-MM-dd HH:mm";

    static String stamp(long epochMillis) {
        SimpleDateFormat f = new SimpleDateFormat(PATTERN, Locale.US);
        f.setTimeZone(TimeZone.getTimeZone("UTC"));
        return f.format(new Date(epochMillis));
    }
}

Read-only context: src/session/SessionGuard.java.

Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More Java bug hunts.