Skip to content

[bug] : getTimestamp() returns a nonsense timestamp for non-sortable IDs #29

Description

@suhailopensource

Random IDs are valid base62, so getTimestamp() decodes them into numbers that were never
timestamps. Verified on main @ 0cd81bf:

getTimestamp(id("user"))        -> 6551754662245195   (year 209586)
getTimestampOrThrow(id("user")) -> same number, does NOT throw

The documented contract is "or undefined if malformed", and getTimestampOrThrow exists to be
loud about malformed values. Both return a confidently wrong answer instead.

Everything around it was fixed already — getDate (#9), parseId and getPrefix (#13, #26).
getTimestamp, which they all sit on, is the last one.

Background: originally #5, closed as completed on Aug 14 without the fix landing. PR #7 had the
right change but was closed for bundling two issues; only its getDate half was ever re-submitted.

Fix

In getTimestamp (src/generators/sortable.ts), return undefined when the decoded value
exceeds SORTABLE_TIME_MAX (already in src/constants/index.ts). getTimestampOrThrow then
throws for free. Leave getDate's own guard alone — that's deliberate (#11).

This is not a validity check: ~2% of random IDs still decode below the ceiling (measured 2.10%
over 200k samples; theory is 2^48 / 62^9 = 2.08%). Keep the existing docs wording that says these
functions can't verify a value came from sortableId.

One existing test will fail — expected

test/sortable.test.ts → getDate() > returns a Date exactly at the Date-range ceiling and undefined just past it. MAX_DATE_MS is ~30x above SORTABLE_TIME_MAX, so getTimestamp now
rejects it first. Reduce it to a single toBeUndefined() and keep the invariant test beside it.
Applying the fix leaves the suite at 1 failed | 114 passed — that one failure and nothing else.

Acceptance criteria

  • getTimestamp(id("user")) returns undefined; getTimestampOrThrow throws a TypeError.
  • Real sortableId() round-trips unchanged, including BASE32_CROCKFORD and custom timestampSize.
  • The getDate boundary test updated as above; invariant test kept.
  • Tests in the existing getTimestamp() describe block; CHANGELOG entry under Unreleased.
  • npm run verify passes. No inline comments — see CONTRIBUTING.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions