Initialize the monotonic factory with the minimum timestamp

Now the `MonotonicFunction` inner class is initialized with the instant
1970-01-01 00:00:00.000 UTC, which is represented by the value 0L.

Also updated the documentation and the unit tests.
This commit is contained in:
Fabio Lima 2023-09-02 21:28:15 -03:00
parent c7191c6e27
commit dbf31d3be7
3 changed files with 23 additions and 20 deletions

View File

@ -149,10 +149,14 @@ public final class Ulid implements Serializable, Comparable<Ulid> {
/** /**
* Creates a new ULID. * Creates a new ULID.
* <p> * <p>
* Time parameter is the number of milliseconds since 1970-01-01 (Unix epoch). * The time parameter is the number of milliseconds since 1970-01-01, also known
* It must be a positive number not larger than 2^48-1. * as Unix epoch. It must be a positive number not larger than 2^48-1.
* <p> * <p>
* Random parameter must be an array of 10 bytes. * The random parameter must be an arbitrary array of 10 bytes.
* <p>
* Note: ULIDs cannot be composed of dates before 1970-01-01, as their embedded
* timestamp is internally treated as an unsigned integer, i.e., it can only
* represent the set of natural numbers including zero, up to 2^48-1.
* *
* @param time the number of milliseconds since 1970-01-01 * @param time the number of milliseconds since 1970-01-01
* @param random an array of 10 bytes * @param random an array of 10 bytes

View File

@ -360,7 +360,8 @@ public final class UlidFactory {
} }
void initialize(LongSupplier timeFunction) { void initialize(LongSupplier timeFunction) {
this.lastUlid = new Ulid(timeFunction.getAsLong(), this.random.nextBytes(Ulid.RANDOM_BYTES)); // initialize the factory with the instant 1970-01-01 00:00:00.000 UTC
this.lastUlid = new Ulid(0L, this.random.nextBytes(Ulid.RANDOM_BYTES));
} }
@Override @Override

View File

@ -39,8 +39,7 @@ public class UlidFactoryMonotonicTest extends UlidFactoryTest {
long diff = UlidFactory.MonotonicFunction.CLOCK_DRIFT_TOLERANCE; long diff = UlidFactory.MonotonicFunction.CLOCK_DRIFT_TOLERANCE;
long time = Instant.parse("2021-12-31T23:59:59.000Z").toEpochMilli(); long time = Instant.parse("2021-12-31T23:59:59.000Z").toEpochMilli();
long times[] = { /* init */ 0L, time + 0, time + 1, time + 2, time + 3, time + 4 - diff, time + 5 - diff, long times[] = { time + 0, time + 1, time + 2, time + 3, time + 4 - diff, time + 5 - diff, time + 6 - diff };
time + 6 - diff };
Clock clock = new Clock() { Clock clock = new Clock() {
private int i; private int i;
@ -113,8 +112,11 @@ public class UlidFactoryMonotonicTest extends UlidFactoryTest {
@Test @Test
public void testGetMonotonicUlidAfterLeapSecond() { public void testGetMonotonicUlidAfterLeapSecond() {
// The best article about leap seconds:
// (Unfortunately it can't be translated)
// https://ntp.br/conteudo/artigo-leap-second/
long time = Instant.parse("2021-12-31T23:59:59.000Z").toEpochMilli(); long time = Instant.parse("2021-12-31T23:59:59.000Z").toEpochMilli();
long leap = time - 1000; // simulate a leap second long leap = time - 1000; // moving the clock hands 1 second backwards
long times[] = { time, leap }; long times[] = { time, leap };
Clock clock = new Clock() { Clock clock = new Clock() {
@ -144,28 +146,26 @@ public class UlidFactoryMonotonicTest extends UlidFactoryTest {
LongSupplier randomFunction = () -> 0; LongSupplier randomFunction = () -> 0;
UlidFactory factory = UlidFactory.newMonotonicInstance(randomFunction, clock); UlidFactory factory = UlidFactory.newMonotonicInstance(randomFunction, clock);
// the clock moved normally
Ulid ulid1 = factory.create(); Ulid ulid1 = factory.create();
Ulid ulid2 = factory.create();
long t1 = ulid1.getTime(); long t1 = ulid1.getTime();
long t2 = ulid2.getTime(); // leap second
long r1 = ulid1.getLeastSignificantBits(); long r1 = ulid1.getLeastSignificantBits();
long r2 = ulid2.getLeastSignificantBits(); // leap second
assertEquals(time, t1); assertEquals(time, t1);
assertEquals(time, t2); // leap second assertEquals(0, r1);
assertEquals(1, r1);
assertEquals(2, r2);
// the clock moved backwards
Ulid ulid2 = factory.create();
long t2 = ulid2.getTime();
long r2 = ulid2.getLeastSignificantBits();
assertEquals(time, t2); // should freeze
assertEquals(1, r2); // should increment
} }
@Test @Test
public void testGetMonotonicUlidAfterRandomBitsOverflowFollowedByTimeBitsIncrement() { public void testGetMonotonicUlidAfterRandomBitsOverflowFollowedByTimeBitsIncrement() {
long time = Instant.parse("2021-12-31T23:59:59.999Z").toEpochMilli(); long time = Instant.parse("2021-12-31T23:59:59.999Z").toEpochMilli();
long times[] = { /* init */ 0L, time + 1, time + 2, time + 3, time, time, time }; long times[] = { time + 1, time + 2, time + 3, time, time, time };
Clock clock = new Clock() { Clock clock = new Clock() {
private int i; private int i;
@ -194,8 +194,6 @@ public class UlidFactoryMonotonicTest extends UlidFactoryTest {
LongSupplier randomSupplier = () -> 0xffffffffffffffffL; LongSupplier randomSupplier = () -> 0xffffffffffffffffL;
UlidFactory factory = UlidFactory.newMonotonicInstance(randomSupplier, clock); UlidFactory factory = UlidFactory.newMonotonicInstance(randomSupplier, clock);
// System.out.println("time: " + time); // 1640995199999
Ulid ulid1 = factory.create(); Ulid ulid1 = factory.create();
Ulid ulid2 = factory.create(); Ulid ulid2 = factory.create();
Ulid ulid3 = factory.create(); Ulid ulid3 = factory.create();