Skip to content

Commit

Permalink
CON-629: Make Timestamps comparable (#348)
Browse files Browse the repository at this point in the history
* make Timestamp implement Comparable

* update changelog

* use the native compareTo method for comparing Timestamps against each other within the Value wrapper

* use internal microseconds for comparability instead of relying on conversion to an Instant which looses some precision
  • Loading branch information
jtnelson committed Sep 3, 2018
1 parent 5adc398 commit 9853b37
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Upgraded to `ccl` version `2.4.1` to capture fix for an bug that caused both the v1 and v2 parsers to mishandle numeric String and Tag values. These values were treated as numbers instead of their actual type. This made it possible for queries containing those values to return inaccurate results.
* Added the associated key to the error message of the Exception that is thrown when attempting to store a blank value.
* Fixed a regression that caused programmatic configuration updates to no longer persist to the underlying file.
* The `Timestamp` data type now implements the `Comparable` interface.

#### Version 0.9.1 (August 12, 2018)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,25 @@
*/
@Immutable
@ThreadSafe
public final class Timestamp {
public final class Timestamp implements Comparable<Timestamp> {

// Joda DateTime uses millisecond instead of microsecond precision, so this
// class is a wrapper that will handle microseconds so we don't ever lose
// data that happens within the same millisecond.

/**
* The default formatter that is used to display objects of this class.
*/
public static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormat
.forPattern("E MMM dd, yyyy @ h:mm:ss:SS a z");

/**
* The default formatter that is used to display {@link #isDateOnly() date
* only} timestamps.
*/
public static final DateTimeFormatter DEFAULT_DATE_ONLY_FORMATTER = DateTimeFormat
.forPattern("E MMM dd, yyyy");

/**
* The default formatter that is used to display objects of this class.
*/
public static final DateTimeFormatter DEFAULT_FORMATTER = DateTimeFormat
.forPattern("E MMM dd, yyyy @ h:mm:ss:SS a z");

/**
* Return a {@code Timestamp} that corresponds to the system
* epoch timestamp with microsecond precision.
Expand All @@ -71,6 +71,18 @@ public static Timestamp epoch() {
return Timestamp.fromMicros(-1);
}

/**
* Return a {@link Timestamp} that corresponds to the specified
* {@code instant}.
*
* @param instant an {@linm Instant} instance
* @return the corresponding {@link Timestamp}
*/
public static Timestamp fromInstant(Instant instant) {
return new Timestamp(
TimeUnit.MILLISECONDS.toMicros(instant.toEpochMilli()));
}

/**
* Return the {@code Timestamp} that corresponds to the provided joda
* DateTime object.
Expand Down Expand Up @@ -112,18 +124,6 @@ public static Timestamp fromString(String description) {
return new Timestamp(description);
}

/**
* Return a {@link Timestamp} that corresponds to the specified
* {@code instant}.
*
* @param instant an {@linm Instant} instance
* @return the corresponding {@link Timestamp}
*/
public static Timestamp fromInstant(Instant instant) {
return new Timestamp(
TimeUnit.MILLISECONDS.toMicros(instant.toEpochMilli()));
}

/**
* Return a {@code Timestamp} set the current system microsecond time using
* <code>ISOChronology</code> in the default time zone.
Expand Down Expand Up @@ -255,6 +255,11 @@ private Timestamp(String description) {
this.description = description;
}

@Override
public int compareTo(Timestamp other) {
return Longs.compare(getMicros(), other.getMicros());
}

@Override
public boolean equals(Object obj) {
if(obj instanceof Timestamp && !isString()) {
Expand All @@ -265,6 +270,17 @@ public boolean equals(Object obj) {
return false;
}

/**
* Return an {@link Instant} that corresponds to the point on the time-line
* represented by this {@link Timestamp}.
*
* @return the corresponding {@link Instant}
*/
public Instant getInstant() {
return Instant
.ofEpochMilli(TimeUnit.MICROSECONDS.toMillis(getMicros()));
}

/**
* Return the Joda {@link DateTime} object that corresponds to this
* Timestamp.
Expand All @@ -291,17 +307,6 @@ public long getMicros() {
return microseconds;
}

/**
* Return an {@link Instant} that corresponds to the point on the time-line
* represented by this {@link Timestamp}.
*
* @return the corresponding {@link Instant}
*/
public Instant getInstant() {
return Instant
.ofEpochMilli(TimeUnit.MICROSECONDS.toMillis(microseconds));
}

@Override
public int hashCode() {
return isString() ? description.hashCode()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@
*/
package com.cinchapi.concourse.util;

import static org.junit.Assert.assertEquals;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.junit.Assert;
import org.junit.Test;

import com.cinchapi.concourse.Timestamp;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
* Unit tests for {@link com.cinchapi.concourse.util.Timestamps}.
Expand All @@ -45,8 +47,8 @@ public void testFindNearestSuccessorForTimestampWithStartTimestampLessThanFirstT
Iterables.getLast(timestamps).getMicros() + increment));
}
Timestamp startTimestamp = Timestamp.epoch();
assertEquals(0, Timestamps.findNearestSuccessorForTimestamp(timestamps,
startTimestamp));
Assert.assertEquals(0, Timestamps
.findNearestSuccessorForTimestamp(timestamps, startTimestamp));
}

