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 Timestamp()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 */ create(long seconds, int nanos)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 */ fromMillis(long epochMilli)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 */ getSeconds()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 */ getNanos()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 */ addNanos(long nanosToAdd)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 */ addDuration(Duration duration)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 */ subtractTimestamp(Timestamp timestamp)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 compareTo(Timestamp otherTimestamp)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. plus(long secondsToAdd, long nanosToAdd)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). ofEpochSecond(long epochSecond, long nanoAdjustment)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. floorDiv(long x, long y)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)" floorMod(long x, long y)197 private static long floorMod(long x, long y) { 198 return x - floorDiv(x, y) * y; 199 } 200 } 201