Skip to content

Instantly share code, notes, and snippets.

@fabiolimace
Last active October 24, 2020 22:15
Show Gist options
  • Save fabiolimace/c3da33d84683fadfa0cba63e433868a2 to your computer and use it in GitHub Desktop.
Save fabiolimace/c3da33d84683fadfa0cba63e433868a2 to your computer and use it in GitHub Desktop.
Generate COMB UUID/GUID with nanosecond resolution in Java
package your.package.name;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.UUID;
/**
* A UUID generator that creates a COMB GUID with nanoseconds resolution.
*
* It borrows the main idea from ULID and COMB generators: a concatenation of
* time and random bytes. It is composed of 64 bits for time and 64 for random
* bits.
*
* A Nano COMB has two components:
*
* 1. Time camponent (64 bits): nanoseconds since 1970
*
* 2. Random component (64 bits): a value generated by a secure random
* generator.
*
* Maximum time component year is ~2262 A.D. (2^63/10^9/60/60/24/365.25 + 1970)
*
* Tags: uuid guid comb comb-guid comb-uuid uuid-generator guid-generator comb-generator generator comb time random
*
* @author: Fabio Lima 2020
*/
public final class NanoCombCreator {
private long prevTime = System.currentTimeMillis();
private long prevNano = System.nanoTime();
private static final long ONE_MILLION_NANOSECONDS = 1_000_000L;
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
/**
* Returns a time component in nanoseconds.
*
* It uses `System.currentTimeMillis()` to get the system time in milliseconds
* accuracy. The nanoseconds resolution is simulated by calling
* `System.nanoTime()` between subsequent calls within the same millisecond.
* It's not precise, but it provides some monotonicity to the values generates.
*
* @return the current time in nanoseconds
*/
private synchronized long getTimeComponent() {
long time = System.currentTimeMillis();
long nano = System.nanoTime();
long elapsed = 0; // nanoseconds since last call
if (time == prevTime) {
elapsed = (nano - prevNano);
} else {
prevNano = nano;
}
prevTime = time;
return (time * ONE_MILLION_NANOSECONDS) + elapsed;
}
/**
* Returns the random component using a secure random generator.
*
* @return a random value.
*/
private synchronized long getRandomComponent() {
return SECURE_RANDOM.nextLong();
}
/**
* Returns a Nano COMB.
*
* A Nano COMB is inspired on ULID and COMB generators.
*
* It is composed of 64 bits for time and 64 for random bits.
*
* @return a UUID
*/
public synchronized UUID create() {
final long timeBits = getTimeComponent();
final long randomBits = getRandomComponent();
return new UUID(timeBits, randomBits);
}
/**
* Test method that generates many Nano COMBs in a loop.
*
* @param args
*/
public static void main(String[] args) {
NanoCombCreator creator = new NanoCombCreator();
for (int i = 0; i < 100; i++) {
// Generate a Nano COMB
UUID uuid = creator.create();
// Extract the milliseconds and nanoseconds
long milliseconds = uuid.getMostSignificantBits() / ONE_MILLION_NANOSECONDS;
long nanoseconds = uuid.getMostSignificantBits() & ONE_MILLION_NANOSECONDS;
// Instantiate an instant using the milliseconds and nanoseconds
Instant time = Instant.ofEpochMilli(milliseconds).plusNanos(nanoseconds);
// Print the UUID and the time it was generated (UTC)
System.out.println("UUID: '" + uuid + "', time: " + time);
}
}
}
// OUTPUT:
//
// UUID: '16410b91-6e48-5769-15a0-c2e40593f728', time: 2020-10-24T21:41:01.569541248Z
// UUID: '16410b91-7283-a200-a74d-c903b23126bc', time: 2020-10-24T21:41:01.640197120Z
// UUID: '16410b91-7284-d51a-154f-a0528d5924d3', time: 2020-10-24T21:41:01.640278528Z
// UUID: '16410b91-7286-924b-ecf7-3b3e0cf69d3f', time: 2020-10-24T21:41:01.640393792Z
// UUID: '16410b91-7287-9322-b780-5dc116f837b7', time: 2020-10-24T21:41:01.640459264Z
// UUID: '16410b91-7288-e4a9-eac3-72a8a7ca0afe', time: 2020-10-24T21:41:01.640540672Z
// UUID: '16410b91-728a-45c7-ddf5-5d213eb6a132', time: 2020-10-24T21:41:01.640671808Z
// UUID: '16410b91-728b-5a2d-7b3c-14656a0a10fc', time: 2020-10-24T21:41:01.640737792Z
// UUID: '16410b91-728c-abd5-6e15-b38781250ef5', time: 2020-10-24T21:41:01.640787008Z
// UUID: '16410b91-7292-e440-c088-f93a8185c78d', time: 2020-10-24T21:41:01.641147520Z
// UUID: '16410b91-7293-e216-b672-f7d58f9fe278', time: 2020-10-24T21:41:01.641213504Z
// UUID: '16410b91-7295-5c2d-5160-f57780c58655', time: 2020-10-24T21:41:01.641344064Z
// UUID: '16410b91-7296-cb55-6ad5-17c2cc852862', time: 2020-10-24T21:41:01.641410176Z
// UUID: '16410b91-7298-9407-8f1b-cc6d68a9e083', time: 2020-10-24T21:41:01.641524288Z
// UUID: '16410b91-729a-1345-b386-4d63561c489e', time: 2020-10-24T21:41:01.641655936Z
// UUID: '16410b91-729b-7ef4-767b-7abe334a9c9a', time: 2020-10-24T21:41:01.641737856Z
// UUID: '16410b91-729d-44d7-24de-6b350ccbefda', time: 2020-10-24T21:41:01.641868416Z
// UUID: '16410b91-729e-903b-4829-833131df868e', time: 2020-10-24T21:41:01.641917504Z
// UUID: '16410b91-72a0-3594-cec7-497dcb6511c7', time: 2020-10-24T21:41:01.641Z
// UUID: '16410b91-72a1-364d-bc1c-fbfa9dbeefa9', time: 2020-10-24T21:41:01.641066112Z
// UUID: '16410b91-72a2-2680-3761-035b38c80564', time: 2020-10-24T21:41:01.642131584Z
// UUID: '16410b91-72a3-c5da-f72b-013316ae31d8', time: 2020-10-24T21:41:01.642213056Z
// UUID: '16410b91-72a4-cfa4-a3a7-7927d7f63ad1', time: 2020-10-24T21:41:01.642279040Z
// UUID: '16410b91-72a6-5e34-4e1a-c74b85fbb39f', time: 2020-10-24T21:41:01.642410112Z
// UUID: '16410b91-72a7-678c-7098-fc9bb3063588', time: 2020-10-24T21:41:01.642475648Z
// UUID: '16410b91-72a8-75cf-9803-da85d9f7391e', time: 2020-10-24T21:41:01.642540736Z
// UUID: '16410b91-72aa-bf7c-21ac-57e3c817fca4', time: 2020-10-24T21:41:01.642655936Z
// UUID: '16410b91-72ac-3450-9d1b-8959c9957039', time: 2020-10-24T21:41:01.642786496Z
// UUID: '16410b91-72ad-c6c8-8e18-2c39984bc5b0', time: 2020-10-24T21:41:01.642868928Z
// UUID: '16410b91-72ae-ca91-aa4a-682084b02be5', time: 2020-10-24T21:41:01.642934400Z
// UUID: '16410b91-72af-cc63-1721-895fbf0e157a', time: 2020-10-24T21:41:01.642999488Z
// UUID: '16410b91-72b1-68c0-46c6-9af7ebca0d71', time: 2020-10-24T21:41:01.643081984Z
// UUID: '16410b91-72b2-8707-b4fa-5a29b2005f9a', time: 2020-10-24T21:41:01.643131584Z
// UUID: '16410b91-72b3-ff6d-7ab9-9b8653db726f', time: 2020-10-24T21:41:01.643213568Z
// UUID: '16410b91-72b4-fb77-56af-3243ab595d49', time: 2020-10-24T21:41:01.643279104Z
// UUID: '16410b91-72b5-eb26-5ff5-abc45cb98ae6', time: 2020-10-24T21:41:01.643344576Z
// UUID: '16410b91-72b7-cc2a-526e-2220f8d16b82', time: 2020-10-24T21:41:01.643475136Z
// UUID: '16410b91-72b8-c892-9527-a9612a664722', time: 2020-10-24T21:41:01.643540672Z
// UUID: '16410b91-72ba-182d-6ec9-962ea678153f', time: 2020-10-24T21:41:01.643655360Z
// UUID: '16410b91-72bb-a9cc-e83b-f0f77ea9bcf7', time: 2020-10-24T21:41:01.643720960Z
// UUID: '16410b91-72bc-ad57-ce53-8f1886523766', time: 2020-10-24T21:41:01.643786496Z
// UUID: '16410b91-72bd-f0d5-d0f4-21c5803b05fc', time: 2020-10-24T21:41:01.643868416Z
// UUID: '16410b91-72bf-2664-808b-69831505c7b8', time: 2020-10-24T21:41:01.643983616Z
// UUID: '16410b91-72c0-ab00-fd09-e7eba03dfe47', time: 2020-10-24T21:41:01.644000512Z
// UUID: '16410b91-72c1-a889-3de9-8208a0d8e31c', time: 2020-10-24T21:41:01.644065536Z
// UUID: '16410b91-72c2-a220-8d10-f0482a2bcaae', time: 2020-10-24T21:41:01.644131584Z
// UUID: '16410b91-72c4-0e6f-4ea4-5fe7cb4546ec', time: 2020-10-24T21:41:01.644262720Z
// UUID: '16410b91-72c5-0681-0fd2-d25ed4728763', time: 2020-10-24T21:41:01.644328192Z
// UUID: '16410b91-72c6-4d7b-9666-3ef582389fcc', time: 2020-10-24T21:41:01.644409664Z
// UUID: '16410b91-72c7-4a52-071b-7ef8592e903f', time: 2020-10-24T21:41:01.644475712Z
// UUID: '16410b91-72c8-471f-fd0a-6036f6299f75', time: 2020-10-24T21:41:01.644541184Z
// UUID: '16410b91-72c9-8a30-6ddf-238d6569aeb2', time: 2020-10-24T21:41:01.644590336Z
// UUID: '16410b91-72ca-a42c-ee4c-c066f301ded1', time: 2020-10-24T21:41:01.644655360Z
// UUID: '16410b91-72cb-eb29-7e63-039c3e676484', time: 2020-10-24T21:41:01.644737792Z
// UUID: '16410b91-72cc-e996-8c0c-bc1804afaa50', time: 2020-10-24T21:41:01.644802816Z
// UUID: '16410b91-72ce-1b2a-b9ca-5a0eb9eb6c97', time: 2020-10-24T21:41:01.644918016Z
// UUID: '16410b91-72cf-ed40-981e-58572f1a0343', time: 2020-10-24T21:41:01.645999488Z
// UUID: '16410b91-72d0-cf70-c346-30b4e8ec5742', time: 2020-10-24T21:41:01.645016960Z
// UUID: '16410b91-72d2-167b-7151-51426305d8bf', time: 2020-10-24T21:41:01.645131648Z
// UUID: '16410b91-72d3-02e6-7194-355272f16654', time: 2020-10-24T21:41:01.645197184Z
// UUID: '16410b91-72d4-21ee-6850-ec5a96e4fa79', time: 2020-10-24T21:41:01.645262208Z
// UUID: '16410b91-72d5-8f1b-f5fb-3a4468ce898d', time: 2020-10-24T21:41:01.645328192Z
// UUID: '16410b91-72d6-7f9b-8848-d1ae9070d6f1', time: 2020-10-24T21:41:01.645410112Z
// UUID: '16410b91-72d7-ff34-e5a3-5cdc80559d22', time: 2020-10-24T21:41:01.645475648Z
// UUID: '16410b91-72d9-dec2-0ac5-22db48c5586b', time: 2020-10-24T21:41:01.645606784Z
// UUID: '16410b91-72db-0428-aa36-ffdeb845569c', time: 2020-10-24T21:41:01.645720896Z
// UUID: '16410b91-72dc-302e-2a84-e6833c0422be', time: 2020-10-24T21:41:01.645786432Z
// UUID: '16410b91-72dd-1405-609a-c30fdfdcdad5', time: 2020-10-24T21:41:01.645851968Z
// UUID: '16410b91-72de-534f-ce22-2d0d81f2d2f8', time: 2020-10-24T21:41:01.645934464Z
// UUID: '16410b91-72df-2f80-c0f5-be207978e4fc', time: 2020-10-24T21:41:01.646983552Z
// UUID: '16410b91-72e0-1314-56ae-2c3c4761b296', time: 2020-10-24T21:41:01.646000512Z
// UUID: '16410b91-72e1-4568-6335-1ae2ad16b83a', time: 2020-10-24T21:41:01.646081984Z
// UUID: '16410b91-72e2-1cff-a7fa-e402ce2d723c', time: 2020-10-24T21:41:01.646131136Z
// UUID: '16410b91-72e3-538f-a65c-b29e2539be7e', time: 2020-10-24T21:41:01.646213504Z
// UUID: '16410b91-72e4-3d5b-0b06-78775e0984b8', time: 2020-10-24T21:41:01.646262208Z
// UUID: '16410b91-72e5-1eda-947f-18a35d504df5', time: 2020-10-24T21:41:01.646328256Z
// UUID: '16410b91-72e6-6172-5628-ad98dc142383', time: 2020-10-24T21:41:01.646409664Z
// UUID: '16410b91-72e7-51b8-75f6-bb38a4332c0f', time: 2020-10-24T21:41:01.646475136Z
// UUID: '16410b91-72e8-f298-295d-eb77ac92079a', time: 2020-10-24T21:41:01.646541184Z
// UUID: '16410b91-72e9-d6a4-409a-2647d33245b6', time: 2020-10-24T21:41:01.646606720Z
// UUID: '16410b91-72ea-b9b5-31dd-67c934019032', time: 2020-10-24T21:41:01.646655360Z
// UUID: '16410b91-72eb-edb2-1ba3-b6428e17469a', time: 2020-10-24T21:41:01.646737280Z
// UUID: '16410b91-72ec-c664-233e-99cb4e987089', time: 2020-10-24T21:41:01.646803392Z
// UUID: '16410b91-72ee-18f2-6879-4bf1e2306b9f', time: 2020-10-24T21:41:01.646917568Z
// UUID: '16410b91-72ee-71c0-b87f-8d5a5f374637', time: 2020-10-24T21:41:01.647933952Z
// UUID: '16410b91-72ef-4a7e-c8de-02e417b506a0', time: 2020-10-24T21:41:01.648Z
// UUID: '16410b91-72f0-a1c3-4fb3-a6d4e7f4d102', time: 2020-10-24T21:41:01.647000064Z
// UUID: '16410b91-72f1-8ed1-9b05-8cf136b44922', time: 2020-10-24T21:41:01.647066112Z
// UUID: '16410b91-72f3-222e-3024-26b8cab9374f', time: 2020-10-24T21:41:01.647197120Z
// UUID: '16410b91-72f4-0486-db01-7062b6de2ae5', time: 2020-10-24T21:41:01.647262144Z
// UUID: '16410b91-72f4-de2a-4c35-56432aba719f', time: 2020-10-24T21:41:01.647279040Z
// UUID: '16410b91-72f5-fdd3-dc76-f220bfa2b728', time: 2020-10-24T21:41:01.647344128Z
// UUID: '16410b91-72f6-d29f-201a-95e778539989', time: 2020-10-24T21:41:01.647410112Z
// UUID: '16410b91-72f7-fc0a-0da9-07a9f70a1e35', time: 2020-10-24T21:41:01.647475136Z
// UUID: '16410b91-72f8-c39e-6b2b-2574d9780e2d', time: 2020-10-24T21:41:01.647541184Z
// UUID: '16410b91-72f9-ba3e-5e0d-9513334160f8', time: 2020-10-24T21:41:01.647590336Z
// UUID: '16410b91-72fb-429a-3a45-f532c7ee8485', time: 2020-10-24T21:41:01.647737792Z
// UUID: '16410b91-72fc-2eb5-f91b-eab43c2aa2e4', time: 2020-10-24T21:41:01.647786944Z
// UUID: '16410b91-72fd-b400-26dc-dbc4d235e214', time: 2020-10-24T21:41:01.648851968Z
// UUID: '16410b91-72fe-8680-a161-823e569b1691', time: 2020-10-24T21:41:01.648918016Z
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment