Home | History | Annotate | Download | only in common
      1 /*
      2  * Copyright 2016-17, OpenCensus Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package io.opencensus.common;
     18 
     19 import static io.opencensus.common.TimeUtils.MAX_NANOS;
     20 import static io.opencensus.common.TimeUtils.MAX_SECONDS;
     21 import static io.opencensus.common.TimeUtils.MILLIS_PER_SECOND;
     22 import static io.opencensus.common.TimeUtils.NANOS_PER_MILLI;
     23 import static io.opencensus.common.TimeUtils.NANOS_PER_SECOND;
     24 
     25 import com.google.auto.value.AutoValue;
     26 import java.math.BigDecimal;
     27 import java.math.RoundingMode;
     28 import javax.annotation.concurrent.Immutable;
     29 
     30 /**
     31  * A representation of an instant in time. The instant is the number of nanoseconds after the number
     32  * of seconds since the Unix Epoch.
     33  *
     34  * <p>Use {@code Tracing.getClock().now()} to get the current timestamp since epoch
     35  * (1970-01-01T00:00:00Z).
     36  *
     37  * @since 0.5
     38  */
     39 @Immutable
     40 @AutoValue
     41 public abstract class Timestamp implements Comparable<Timestamp> {
     42 
     43   Timestamp() {}
     44 
     45   /**
     46    * Creates a new timestamp from given seconds and nanoseconds.
     47    *
     48    * @param seconds Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must be
     49    *     from from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.
     50    * @param nanos Non-negative fractions of a second at nanosecond resolution. Negative second
     51    *     values with fractions must still have non-negative nanos values that count forward in time.
     52    *     Must be from 0 to 999,999,999 inclusive.
     53    * @return new {@code Timestamp} with specified fields.
     54    * @throws IllegalArgumentException if the arguments are out of range.
     55    * @since 0.5
     56    */
     57   public static Timestamp create(long seconds, int nanos) {
     58     if (seconds < -MAX_SECONDS) {
     59       throw new IllegalArgumentException(
     60           "'seconds' is less than minimum (" + -MAX_SECONDS + "): " + seconds);
     61     }
     62     if (seconds > MAX_SECONDS) {
     63       throw new IllegalArgumentException(
     64           "'seconds' is greater than maximum (" + MAX_SECONDS + "): " + seconds);
     65     }
     66     if (nanos < 0) {
     67       throw new IllegalArgumentException("'nanos' is less than zero: " + nanos);
     68     }
     69     if (nanos > MAX_NANOS) {
     70       throw new IllegalArgumentException(
     71           "'nanos' is greater than maximum (" + MAX_NANOS + "): " + nanos);
     72     }
     73     return new AutoValue_Timestamp(seconds, nanos);
     74   }
     75 
     76   /**
     77    * Creates a new timestamp from the given milliseconds.
     78    *
     79    * @param epochMilli the timestamp represented in milliseconds since epoch.
     80    * @return new {@code Timestamp} with specified fields.
     81    * @throws IllegalArgumentException if the number of milliseconds is out of the range that can be
     82    *     represented by {@code Timestamp}.
     83    * @since 0.5
     84    */
     85   public static Timestamp fromMillis(long epochMilli) {
     86     long secs = floorDiv(epochMilli, MILLIS_PER_SECOND);
     87     int mos = (int) floorMod(epochMilli, MILLIS_PER_SECOND);
     88     return create(secs, (int) (mos * NANOS_PER_MILLI)); // Safe int * NANOS_PER_MILLI
     89   }
     90 
     91   /**
     92    * Returns the number of seconds since the Unix Epoch represented by this timestamp.
     93    *
     94    * @return the number of seconds since the Unix Epoch.
     95    * @since 0.5
     96    */
     97   public abstract long getSeconds();
     98 
     99   /**
    100    * Returns the number of nanoseconds after the number of seconds since the Unix Epoch represented
    101    * by this timestamp.
    102    *
    103    * @return the number of nanoseconds after the number of seconds since the Unix Epoch.
    104    * @since 0.5
    105    */
    106   public abstract int getNanos();
    107 
    108   /**
    109    * Returns a {@code Timestamp} calculated as this {@code Timestamp} plus some number of
    110    * nanoseconds.
    111    *
    112    * @param nanosToAdd the nanos to add, positive or negative.
    113    * @return the calculated {@code Timestamp}. For invalid inputs, a {@code Timestamp} of zero is
    114    *     returned.
    115    * @throws ArithmeticException if numeric overflow occurs.
    116    * @since 0.5
    117    */
    118   public Timestamp addNanos(long nanosToAdd) {
    119     return plus(0, nanosToAdd);
    120   }
    121 
    122   /**
    123    * Returns a {@code Timestamp} calculated as this {@code Timestamp} plus some {@code Duration}.
    124    *
    125    * @param duration the {@code Duration} to add.
    126    * @return a {@code Timestamp} with the specified {@code Duration} added.
    127    * @since 0.5
    128    */
    129   public Timestamp addDuration(Duration duration) {
    130     return plus(duration.getSeconds(), duration.getNanos());
    131   }
    132 
    133   /**
    134    * Returns a {@link Duration} calculated as: {@code this - timestamp}.
    135    *
    136    * @param timestamp the {@code Timestamp} to subtract.
    137    * @return the calculated {@code Duration}. For invalid inputs, a {@code Duration} of zero is
    138    *     returned.
    139    * @since 0.5
    140    */
    141   public Duration subtractTimestamp(Timestamp timestamp) {
    142     long durationSeconds = getSeconds() - timestamp.getSeconds();
    143     int durationNanos = getNanos() - timestamp.getNanos();
    144     if (durationSeconds < 0 && durationNanos > 0) {
    145       durationSeconds += 1;
    146       durationNanos = (int) (durationNanos - NANOS_PER_SECOND);
    147     } else if (durationSeconds > 0 && durationNanos < 0) {
    148       durationSeconds -= 1;
    149       durationNanos = (int) (durationNanos + NANOS_PER_SECOND);
    150     }
    151     return Duration.create(durationSeconds, durationNanos);
    152   }
    153 
    154   /**
    155    * Compares this {@code Timestamp} to the specified {@code Timestamp}.
    156    *
    157    * @param otherTimestamp the other {@code Timestamp} to compare to, not {@code null}.
    158    * @return the comparator value: zero if equal, negative if this timestamp happens before
    159    *     otherTimestamp, positive if after.
    160    * @throws NullPointerException if otherTimestamp is {@code null}.
    161    */
    162   @Override
    163   public int compareTo(Timestamp otherTimestamp) {
    164     int cmp = TimeUtils.compareLongs(getSeconds(), otherTimestamp.getSeconds());
    165     if (cmp != 0) {
    166       return cmp;
    167     }
    168     return TimeUtils.compareLongs(getNanos(), otherTimestamp.getNanos());
    169   }
    170 
    171   // Returns a Timestamp with the specified duration added.
    172   private Timestamp plus(long secondsToAdd, long nanosToAdd) {
    173     if ((secondsToAdd | nanosToAdd) == 0) {
    174       return this;
    175     }
    176     long epochSec = TimeUtils.checkedAdd(getSeconds(), secondsToAdd);
    177     epochSec = TimeUtils.checkedAdd(epochSec, nanosToAdd / NANOS_PER_SECOND);
    178     nanosToAdd = nanosToAdd % NANOS_PER_SECOND;
    179     long nanoAdjustment = getNanos() + nanosToAdd; // safe int + NANOS_PER_SECOND
    180     return ofEpochSecond(epochSec, nanoAdjustment);
    181   }
    182 
    183   // Returns a Timestamp calculated using seconds from the epoch and nanosecond fraction of
    184   // second (arbitrary number of nanoseconds).
    185   private static Timestamp ofEpochSecond(long epochSecond, long nanoAdjustment) {
    186     long secs = TimeUtils.checkedAdd(epochSecond, floorDiv(nanoAdjustment, NANOS_PER_SECOND));
    187     int nos = (int) floorMod(nanoAdjustment, NANOS_PER_SECOND);
    188     return create(secs, nos);
    189   }
    190 
    191   // Returns the result of dividing x by y rounded using floor.
    192   private static long floorDiv(long x, long y) {
    193     return BigDecimal.valueOf(x).divide(BigDecimal.valueOf(y), 0, RoundingMode.FLOOR).longValue();
    194   }
    195 
    196   // Returns the floor modulus "x - (floorDiv(x, y) * y)"
    197   private static long floorMod(long x, long y) {
    198     return x - floorDiv(x, y) * y;
    199   }
    200 }
    201