• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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