All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.permazen.kv.raft.Timestamp Maven / Gradle / Ivy


/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen.kv.raft;

import com.google.common.base.Preconditions;

import java.util.Comparator;

/**
 * Represents a relative timestamp in milliseconds.
 *
 * 

* Values are stored as an unsigned 32 bit integer, which therefore recycles after 232 milliseconds (about 48 days). * Two values to be {@linkplain #compareTo compared} must have been generated within 24 days of each other to be ordered correctly. * *

* This class uses {@link System#nanoTime}, not {@link System#currentTimeMillis}, and so is immune to changes in the system clock. * To facilitate debugging, the zero mark is set at class initialization time. * *

* Instances are immutable. */ public class Timestamp implements Comparable { /** * Sorts possibly null {@link Timestamp}s in chronological order, with null sorting first. * *

* All non-null {@link Timestamp}s must be contained within a single 231-1 range; otherwise results are undefined. */ public static final Comparator NULL_FIRST_SORT = Comparator.nullsFirst(Comparator.naturalOrder()); // Make timestamps start at zero to facilitate debugging private static final int TIME_BASE = Timestamp.milliTime(); private final int millis; /** * Construtor returning the current time. */ public Timestamp() { this(Timestamp.now()); } /** * Constructor. * * @param millis relative milliseconds value from {@link #getMillis} */ public Timestamp(int millis) { this.millis = millis; } /** * Get the relative milliseconds value contained by this instance. * * @return relative millisecond value */ public int getMillis() { return this.millis; } /** * Get the number of milliseconds this instance is offset from the current time. * *

* A positive offset means this instance is in the future. * * @return relative millisecond offset */ public int offsetFromNow() { return this.millis - Timestamp.now(); } /** * Get the number of milliseconds this instance is offset from the given instance. * *

* A positive offset means this instance is after {@code base}. * * @param base base timestamp * @return relative millisecond offset * @throws IllegalArgumentException if {@code base} is null */ public int offsetFrom(Timestamp base) { Preconditions.checkArgument(base != null, "null base"); return this.millis - base.millis; } /** * Return this timestamp offset by the given amount. * * @param offset offset in milliseconds * @return adjusted timestamp */ public Timestamp offset(int offset) { return new Timestamp(this.millis + offset); } /** * Determine whether this timestamp is in the past or the future. * * @return true if this timestamp is in the past */ public boolean hasOccurred() { return Timestamp.now() - this.millis >= 0; } /** * Determine whether this timestamp is so far in the past that it is in danger of rolling over to the future * as time continues to move forward. * *

* This returns true if this timestamp is within 5% of the roll-over point relative to the current time. * This would represent a time approximately 22.8 days in the past. * * @return true if this timestamp's offset is dangerously negative */ public boolean isRolloverDanger() { return this.offsetFromNow() <= (int)(Integer.MIN_VALUE * 0.95f); } // Internal methods private static int now() { return Timestamp.milliTime() - TIME_BASE; } private static int milliTime() { return (int)(System.nanoTime() / 1000000L); } // Object @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || obj.getClass() != this.getClass()) return false; final Timestamp that = (Timestamp)obj; return this.millis == that.millis; } @Override public int hashCode() { return this.millis; } @Override public String toString() { final long value = this.millis & 0xFFFFFFFFL; return String.format("%05d.%03d", value / 1000, value % 1000); } // Comparable /** * Compare two instances, where "smaller" means earlier in time. * *

* Note: because timestamps recycle every 48 days, this method does not totally order instances. * * @param that timestamp to compare with * @throws IllegalArgumentException if this instance and {@code that} differ by exactly 231 milliseconds * @throws NullPointerException if {@code that} is null */ @Override public int compareTo(Timestamp that) { Preconditions.checkArgument(this.millis != (that.millis ^ 0x80000000)); final int diff = this.millis - that.millis; return diff < 0 ? -1 : diff > 0 ? 1 : 0; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy