diff --git a/CHANGELOG.md b/CHANGELOG.md index 85cdb95439..2f4f9007fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/concourse-driver-java/src/main/java/com/cinchapi/concourse/Timestamp.java b/concourse-driver-java/src/main/java/com/cinchapi/concourse/Timestamp.java index 43669df671..39f5090aa3 100644 --- a/concourse-driver-java/src/main/java/com/cinchapi/concourse/Timestamp.java +++ b/concourse-driver-java/src/main/java/com/cinchapi/concourse/Timestamp.java @@ -42,18 +42,12 @@ */ @Immutable @ThreadSafe -public final class Timestamp { +public final class Timestamp implements Comparable { // 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. @@ -61,6 +55,12 @@ public final class Timestamp { 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. @@ -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. @@ -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 * ISOChronology in the default time zone. @@ -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()) { @@ -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. @@ -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() diff --git a/concourse-driver-java/src/test/java/com/cinchapi/concourse/util/TimestampsTest.java b/concourse-driver-java/src/test/java/com/cinchapi/concourse/util/TimestampsTest.java index cdc9a5ddfd..6345f29ad8 100644 --- a/concourse-driver-java/src/test/java/com/cinchapi/concourse/util/TimestampsTest.java +++ b/concourse-driver-java/src/test/java/com/cinchapi/concourse/util/TimestampsTest.java @@ -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}. @@ -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 @@ -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)); } @@ -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)); } @@ -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 timestamps = Lists.newArrayList(); + for (int i = 0; i < 10; ++i) { + timestamps.add(Timestamp.now()); + Random.tinySleep(); + } + java.util.Collections.shuffle(timestamps); + Set sorted = Sets.newTreeSet(timestamps); + Timestamp previous = null; + for (Timestamp timestamp : sorted) { + if(previous != null) { + Assert.assertTrue( + previous.getInstant().isBefore(timestamp.getInstant())); + } + previous = timestamp; + } + + } + } diff --git a/concourse-server/src/main/java/com/cinchapi/concourse/server/model/Value.java b/concourse-server/src/main/java/com/cinchapi/concourse/server/model/Value.java index d6617e0652..952dbf2292 100644 --- a/concourse-server/src/main/java/com/cinchapi/concourse/server/model/Value.java +++ b/concourse-server/src/main/java/com/cinchapi/concourse/server/model/Value.java @@ -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 @@ -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