ts6 · a timestamp, six characters wide
Fourteen
characters in.
Six out.
A tiny, dependency-free timestamp codec. yyyymmddHHMMSS collapses to six URL-safe characters that still sort in time order — one algorithm, four byte-identical languages.
yyyymmddHHMMSS — editing pauses the clock
decode — paste a ts6 code
Interactive demo needs JavaScript — the worked example below shows it statically.
why six characters
Compact first — then three nice bonuses.
The short form exists mainly to save screen space, where a full timestamp won't fit (mobile, especially). Everything else is designed in for free, in priority order.
Compact
Six characters cover every timestamp from 2025 through 2086. Then it grows one character at a time.
Sortable
The alphabet is in ASCII order, so a plain string sort puts codes in chronological order.
Safe
Output is [A-Za-z0-9] only — drops straight into URLs and filenames. A - appears solely on error.
Readable
Month is always uppercase A–L, hour always lowercase a–x — your eye finds them at a glance.
the encoding
A per-component base62 codec.
Each of the six timestamp fields becomes exactly one base62 digit via a fixed offset. The alphabet is 62 symbols in ASCII order — which is why a naive sort equals a chronological one.
| Field | Source | Offset | Renders as | Notes |
|---|---|---|---|---|
| year | yyyy | year − 2025 | 0-9A-Za-z | epoch 2025, so 2026 → 1 |
| month | 01–12 | +9 | A–L | always uppercase |
| day | 01–31 | none | 1-9A-V | raw base62 |
| hour | 00–23 | +36 | a–x | always lowercase |
| minute | 00–59 | none | 0-9A-Za-x | raw base62 |
| second | 00–59 | none | 0-9A-Za-x | raw base62 |
Reading 1F3kg0: 1 = 2026, F = June, 3 = the 3rd, k = 10h, g = 42m, 0 = 00s.
Sort check: 10:43:00 → 1F3kh0, which sorts after 1F3kg0. Correct.
range & the future
ts6 → ts7 → … → tsN.
One year character covers 2025–2086. When year − 2025 tops 61, the year field grows by one base62 digit — the trailing five characters never change — so the format quietly becomes ts7, ts8, and on. Decoding infers the year width as length − 5, no flag needed.
| Form | Year digits | Total length | Max year offset | Upper year |
|---|---|---|---|---|
| ts6 | 1 | 6 | 61 | 2086 |
| ts7 | 2 | 7 | 3,843 | 5868 |
| ts8 | 3 | 8 | 238,327 | 240,352 |
| ts9 | 4 | 9 | 14,776,335 | 14,778,360 |
| ts10 | 5 | 10 | 916,132,831 | 916,134,856 |
ts7 alone reaches the year 5868, so the format sails past the year 3026 without ever leaving ts7. By ts10 we are at roughly 916 million AD — at which point the heat death of the sun is the more pressing item in the backlog. This format will not be what fails you.
Known limitation — cross-width sort. Sorting is perfect within one width. Because the year field is variable-width, a ts6 code and a ts7 code don't sort against each other (z… sorts after 10…). Since ts7 only appears after 2086 — by definition someone else's problem — it's documented rather than papered over with zero-padding.
error handling
Fails loud. Never lies.
A bad input doesn't throw and doesn't silently pass. It produces a deliberately broken output that fails downstream, with a - marking each offending position. One bad decode character means the length goes wrong — on purpose, so it can't masquerade as a clean 14-char string.
Valid — round-trips
Bad — marked, not thrown
four languages, one algorithm
Two functions. Same verbs everywhere.
Procedural, no classes or namespaces: encode (14 → ts6) and decode (ts6 → 14). Pick whatever fits your stack — the codes are interchangeable.
# functions echo the result; capture with $( ) source ts6.sh code=$(ts6_encode 20260603104200) # 1F3kg0 ts=$(ts6_decode 1F3kg0) # 20260603104200 echo "$code -> $ts"
require 'ts6.php'; echo ts6_encode('20260603104200'); // 1F3kg0 echo ts6_decode('1F3kg0'); // 20260603104200
import ts6 ts6.encode('20260603104200') # '1F3kg0' ts6.decode('1F3kg0') # '20260603104200'
import ts6 from './ts6.js'; // or: import { encode, decode } from './ts6.js'; ts6.encode('20260603104200'); // '1F3kg0' ts6.decode('1F3kg0'); // '20260603104200'
This very page runs on the real ts6.js above — the live converter at the top imports the same file you'd download.
download
One drop-in file. No dependencies.
Take only the language you need, or grab the whole set. Nothing to install, nothing to configure.
The four implementations are byte-identical in both directions for every input, errors included — kept honest by one shared vector table. Source and issues live on GitHub.