javascalatimecassandratimeuuid

get timebased uuid's upto 100s of nanoseconds


I am using this libraryDependencies += "com.datastax.oss" % "java-driver-core" % "4.3.0" library for creating time based uuid's. Although it generates time based uuid's it gives me upto seconds but i am looking for values in 100s of nano seconds

import com.datastax.oss.driver.api.core.uuid.Uuids
println(Uuids.timeBased().toString)

The output uuid is something like f642f350-0230-11ea-a02f-597f2801796a which corresponds to Friday, November 8, 2019 at 2:06:30 PM Greenwich Mean Time

Please help on how to get time in milliseconds seomething like this Friday, November 8, 2019 at 2:06:30:0000000Z PM Greenwich Mean Time

I want timestamp to be converted in uuid format for testing(tests accept only uuid format). i would then convert uuid back to time to measure some time difference.


Solution

  • There are a few steps here. The first is to convert the time-based UUID's timestamp (which is in 100s of nanoseconds from October 15, 1582) to one compatible with Java's date functionality (i.e. milliseconds from January 1, 1970). Notably, you asked for better than millisecond precision.

    Next, we need to interpret that date into the correct time zone.

    Finally, we need to format it into text in the desired format.

    Here's the code:

    // this is the difference between midnight October 15, 1582 UTC and midnight January 1, 1970 UTC as 100 nanosecond units
    private static final long EPOCH_DIFFERENCE = 122192928000000000L;
    
    private static final ZoneId GREENWICH_MEAN_TIME = ZoneId.of("GMT");
    
    private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
            .appendText(DAY_OF_WEEK, FULL)
            .appendLiteral(", ")
            .appendText(MONTH_OF_YEAR, FULL)
            .appendLiteral(' ')
            .appendValue(DAY_OF_MONTH)
            .appendLiteral(", ")
            .appendValue(YEAR, 4)
            .appendLiteral(" at ")
            .appendValue(CLOCK_HOUR_OF_AMPM)
            .appendLiteral(':')
            .appendValue(MINUTE_OF_HOUR, 2)
            .appendLiteral(':')
            .appendValue(SECOND_OF_MINUTE, 2)
            .appendLiteral('.')
            .appendFraction(NANO_OF_SECOND, 7, 7, false)
            .appendLiteral(' ')
            .appendText(AMPM_OF_DAY)
            .appendLiteral(' ')
            .appendZoneText(FULL)
            .toFormatter(Locale.getDefault());
    
    public static String formattedDateFromTimeBasedUuid(UUID uuid) {
        ZonedDateTime date = timeBasedUuidToDate(uuid);
        return FORMATTER.format(date);
    }
    
    public static ZonedDateTime timeBasedUuidToDate(UUID uuid) {
        if (uuid.version() != 1) {
            throw new IllegalArgumentException("Provided UUID was not time-based.");
        }
        // the UUID timestamp is in 100 nanosecond units.
        // convert that to nanoseconds
        long nanoseconds = (uuid.timestamp() - EPOCH_DIFFERENCE) * 100;
        long milliseconds = nanoseconds / 1000000000;
        long nanoAdjustment = nanoseconds % 1000000000;
        Instant instant = Instant.ofEpochSecond(milliseconds, nanoAdjustment);
        return ZonedDateTime.ofInstant(instant, GREENWICH_MEAN_TIME);
    }
    

    I'd drop those methods and constants in a utility class for convenient reuse.

    A couple notes:

    To determine the amount of time between two ZonedDateTimes, you just do this:

    Duration duration = Duration.between(date1, date2);
    

    The Duration class has several useful methods you can use to interpret the result.