This repository has been archived by the owner on Dec 3, 2023. It is now read-only.
/
Timestamp.java
233 lines (202 loc) · 7.71 KB
/
Timestamp.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
* Copyright 2017 Google LLC
*
* 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 com.google.cloud;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.protobuf.util.Timestamps;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.threeten.bp.Instant;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.format.DateTimeFormatter;
import org.threeten.bp.format.DateTimeFormatterBuilder;
import org.threeten.bp.temporal.TemporalAccessor;
/**
* Represents a timestamp with nanosecond precision. Timestamps cover the range [0001-01-01,
* 9999-12-31].
*
* <p>{@code Timestamp} instances are immutable.
*/
public final class Timestamp implements Comparable<Timestamp>, Serializable {
private static final long serialVersionUID = 5152143600571559844L;
/** The smallest legal timestamp ("0001-01-01T00:00:00Z"). */
public static final Timestamp MIN_VALUE = new Timestamp(-62135596800L, 0);
/** The largest legal timestamp ("9999-12-31T23:59:59Z"). */
public static final Timestamp MAX_VALUE =
new Timestamp(253402300799L, (int) TimeUnit.SECONDS.toNanos(1) - 1);
private static final DateTimeFormatter format = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
private static final DateTimeFormatter timestampParser =
new DateTimeFormatterBuilder()
.appendOptional(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart()
.appendOffsetId()
.optionalEnd()
.toFormatter()
.withZone(ZoneOffset.UTC);
private final long seconds;
private final int nanos;
private Timestamp(long seconds, int nanos) {
this.seconds = seconds;
this.nanos = nanos;
}
/**
* Creates an instance representing the value of {@code seconds} and {@code nanos} since January
* 1, 1970, 00:00:00 UTC.
*
* @param seconds seconds since January 1, 1970, 00:00:00 UTC. A negative value is the number of
* seconds before January 1, 1970, 00:00:00 UTC.
* @param nanos the fractional seconds component, in the range 0..999999999.
* @throws IllegalArgumentException if the timestamp is outside the representable range
*/
public static Timestamp ofTimeSecondsAndNanos(long seconds, int nanos) {
checkArgument(
Timestamps.isValid(seconds, nanos), "timestamp out of range: %s, %s", seconds, nanos);
return new Timestamp(seconds, nanos);
}
/**
* Creates an instance representing the value of {@code microseconds}.
*
* @throws IllegalArgumentException if the timestamp is outside the representable range
*/
public static Timestamp ofTimeMicroseconds(long microseconds) {
long seconds = microseconds / 1_000_000;
int nanos = (int) (microseconds % 1_000_000 * 1000);
if (nanos < 0) {
seconds--;
nanos += 1_000_000_000;
}
checkArgument(
Timestamps.isValid(seconds, nanos), "timestamp out of range: %s, %s", seconds, nanos);
return new Timestamp(seconds, nanos);
}
/**
* Creates an instance representing the value of {@code Date}.
*
* @throws IllegalArgumentException if the timestamp is outside the representable range
*/
public static Timestamp of(Date date) {
return ofTimeMicroseconds(TimeUnit.MILLISECONDS.toMicros(date.getTime()));
}
/** Creates an instance with current time. */
public static Timestamp now() {
java.sql.Timestamp date = new java.sql.Timestamp(System.currentTimeMillis());
return of(date);
}
/**
* Creates an instance representing the value of {@code timestamp}.
*
* @throws IllegalArgumentException if the timestamp is outside the representable range
*/
public static Timestamp of(java.sql.Timestamp timestamp) {
int nanos = timestamp.getNanos();
// A pre-epoch timestamp will be off by one second because of the way that integer division
// works. For example, -1001 / 1000 == -1. In this case of timestamps, we want this result to be
// -2. This causes any pre-epoch timestamp to be off by 1 second - fix this by adjusting the
// seconds value by 1 if the timestamp < 0.
// TODO: replace with Math.floorDiv when we drop Java 7 support
long seconds = timestamp.getTime() / 1000;
if (seconds < 0) {
--seconds;
}
return Timestamp.ofTimeSecondsAndNanos(seconds, nanos);
}
/**
* Returns the number of seconds since January 1, 1970, 00:00:00 UTC. A negative value is the
* number of seconds before January 1, 1970, 00:00:00 UTC.
*/
public long getSeconds() {
return seconds;
}
/** Returns the fractional seconds component, in nanoseconds. */
public int getNanos() {
return nanos;
}
/** Returns a JDBC timestamp initialized to the same point in time as {@code this}. */
public java.sql.Timestamp toSqlTimestamp() {
java.sql.Timestamp ts = new java.sql.Timestamp(seconds * 1000);
ts.setNanos(nanos);
return ts;
}
/**
* Returns a new {@code java.util.Date} corresponding to this {@code timestamp}. Any
* sub-millisecond precision will be stripped.
*
* @return An approximate {@code java.util.Date} representation of this {@code timestamp}.
*/
public Date toDate() {
long secondsInMilliseconds = TimeUnit.SECONDS.toMillis(this.seconds);
long nanosInMilliseconds = TimeUnit.NANOSECONDS.toMillis(this.nanos);
return new Date(secondsInMilliseconds + nanosInMilliseconds);
}
@Override
public int compareTo(Timestamp other) {
int r = Long.compare(seconds, other.seconds);
if (r == 0) {
r = Integer.compare(nanos, other.nanos);
}
return r;
}
/** Creates an instance of Timestamp from {@code com.google.protobuf.Timestamp}. */
public static Timestamp fromProto(com.google.protobuf.Timestamp proto) {
return new Timestamp(proto.getSeconds(), proto.getNanos());
}
/**
* Returns a {@code com.google.protobuf.Timestamp} initialized to the same point in time as {@code
* this}.
*/
public com.google.protobuf.Timestamp toProto() {
return com.google.protobuf.Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build();
}
/**
* Creates a Timestamp instance from the given string. String is in the RFC 3339 format without
* the timezone offset (always ends in "Z").
*/
public static Timestamp parseTimestamp(String timestamp) {
TemporalAccessor temporalAccessor = timestampParser.parse(timestamp);
Instant instant = Instant.from(temporalAccessor);
return ofTimeSecondsAndNanos(instant.getEpochSecond(), instant.getNano());
}
private StringBuilder toString(StringBuilder b) {
format.formatTo(LocalDateTime.ofEpochSecond(seconds, 0, ZoneOffset.UTC), b);
if (nanos != 0) {
b.append(String.format(".%09d", nanos));
}
b.append('Z');
return b;
}
@Override
public String toString() {
return toString(new StringBuilder()).toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Timestamp that = (Timestamp) o;
return seconds == that.seconds && nanos == that.nanos;
}
@Override
public int hashCode() {
return Objects.hash(seconds, nanos);
}
// TODO(user): Consider adding math operations.
}