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.
Random IDs are valid base62, so
getTimestamp()decodes them into numbers that were nevertimestamps. Verified on main @ 0cd81bf:
The documented contract is "or
undefinedif malformed", andgetTimestampOrThrowexists to beloud about malformed values. Both return a confidently wrong answer instead.
Everything around it was fixed already —
getDate(#9),parseIdandgetPrefix(#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
getDatehalf was ever re-submitted.Fix
In
getTimestamp(src/generators/sortable.ts), returnundefinedwhen the decoded valueexceeds
SORTABLE_TIME_MAX(already insrc/constants/index.ts).getTimestampOrThrowthenthrows 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_MSis ~30x aboveSORTABLE_TIME_MAX, sogetTimestampnowrejects 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"))returnsundefined;getTimestampOrThrowthrows aTypeError.sortableId()round-trips unchanged, includingBASE32_CROCKFORDand customtimestampSize.getDateboundary test updated as above; invariant test kept.getTimestamp()describe block; CHANGELOG entry underUnreleased.npm run verifypasses. No inline comments — see CONTRIBUTING.