Skip to content

Commit

Permalink
#105 UUIDUtil.extractTimestamp() is broken (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
magdel authored May 21, 2024
1 parent f59ec83 commit 190040a
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 9 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Releases

#99: New factory method to create TimeBasedEpochRandomGenerator
(contributed by Daniel A)
#105: `UUIDUtil.extractTimestamp()` is broken for versions 1 and 6
(contributed by @magdel)

5.0.0 (23-Feb-2024)

Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/fasterxml/uuid/UUIDTimer.java
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,20 @@ public synchronized long getTimestamp()
return systime;
}

/**
* Converts a UUID v1 or v6 timestamp (where unit is 100 nanoseconds),
* to Unix epoch timestamp (milliseconds since 01-Jan-1970 UTC)
*
* @param timestamp Timestamp used to create UUID versions 1 and 6
*
* @return Unix epoch timestamp
*
* @since 5.1
*/
public static long timestampToEpoch(long timestamp) {
return (timestamp - kClockOffset) / kClockMultiplierL;
}

/*
/**********************************************************************
/* Test-support methods
Expand Down
31 changes: 24 additions & 7 deletions src/main/java/com/fasterxml/uuid/impl/UUIDUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.UUID;

import com.fasterxml.uuid.UUIDTimer;
import com.fasterxml.uuid.UUIDType;

public class UUIDUtil
Expand Down Expand Up @@ -360,7 +361,7 @@ private final static void _checkUUIDByteArray(byte[] bytes, int offset)
*
* @param uuid uuid timestamp to extract from
*
* @return timestamp in milliseconds (since Epoch), or 0 if type does not support timestamps
* @return Unix timestamp in milliseconds (since Epoch), or 0 if type does not support timestamps
*
* @since 5.0
*/
Expand All @@ -380,17 +381,25 @@ public static long extractTimestamp(UUID uuid)
case NAME_BASED_MD5:
return 0L;
case TIME_BASED:
return _getTimestampFromUuidV1(uuid);
return UUIDTimer.timestampToEpoch(_getRawTimestampFromUuidV1(uuid));
case TIME_BASED_REORDERED:
return _getTimestampFromUuidV6(uuid);
return UUIDTimer.timestampToEpoch(_getRawTimestampFromUuidV6(uuid));
case TIME_BASED_EPOCH:
return _getTimestampFromUuidV7(uuid);
return _getRawTimestampFromUuidV7(uuid);
default:
throw new IllegalArgumentException("Invalid `UUID`: unexpected type " + type);
}
}

private static long _getTimestampFromUuidV1(UUID uuid) {
/**
* Get raw timestamp, used to create the UUID v1
*<p>
* NOTE: no verification is done to ensure UUID given is of version 1.
*
* @param uuid uuid, to extract timestamp from
* @return timestamp, used to create uuid v1
*/
static long _getRawTimestampFromUuidV1(UUID uuid) {
long mostSignificantBits = uuid.getMostSignificantBits();
mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1110_1111_1111_1111L;
long low = mostSignificantBits >>> 32;
Expand All @@ -400,7 +409,15 @@ private static long _getTimestampFromUuidV1(UUID uuid) {
return highOfHigher << 48 | lowOfHigher << 32 | low;
}

private static long _getTimestampFromUuidV6(UUID uuid) {
/**
* Get raw timestamp, used to create the UUID v6.
*<p>
* NOTE: no verification is done to ensure UUID given is of version 6.
*
* @param uuid uuid, to extract timestamp from
* @return timestamp, used to create uuid v6
*/
static long _getRawTimestampFromUuidV6(UUID uuid) {
long mostSignificantBits = uuid.getMostSignificantBits();
mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1001_1111_1111_1111L;
long lowL = mostSignificantBits & 0xFFFL;
Expand All @@ -410,7 +427,7 @@ private static long _getTimestampFromUuidV6(UUID uuid) {
return high >>> 4 | lowH << 12 | lowL;
}

private static long _getTimestampFromUuidV7(UUID uuid) {
static long _getRawTimestampFromUuidV7(UUID uuid) {
long mostSignificantBits = uuid.getMostSignificantBits();
mostSignificantBits = mostSignificantBits & 0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1001_1111_1111_1111L;
return mostSignificantBits >>> 16;
Expand Down
28 changes: 26 additions & 2 deletions src/test/java/com/fasterxml/uuid/impl/UUIDUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.UUID;

import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.NoArgGenerator;
import junit.framework.TestCase;

/**
Expand Down Expand Up @@ -37,20 +38,35 @@ public void testExtractTimestampUUIDTimeBased() {
for (int i = 0; i < TEST_REPS; i++) {
long rawTimestamp = rnd.nextLong() >>> 4;
UUID uuid = generator.construct(rawTimestamp);
assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid));
assertEquals(rawTimestamp, UUIDUtil._getRawTimestampFromUuidV1(uuid));
}
}

public void testExtractTimestampUUIDTimeBasedCurrentTimemillis() {
TimeBasedGenerator generator = Generators.timeBasedGenerator();
long time = System.currentTimeMillis();
UUID uuid2 = generator.generate();
assertEquals(time, UUIDUtil.extractTimestamp(uuid2));
}


public void testExtractTimestampUUIDTimeBasedReordered() {
TimeBasedReorderedGenerator generator = Generators.timeBasedReorderedGenerator();
final Random rnd = new Random(2);
for (int i = 0; i < TEST_REPS; i++) {
long rawTimestamp = rnd.nextLong() >>> 4;
UUID uuid = generator.construct(rawTimestamp);
assertEquals(rawTimestamp, UUIDUtil.extractTimestamp(uuid));
assertEquals(rawTimestamp, UUIDUtil._getRawTimestampFromUuidV6(uuid));
}
}

public void testExtractTimestampUUIDTimeBasedReorderedCurrentTimeMillis() {
NoArgGenerator generator = Generators.timeBasedReorderedGenerator();
long time = System.currentTimeMillis();
UUID uuid = generator.generate();
assertEquals(time, UUIDUtil.extractTimestamp(uuid));
}

public void testExtractTimestampUUIDEpochBased() {
TimeBasedEpochGenerator generator = Generators.timeBasedEpochGenerator();
final Random rnd = new Random(3);
Expand All @@ -61,6 +77,14 @@ public void testExtractTimestampUUIDEpochBased() {
}
}

public void testExtractTimestampUUIDEpochBasedCurrentTimeMillis() {
NoArgGenerator generator = Generators.timeBasedEpochGenerator();
long time = System.currentTimeMillis();
UUID uuid = generator.generate();
assertEquals(time, UUIDUtil.extractTimestamp(uuid));
}


public void testExtractTimestampUUIDEpochRandomBased() {
TimeBasedEpochRandomGenerator generator = Generators.timeBasedEpochRandomGenerator();
final Random rnd = new Random(3);
Expand Down

0 comments on commit 190040a

Please sign in to comment.