@Test
Expand All @@ -63,7 +65,7 @@ public void testFindNearestSuccessorForTimestampWithStartTimestampGreaterThanLas
}
Timestamp startTimestamp = Timestamp
.fromMicros(Iterables.getLast(timestamps).getMicros() + 1000L);
assertEquals(timestamps.size(), Timestamps
Assert.assertEquals(timestamps.size(), Timestamps
.findNearestSuccessorForTimestamp(timestamps, startTimestamp));
}

Expand All @@ -81,15 +83,15 @@ public void testFindNearestSuccessorForTimestampWithStartTimestampEqualToATimest
}
Timestamp startTimestamp = Timestamp
.fromMicros(Iterables.getFirst(timestamps, null).getMicros());
assertEquals(0, Timestamps.findNearestSuccessorForTimestamp(timestamps,
startTimestamp));
Assert.assertEquals(0, Timestamps
.findNearestSuccessorForTimestamp(timestamps, startTimestamp));
startTimestamp = Timestamp.fromMicros(
Iterables.get(timestamps, timestamps.size() / 2).getMicros());
assertEquals(timestamps.size() / 2, Timestamps
Assert.assertEquals(timestamps.size() / 2, Timestamps
.findNearestSuccessorForTimestamp(timestamps, startTimestamp));
startTimestamp = Timestamp
.fromMicros(Iterables.getLast(timestamps).getMicros());
assertEquals(timestamps.size() - 1, Timestamps
Assert.assertEquals(timestamps.size() - 1, Timestamps
.findNearestSuccessorForTimestamp(timestamps, startTimestamp));
}

Expand All @@ -110,8 +112,28 @@ public void testFindNearestSuccessorForTimestampWithStartTimestampGreaterThanFir
timestamps.size() / 3 + 1);
Timestamp startTimestamp = Timestamp.fromMicros(
(abitrary.getMicros() + abitrarySuccessor.getMicros()) / 2);
assertEquals(timestamps.size() / 3 + 1, Timestamps
Assert.assertEquals(timestamps.size() / 3 + 1, Timestamps
.findNearestSuccessorForTimestamp(timestamps, startTimestamp));
}

@Test
public void testTimestampComparableInChronologicalOrder() {
List<Timestamp> timestamps = Lists.newArrayList();
for (int i = 0; i < 10; ++i) {
timestamps.add(Timestamp.now());
Random.tinySleep();
}
java.util.Collections.shuffle(timestamps);
Set<Timestamp> sorted = Sets.newTreeSet(timestamps);
Timestamp previous = null;
for (Timestamp timestamp : sorted) {
if(previous != null) {
Assert.assertTrue(
previous.getInstant().isBefore(timestamp.getInstant()));
}
previous = timestamp;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import com.cinchapi.concourse.util.ByteBuffers;
import com.cinchapi.concourse.util.Convert;
import com.cinchapi.concourse.util.Numbers;
import com.google.common.primitives.Longs;

/**
* A Value is an abstraction for a {@link TObject} that records type information
Expand Down Expand Up @@ -362,8 +361,7 @@ else if(o2 instanceof Number) {
return 1;
}
else if(o1 instanceof Timestamp && o2 instanceof Timestamp) {
return Longs.compare(((Timestamp) o1).getMicros(),
((Timestamp) o2).getMicros());
return ((Timestamp) o1).compareTo((Timestamp) o2);
}
else {
// NOTE: Timestamp's #toString may change depending upon the
Expand Down

0 comments on commit 9853b37

Please sign in to comment.