Reverse Timestamp codec
Most KV stores (DynamoDB, Cloud Datastore, range-scan KV) only support forward lexicographic scans natively. The Reverse Timestamp codec bitwise-inverts the 48-bit timestamp field before encoding, so newer IDs sort lexicographically before older ones — reading the most recent entries first becomes a forward scan.
import { createReverseTimestampId } from "@smonn/ids/reverse";
const events = createReverseTimestampId("evt");
const id = events.generate(); // "evt_…", sorts newest-firstevents.extractTimestamp(id); // Date — inversion is reversed to recover the msNo key material is required, and the inversion is a deterministic byte
transform — generate, generateAt, and extractTimestamp are fully
synchronous, exactly like the Timestamp codec.
Methods
Section titled “Methods”| Method | Description |
|---|---|
generate() |
Produces a new canonical ID using the codec’s now and rng. |
generateAt(date) |
Produces a new canonical ID at date; throws on invalid dates. |
is(value) |
Strict type guard — true only for already-canonical strings. For untrusted input, use safeParse(). |
parse(value) |
Lenient parse that normalises case and Crockford aliases; throws on failure. |
safeParse(value) |
Lenient parse that returns { ok: true, id } or { ok: false, error } without throwing. |
extractTimestamp(id) |
Inverts the timestamp bytes back to recover the original creation Date. Trusts the type — use safeParse() at boundaries first. |
minIdForTime(date) |
Lexicographically smallest ID for any ID generated at date (random portion 0x00). Throws on invalid dates. |
maxIdForTime(date) |
Lexicographically largest ID for any ID generated at date (random portion 0xff). Throws on invalid dates. |
toJsonSchema() |
Returns a JSON Schema object for the canonical wire form. |
~standard |
Standard Schema v1 validate entry point. |
IdsError, isIdsError, and IdsErrorCode are available from @smonn/ids.
Range bounds are flipped
Section titled “Range bounds are flipped”minIdForTime(date) and maxIdForTime(date) build synthetic IDs at the tight
lower and upper bounds of the given millisecond — same timestamp bytes, random
portion all 0x00 (min) or all 0xff (max). Because timestamps are inverted, a
newer date yields a lexicographically smaller result, and an older
date yields a lexicographically larger one. That flips the range-scan bounds
relative to the Timestamp codec:
const start = new Date("2026-01-01T00:00:00Z"); // olderconst end = new Date("2026-02-01T00:00:00Z"); // newer
// Reverse Timestamp: lower bound = newer time, upper bound = older timesql`SELECT * FROM events WHERE id BETWEEN ${events.minIdForTime(end)} AND ${events.maxIdForTime(start)}`;See ADR-0010.
All date-taking methods (generateAt, minIdForTime, maxIdForTime) throw if
passed a pre-epoch date (before 1 January 1970 UTC), a date past the 48-bit
ceiling (~10 889 AD), or an Invalid Date.
Deterministic tests
Section titled “Deterministic tests”The injection contract is identical to the Timestamp codec — pass a fixed now
and a no-op rng for snapshot-friendly output:
const events = createReverseTimestampId("evt", { now: () => new Date("2026-01-01T00:00:00Z").getTime(), rng: (target) => {}, // leave target as zero-filled});
events.generate(); // deterministic outputSee the Testing guide for the full pattern across every codec.
Inversion is reversible by anyone
Section titled “Inversion is reversible by anyone”The bitwise inversion (~ts & 0xFFFFFFFFFFFF) is a key-free, deterministic
transform. Creation time is always recoverable from the wire form by anyone —
extractTimestamp just inverts the bytes back. Like the Timestamp codec, the
Reverse Timestamp codec is not a tool for hiding creation time. If you need
the timestamp to be confidential, use the
Opaque Timestamp codec instead.