how do i get the date/time back from a UUID type 1
Asked Answered
C

3

5

I have included the following UUID library

compile group: 'com.fasterxml.uuid', name: 'java-uuid-generator', version: '3.1.5'

in my build.

i have some code like this

        NoArgGenerator timeBasedGenerator = Generators.timeBasedGenerator()
        UUID tuid = timeBasedGenerator.generate()
        Timestamp timestamp = new Timestamp ((tuid.timestamp()/1000) as Long)
        Date dateTime = new Date (timestamp.getTime())

however when I try and look at the date its nothing like what it should be so for example I get uid fef57eca-7c8b-11e8-bedd-992c2ac3197a was Sun Feb 06 07:55:54 GMT 6327 when today is 30/06/2018

Does anyone know how to correctly extract the real date and time from a time based UUID using the fasterxml.uuid library?

but stumped

ps tried this instead

        UUID tuid = timeBasedGenerator.generate()
        Long t = tuid.timestamp()
        Timestamp timestamp = new Timestamp (t)
        Date dateTime = new Date (timestamp.getTime())

which gives a uid ff79d7d9-7cb5-11e8-976c-6ba57a5e9636 and date of Thu Aug 14 11:11:40 BST 4359073

Chromic answered 30/6, 2018 at 17:42 Comment(6)
Have you read the widely available (i.e. Wikipedia) documentation on the structure of a Type 1 UUID? This will tell you where the timestamp is, the rest is writing code to extract it.Wellturned
See Universally unique identifier. Pay special attention to the "Format" and "Encoding" sectionsWellturned
Why do you think that the timestamp of the UUID is expressed as 1000th of the unit of timestamps for Java, and that the epoch instants of UUID and Java are the same? Did you verify these rather naive assumptions?Fireproofing
well didn't help exactly - the library has a method called timestamp. I read this docs.oracle.com/javase/7/docs/api/java/sql/Timestamp.html which says it can go to nano seconds - so dividing by 1000 seemed appropriate. I wasnt trying to get into doing lots of complicated processing ala the spec - surely the library is taking that pain away from me. so surely timestamp() call on UUID returns tim ein non seconds as long?Chromic
so my reading of the name of the function aka timestamp() in the library is not doing what it suggests - so with the UUID.timestamp() method return who am i supposed to get the correct time back ? see PS added to aboveChromic
You need to adjust the timestamp() method's units to the units of Java's Timestamp, and adjust the timestamp() method's epoch to the epoch of Java's Timestamp. Also to the best of my knowledge, to build a Java Timestamp to nanosecond precision, you need to build it first to second precision, then add in the nanoseconds within that second.Fireproofing
H
4

To get the full 100ns precision as a java.util.Instant, you can do the following:

private static final long NUM_HUNDRED_NANOS_IN_A_SECOND = 10_000_000L;

private static final long NUM_HUNDRED_NANOS_FROM_UUID_EPOCH_TO_UNIX_EPOCH = 122_192_928_000_000_000L;


/**
 * Extracts the Instant (with the maximum available 100ns precision) from the given time-based (version 1) UUID.
 *
 * @return the {@link Instant} extracted from the given time-based UUID
 * @throws UnsupportedOperationException If this UUID is not a version 1 UUID
 */
public static Instant getInstantFromUUID(final UUID uuid) {
    final long hundredNanosSinceUnixEpoch = uuid.timestamp() - NUM_HUNDRED_NANOS_FROM_UUID_EPOCH_TO_UNIX_EPOCH;
    final long secondsSinceUnixEpoch = hundredNanosSinceUnixEpoch / NUM_HUNDRED_NANOS_IN_A_SECOND;
    final long nanoAdjustment = ((hundredNanosSinceUnixEpoch % NUM_HUNDRED_NANOS_IN_A_SECOND) * 100);
    return Instant.ofEpochSecond(secondsSinceUnixEpoch, nanoAdjustment);
}
Hartsfield answered 22/12, 2020 at 11:1 Comment(0)
C
2

I did some more searching on the web.

I built the following 'simple utility' class that can be expanded as required:

