Skip to content

ULID Generator — Java source

Generate Universally Unique Lexicographically Sortable Identifiers (ULID) - 26-character Crockford-base32 strings that sort by millisecond timestamp. Paste any ULID to decode its timestamp and randomness. Runs entirely in your browser.

This is the Java implementation — the same logic the interactive tool runs, in a shareable, citable form.

// ulid-generator - 26-char Crockford-base32 sortable ID: 10 chars of 48-bit ms timestamp + 16 chars of 80-bit randomness. Language: Java (17+, JDK only). Port of src/lib/ulid.ts (Go twin: cli/ulid-generator) - encode/generate/decode-time core; inspectUlid (date/random-hex view) lives in this dir's javascript.js (80-line budget).
import java.security.SecureRandom;

final class UlidDemo { // package-private: compiles under the showcase filename java.java
    private static final String CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; // no I, L, O, U
    private static final SecureRandom RNG = new SecureRandom(); // the JDK CSPRNG

    // Divide the big-endian value in b (mutated in place) by 32; return the
    // remainder 0-31. Long division in base 256, exactly as in the Go twin.
    private static int divBy32(byte[] b) {
        int rem = 0;
        for (int i = 0; i < b.length; i++) {
            int cur = (rem << 8) | (b[i] & 0xFF);
            b[i] = (byte) (cur >> 5);
            rem = cur & 0x1F;
        }
        return rem;
    }

    // Base32-encode the 128-bit big-endian [6 time bytes | 10 random bytes] into
    // the canonical 26 chars. Because 32^16 == 2^80, the first 10 chars equal the
    // TS/Go twins' separate time encoding - the cross-language lock-step anchor.
    public static String encodeUlid(byte[] timeBytes, byte[] randomBytes) {
        byte[] b = new byte[16];
        System.arraycopy(timeBytes, 0, b, 0, 6);
        System.arraycopy(randomBytes, 0, b, 6, 10);
        char[] out = new char[26];
        for (int i = 25; i >= 0; i--) out[i] = CROCKFORD.charAt(divBy32(b));
        return new String(out);
    }

    // 48-bit ms timestamp -> 6 big-endian bytes (MSB first), like TS timeBytes().
    public static byte[] timeBytes(long ms) {
        byte[] out = new byte[6];
        for (int i = 5; i >= 0; i--, ms >>= 8) out[i] = (byte) (ms & 0xFF);
        return out;
    }

    // New ULID for the epoch-ms; the random tail comes from SecureRandom,
    // the JDK analogue of Go's crypto/rand.
    public static String generateUlid(long ms) {
        byte[] rnd = new byte[10];
        RNG.nextBytes(rnd);
        return encodeUlid(timeBytes(ms), rnd);
    }

    // Extract the 48-bit ms timestamp from the first 10 chars; throws on
    // malformed input (wrong length, or a char outside the alphabet such as I/L/O/U).
    public static long decodeUlidTime(String id) {
        if (id.length() != 26) throw new IllegalArgumentException("ulid: malformed id: want 26 chars");
        long ts = 0;
        for (int i = 0; i < 10; i++) {
            int v = CROCKFORD.indexOf(id.charAt(i));
            if (v < 0) throw new IllegalArgumentException("ulid: invalid character " + id.charAt(i));
            ts = ts * 32 + v;
        }
        return ts;
    }

    public static void main(String[] args) {
        String ulid = generateUlid(1_722_000_000_000L);
        System.out.println("ulid:     " + ulid);
        System.out.println("time(ms): " + decodeUlidTime(ulid) + "  (round-trip ok)");
    }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →