io.opencensus.common.Timestamp Maven / Gradle / Ivy
/*
* Copyright 2016-17, OpenCensus Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opencensus.common;
import static io.opencensus.common.TimeUtils.MAX_NANOS;
import static io.opencensus.common.TimeUtils.MAX_SECONDS;
import static io.opencensus.common.TimeUtils.MILLIS_PER_SECOND;
import static io.opencensus.common.TimeUtils.NANOS_PER_MILLI;
import static io.opencensus.common.TimeUtils.NANOS_PER_SECOND;
import com.google.auto.value.AutoValue;
import java.math.BigDecimal;
import java.math.RoundingMode;
import javax.annotation.concurrent.Immutable;
/**
* A representation of an instant in time. The instant is the number of nanoseconds after the number
* of seconds since the Unix Epoch.
*
* Use {@code Tracing.getClock().now()} to get the current timestamp since epoch
* (1970-01-01T00:00:00Z).
*
* @since 0.5
*/
@Immutable
@AutoValue
public abstract class Timestamp implements Comparable {
Timestamp() {}
/**
* Creates a new timestamp from given seconds and nanoseconds.
*
* @param seconds Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must be
* from from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.
* @param nanos Non-negative fractions of a second at nanosecond resolution. Negative second
* values with fractions must still have non-negative nanos values that count forward in time.
* Must be from 0 to 999,999,999 inclusive.
* @return new {@code Timestamp} with specified fields.
* @throws IllegalArgumentException if the arguments are out of range.
* @since 0.5
*/
public static Timestamp create(long seconds, int nanos) {
if (seconds < -MAX_SECONDS) {
throw new IllegalArgumentException(
"'seconds' is less than minimum (" + -MAX_SECONDS + "): " + seconds);
}
if (seconds > MAX_SECONDS) {
throw new IllegalArgumentException(
"'seconds' is greater than maximum (" + MAX_SECONDS + "): " + seconds);
}
if (nanos < 0) {
throw new IllegalArgumentException("'nanos' is less than zero: " + nanos);
}
if (nanos > MAX_NANOS) {
throw new IllegalArgumentException(
"'nanos' is greater than maximum (" + MAX_NANOS + "): " + nanos);
}
return new AutoValue_Timestamp(seconds, nanos);
}
/**
* Creates a new timestamp from the given milliseconds.
*
* @param epochMilli the timestamp represented in milliseconds since epoch.
* @return new {@code Timestamp} with specified fields.
* @throws IllegalArgumentException if the number of milliseconds is out of the range that can be
* represented by {@code Timestamp}.
* @since 0.5
*/
public static Timestamp fromMillis(long epochMilli) {
long secs = floorDiv(epochMilli, MILLIS_PER_SECOND);
int mos = (int) floorMod(epochMilli, MILLIS_PER_SECOND);
return create(secs, (int) (mos * NANOS_PER_MILLI)); // Safe int * NANOS_PER_MILLI
}
/**
* Returns the number of seconds since the Unix Epoch represented by this timestamp.
*
* @return the number of seconds since the Unix Epoch.
* @since 0.5
*/
public abstract long getSeconds();
/**
* Returns the number of nanoseconds after the number of seconds since the Unix Epoch represented
* by this timestamp.
*
* @return the number of nanoseconds after the number of seconds since the Unix Epoch.
* @since 0.5
*/
public abstract int getNanos();
/**
* Returns a {@code Timestamp} calculated as this {@code Timestamp} plus some number of
* nanoseconds.
*
* @param nanosToAdd the nanos to add, positive or negative.
* @return the calculated {@code Timestamp}. For invalid inputs, a {@code Timestamp} of zero is
* returned.
* @throws ArithmeticException if numeric overflow occurs.
* @since 0.5
*/
public Timestamp addNanos(long nanosToAdd) {
return plus(0, nanosToAdd);
}
/**
* Returns a {@code Timestamp} calculated as this {@code Timestamp} plus some {@code Duration}.
*
* @param duration the {@code Duration} to add.
* @return a {@code Timestamp} with the specified {@code Duration} added.
* @since 0.5
*/
public Timestamp addDuration(Duration duration) {
return plus(duration.getSeconds(), duration.getNanos());
}
/**
* Returns a {@link Duration} calculated as: {@code this - timestamp}.
*
* @param timestamp the {@code Timestamp} to subtract.
* @return the calculated {@code Duration}. For invalid inputs, a {@code Duration} of zero is
* returned.
* @since 0.5
*/
public Duration subtractTimestamp(Timestamp timestamp) {
long durationSeconds = getSeconds() - timestamp.getSeconds();
int durationNanos = getNanos() - timestamp.getNanos();
if (durationSeconds < 0 && durationNanos > 0) {
durationSeconds += 1;
durationNanos = (int) (durationNanos - NANOS_PER_SECOND);
} else if (durationSeconds > 0 && durationNanos < 0) {
durationSeconds -= 1;
durationNanos = (int) (durationNanos + NANOS_PER_SECOND);
}
return Duration.create(durationSeconds, durationNanos);
}
/**
* Compares this {@code Timestamp} to the specified {@code Timestamp}.
*
* @param otherTimestamp the other {@code Timestamp} to compare to, not {@code null}.
* @return the comparator value: zero if equal, negative if this timestamp happens before
* otherTimestamp, positive if after.
* @throws NullPointerException if otherTimestamp is {@code null}.
*/
@Override
public int compareTo(Timestamp otherTimestamp) {
int cmp = TimeUtils.compareLongs(getSeconds(), otherTimestamp.getSeconds());
if (cmp != 0) {
return cmp;
}
return TimeUtils.compareLongs(getNanos(), otherTimestamp.getNanos());
}
// Returns a Timestamp with the specified duration added.
private Timestamp plus(long secondsToAdd, long nanosToAdd) {
if ((secondsToAdd | nanosToAdd) == 0) {
return this;
}
long epochSec = TimeUtils.checkedAdd(getSeconds(), secondsToAdd);
epochSec = TimeUtils.checkedAdd(epochSec, nanosToAdd / NANOS_PER_SECOND);
nanosToAdd = nanosToAdd % NANOS_PER_SECOND;
long nanoAdjustment = getNanos() + nanosToAdd; // safe int + NANOS_PER_SECOND
return ofEpochSecond(epochSec, nanoAdjustment);
}
// Returns a Timestamp calculated using seconds from the epoch and nanosecond fraction of
// second (arbitrary number of nanoseconds).
private static Timestamp ofEpochSecond(long epochSecond, long nanoAdjustment) {
long secs = TimeUtils.checkedAdd(epochSecond, floorDiv(nanoAdjustment, NANOS_PER_SECOND));
int nos = (int) floorMod(nanoAdjustment, NANOS_PER_SECOND);
return create(secs, nos);
}
// Returns the result of dividing x by y rounded using floor.
private static long floorDiv(long x, long y) {
return BigDecimal.valueOf(x).divide(BigDecimal.valueOf(y), 0, RoundingMode.FLOOR).longValue();
}
// Returns the floor modulus "x - (floorDiv(x, y) * y)"
private static long floorMod(long x, long y) {
return x - floorDiv(x, y) * y;
}
}