import com.fasterxml.uuid.Generators
import com.fasterxml.uuid.NoArgGenerator

class UuidUtil {

    static final NoArgGenerator timeBasedGenerator = Generators.timeBasedGenerator()


    /**
     * From UUID javadocs the resulting timestamp is measured in 100-nanosecond units since midnight, October 15, 1582 UTC
     * timestamp() from UUID is measured in 100-nanosecond units since midnight, October 15, 1582 UTC
     *
     * The Java timestamp in milliseconds since 1970-01-01 as baseline
     *
     * @return
     */
    static long getStartOfUuidRelativeToUnixEpochInMilliseconds () {
        Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT-0"))
        c.set(Calendar.YEAR, 1582)
        c.set(Calendar.MONTH, Calendar.OCTOBER)
        c.set(Calendar.DAY_OF_MONTH, 15)
        c.set(Calendar.HOUR_OF_DAY, 0)
        c.set(Calendar.MINUTE, 0)
        c.set(Calendar.SECOND, 0)
        c.set(Calendar.MILLISECOND, 0)

        return c.getTimeInMillis()
    }

    //https://www.wolframalpha.com/input/?i=convert+1582-10-15+UTC+to+unix+time
    final static long START_OF_UUID_RELATIVE_TO_UNIX_EPOCH_SECONDS = -12219292800L
    final static long START_OF_UUID_RELATIVE_TO_UNIX_EPOCH_MILLIS = -12219292800L * 1000L

    /**
     * timestamp() from UUID is measured in 100-nanosecond units since midnight, October 15, 1582 UTC,
     * so we must convert for 100ns units to millisecond procession
     * @param tuid
     * @return
     */
    static long getMillisecondsFromUuid (UUID tuid) {

        assert tuid.version() == 1      //ensure its a time based UUID

        // timestamp returns in 10^-7 (100 nano second chunks), 
        // java Date constructor  assumes 10^-3 (millisecond precision)
        // so we have to divide by 10^4 (10,000) to get millisecond precision  
        long milliseconds_since_UUID_baseline = tuid.timestamp() /10000L

    }

    static getDateFromUuid (UUID tuid) {
        // Allocates a Date object and initializes it to represent the specified number of milliseconds since the 
        // standard java (unix) base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT
        // have to add relative offset from UUID start date of unix epoch to get start date in unix time milliseconds 
        new Date (getMillisecondsFromUuid (tuid) + START_OF_UUID_RELATIVE_TO_UNIX_EPOCH_MILLIS )
    }

    static UUID getTimeBasedUuid () {
        UUID tuid = timeBasedGenerator.generate()
    }

}

I've added explanatory comment so that you can follow what you had to do to process the UUID timestamp() method into a format that works for normal Java date and time processing.

Why the Java UUID class can't provide the methods one might expect to make a time-based UUID interoperable with the normal java date/time formats based on normal unix epoch is a mystery to me.

I ran a little test script using the above static methods:

println "get start of epoch in milliseconds " + UuidUtil.getStartOfUuidRelativeToUnixEpochInMilliseconds()
assert UuidUtil.START_OF_UUID_RELATIVE_TO_UNIX_EPOCH_MILLIS == UuidUtil.startOfUuidRelativeToUnixEpochInMilliseconds

UUID tuid = UuidUtil.timeBasedUuid

println "uid : $tuid"

Date date = UuidUtil.getDateFromUuid(tuid)
println "extracted date from uid is " + UuidUtil.getDateFromUuid(tuid)

and got this

get start of epoch in milliseconds -12219292800000
uid : 43acb588-7d39-11e8-b37b-59f77bf2d333
extracted date from uid is Sun Jul 01 15:15:53 BST 2018

which looked correct for time the script was run.

Chromic answered 1/7, 2018 at 14:32 Comment(0)
T
0

The library 'uuid-creator' has a utility class that helps to exctract UUID parts like time and node id. See this exemple:

long milliseconds = UuidUtil.extractUnixMilliseconds(uuid);

Project: https://github.com/f4b6a3/uuid-creator

Theurich answered 18/6, 2020 at 5:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.