ULID Generator — C# 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 C# 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: C# (.NET 8, BCL 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).
using System;
using System.Security.Cryptography;
internal static class Ulid
{
private const string Crockford = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; // no I, L, O, U
// 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];
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 Encode(byte[] timeBytes, byte[] randomBytes)
{
var b = new byte[16];
Array.Copy(timeBytes, 0, b, 0, 6);
Array.Copy(randomBytes, 0, b, 6, 10);
var outChars = new char[26];
for (int i = 25; i >= 0; i--) outChars[i] = Crockford[DivBy32(b)];
return new string(outChars);
}
// 48-bit ms timestamp -> 6 big-endian bytes (MSB first), like TS timeBytes().
public static byte[] TimeBytes(long ms)
{
var outBytes = new byte[6];
for (int i = 5; i >= 0; i--, ms >>= 8) outBytes[i] = (byte)(ms & 0xFF);
return outBytes;
}
// New ULID for the epoch-ms. RandomNumberGenerator is the BCL CSPRNG -
// the analogue of Go's crypto/rand.
public static string Generate(long ms) =>
Encode(TimeBytes(ms), RandomNumberGenerator.GetBytes(10));
// 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 DecodeTime(string id)
{
if (id.Length != 26) throw new ArgumentException("ulid: malformed id: want 26 chars");
long ts = 0;
for (int i = 0; i < 10; i++)
{
int v = Crockford.IndexOf(id[i]);
if (v < 0) throw new ArgumentException($"ulid: invalid character '{id[i]}'");
ts = ts * 32 + v;
}
return ts;
}
}
internal static class Program
{
private static void Main()
{
string ulid = Ulid.Generate(1_722_000_000_000);
Console.WriteLine($"ulid: {ulid}");
Console.WriteLine($"time(ms): {Ulid.DecodeTime(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 →