1
1
/*
2
- * Copyright 2021 Google LLC
2
+ * Copyright 2024 Google LLC
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package com .google .cloud .spanner ;
18
18
19
- import com .google .auto .value .AutoValue ;
20
19
import com .google .errorprone .annotations .Immutable ;
21
20
import java .io .Serializable ;
22
21
import java .math .BigDecimal ;
25
24
import java .util .regex .Pattern ;
26
25
import org .jetbrains .annotations .NotNull ;
27
26
28
- @ AutoValue
27
+ /**
28
+ * Represents the time duration as a combination of months, days and nanoseconds. Nanoseconds are
29
+ * broken into two components microseconds and nanoFractions, where nanoFractions can range from
30
+ * [-999, 999]. Internally, Spanner supports Interval value with the following range of individual
31
+ * fields: months: [-120000, 120000] days: [-3660000, 3660000] nanoseconds: [-316224000000000000000,
32
+ * 316224000000000000000] Interval value created outside the specified domain will return error when
33
+ * sent to Spanner backend.
34
+ */
29
35
@ Immutable
30
- public abstract class Interval implements Serializable {
36
+ public class Interval implements Serializable {
37
+ private final int months ;
38
+ private final int days ;
39
+ private final long microseconds ;
40
+ private final short nanoFractions ;
41
+
31
42
public static final long MONTHS_PER_YEAR = 12 ;
32
43
public static final long DAYS_PER_MONTH = 30 ;
33
44
public static final long HOURS_PER_DAY = 24 ;
@@ -48,103 +59,110 @@ public abstract class Interval implements Serializable {
48
59
BigInteger .valueOf (MICROS_PER_MINUTE * NANOS_PER_MICRO );
49
60
public static final BigInteger NANOS_PER_HOUR =
50
61
BigInteger .valueOf (MICROS_PER_HOUR * NANOS_PER_MICRO );
62
+ public static final Interval ZERO = Interval .builder ().build ();
51
63
64
+ /** Regex to ISO8601 formatted interval. `P[n]Y[n]M[n]DT[n]H[n]M[n(.[fraction])]S` */
52
65
private static final Pattern INTERVAL_PATTERN =
53
66
Pattern .compile (
54
67
"^P(?!$)(-?\\ d+Y)?(-?\\ d+M)?(-?\\ d+D)?(T(?=-?.?\\ d)(-?\\ d+H)?(-?\\ d+M)?(-?((\\ d+(\\ .\\ d{1,9})?)|(\\ .\\ d{1,9}))S)?)?$" );
55
68
69
+ private Interval (int months , int days , long microseconds , short nanoFractions ) {
70
+ this .months = months ;
71
+ this .days = days ;
72
+ this .microseconds = microseconds ;
73
+ this .nanoFractions = nanoFractions ;
74
+ }
75
+
56
76
/** Returns the months component of the interval. */
57
- public abstract int months ();
77
+ public int getMonths () {
78
+ return months ;
79
+ }
58
80
59
81
/** Returns the days component of the interval. */
60
- public abstract int days ();
82
+ public int getDays () {
83
+ return days ;
84
+ }
61
85
62
86
/** Returns the microseconds component of the interval. */
63
- public abstract long micros ();
87
+ public long getMicroseconds () {
88
+ return microseconds ;
89
+ }
64
90
65
91
/** Returns the nanoFractions component of the interval. */
66
- public abstract short nanoFractions ();
67
-
68
- public static Builder builder () {
69
- return new AutoValue_Interval .Builder ();
92
+ public short getNanoFractions () {
93
+ return nanoFractions ;
70
94
}
71
95
72
- /** Returns the nanoseconds component of the interval . */
73
- public BigInteger nanos () {
74
- return BigInteger .valueOf (micros ())
96
+ /** Returns the microseconds and nanoFraction of the Interval combined as nanoseconds . */
97
+ public BigInteger getNanoseconds () {
98
+ return BigInteger .valueOf (getMicroseconds ())
75
99
.multiply (BigInteger .valueOf (NANOS_PER_MICRO ))
76
- .add (BigInteger .valueOf (nanoFractions ()));
100
+ .add (BigInteger .valueOf (getNanoFractions ()));
101
+ }
102
+
103
+ public static Builder builder () {
104
+ return new Builder ();
77
105
}
78
106
79
- /** Returns the total microseconds represented by the interval. */
80
- public long getAsMicros () {
81
- return months () * MICROS_PER_MONTH + days () * MICROS_PER_DAY + micros ();
107
+ /**
108
+ * Returns the total microseconds represented by the interval. It combines months, days and
109
+ * microseconds fields of the interval into microseconds.
110
+ */
111
+ public long getAsMicroseconds () {
112
+ return Math .addExact (
113
+ Math .addExact (
114
+ Math .multiplyExact (getMonths (), MICROS_PER_MONTH ),
115
+ Math .multiplyExact (getDays (), MICROS_PER_DAY )),
116
+ getMicroseconds ());
82
117
}
83
118
84
- /** Returns the total nanoseconds represented by the interval. */
85
- public BigInteger getAsNanos () {
86
- return BigInteger .valueOf (getAsMicros ())
119
+ /**
120
+ * Returns the total nanoseconds represented by the interval. It combines months, days,
121
+ * microseconds and nanoFractions fields of the interval into nanoseconds.
122
+ */
123
+ public BigInteger getAsNanoseconds () {
124
+ return BigInteger .valueOf (getAsMicroseconds ())
87
125
.multiply (BigInteger .valueOf (NANOS_PER_MICRO ))
88
- .add (BigInteger .valueOf (nanoFractions ()));
126
+ .add (BigInteger .valueOf (getNanoFractions ()));
89
127
}
90
128
91
129
/** Creates an interval with specified number of months. */
92
130
public static Interval ofMonths (int months ) {
93
- return builder ().setMonths (months ).setDays ( 0 ). setMicros ( 0 ). setNanoFractions (( short ) 0 ). build ();
131
+ return builder ().setMonths (months ).build ();
94
132
}
95
133
96
134
/** Creates an interval with specified number of days. */
97
135
public static Interval ofDays (int days ) {
98
- return builder ().setMonths ( 0 ). setDays (days ). setMicros ( 0 ). setNanoFractions (( short ) 0 ).build ();
136
+ return builder ().setDays (days ).build ();
99
137
}
100
138
101
139
/** Creates an interval with specified number of seconds. */
102
140
public static Interval ofSeconds (long seconds ) {
103
- return builder ()
104
- .setMonths (0 )
105
- .setDays (0 )
106
- .setMicros (seconds * MICROS_PER_SECOND )
107
- .setNanoFractions ((short ) 0 )
108
- .build ();
141
+ return builder ().setMicroseconds (seconds * MICROS_PER_SECOND ).build ();
109
142
}
110
143
111
144
/** Creates an interval with specified number of milliseconds. */
112
145
public static Interval ofMilliseconds (long milliseconds ) {
113
- return builder ()
114
- .setMonths (0 )
115
- .setDays (0 )
116
- .setMicros (milliseconds * MICROS_PER_MILLI )
117
- .setNanoFractions ((short ) 0 )
118
- .build ();
146
+ return builder ().setMicroseconds (milliseconds * MICROS_PER_MILLI ).build ();
119
147
}
120
148
121
149
/** Creates an interval with specified number of microseconds. */
122
- public static Interval ofMicros (long micros ) {
123
- return builder ().months ( 0 ). days ( 0 ). micros ( micros ). nanoFractions (( short ) 0 ).build ();
150
+ public static Interval ofMicroseconds (long micros ) {
151
+ return builder ().setMicroseconds ( micros ).build ();
124
152
}
125
153
126
154
/** Creates an interval with specified number of nanoseconds. */
127
- public static Interval ofNanos (@ NotNull BigInteger nanos ) {
155
+ public static Interval ofNanoseconds (@ NotNull BigInteger nanos ) {
128
156
BigInteger micros = nanos .divide (BigInteger .valueOf (NANOS_PER_MICRO ));
129
157
BigInteger nanoFractions = nanos .subtract (micros .multiply (BigInteger .valueOf (NANOS_PER_MICRO )));
130
158
long microsValue = micros .longValueExact ();
131
159
short nanoFractionsValue = nanoFractions .shortValueExact ();
132
- return builder ()
133
- .setMonths (0 )
134
- .setDays (0 )
135
- .setMicros (microsValue )
136
- .setNanoFractions (nanoFractionsValue )
137
- .build ();
160
+ return builder ().setMicroseconds (microsValue ).setNanoFractions (nanoFractionsValue ).build ();
138
161
}
139
162
140
163
/** Creates an interval with specified number of months, days and microseconds. */
141
164
public static Interval fromMonthsDaysMicros (int months , int days , long micros ) {
142
- return builder ()
143
- .setMonths (months )
144
- .setDays (days )
145
- .setMicros (micros )
146
- .setNanoFractions ((short ) 0 )
147
- .build ();
165
+ return builder ().setMonths (months ).setDays (days ).setMicroseconds (micros ).build ();
148
166
}
149
167
150
168
/** Creates an interval with specified number of months, days and nanoseconds. */
@@ -157,7 +175,7 @@ public static Interval fromMonthsDaysNanos(int months, int days, BigInteger nano
157
175
return builder ()
158
176
.setMonths (months )
159
177
.setDays (days )
160
- .setMicros (micros )
178
+ .setMicroseconds (micros )
161
179
.setNanoFractions (nanoFractions )
162
180
.build ();
163
181
}
@@ -195,17 +213,21 @@ public static Interval parseFromString(String interval) {
195
213
return Interval .builder ()
196
214
.setMonths (Math .toIntExact (totalMonths ))
197
215
.setDays (Math .toIntExact (days ))
198
- .setMicros (totalMicros .longValueExact ())
216
+ .setMicroseconds (totalMicros .longValueExact ())
199
217
.setNanoFractions (nanoFractions .shortValueExact ())
200
218
.build ();
201
219
}
202
220
203
221
/** Converts Interval to ISO8601 Duration Formatted String. */
204
- public String ToISO8601 () {
222
+ public String toISO8601 () {
223
+ if (this .equals (ZERO )) {
224
+ return "P0Y" ;
225
+ }
226
+
205
227
StringBuilder result = new StringBuilder ();
206
228
result .append ("P" );
207
229
208
- long months = this .months ();
230
+ long months = this .getMonths ();
209
231
long years = months / MONTHS_PER_YEAR ;
210
232
months = months - years * MONTHS_PER_YEAR ;
211
233
@@ -217,11 +239,11 @@ public String ToISO8601() {
217
239
result .append (String .format ("%dM" , months ));
218
240
}
219
241
220
- if (this .days () != 0 ) {
221
- result .append (String .format ("%dD" , this .days ()));
242
+ if (this .getDays () != 0 ) {
243
+ result .append (String .format ("%dD" , this .getDays ()));
222
244
}
223
245
224
- BigInteger nanos = this .nanos ();
246
+ BigInteger nanos = this .getNanoseconds ();
225
247
BigInteger zero = BigInteger .valueOf (0 );
226
248
if (nanos .compareTo (zero ) != 0 ) {
227
249
result .append ("T" );
@@ -245,16 +267,12 @@ public String ToISO8601() {
245
267
}
246
268
}
247
269
248
- if (result .length () == 1 ) {
249
- result .append ("0Y" );
250
- }
251
-
252
270
return result .toString ();
253
271
}
254
272
255
- /** Creates an interval which representing 0-duration. */
256
- public static Interval zeroInterval () {
257
- return builder (). setMonths ( 0 ). setDays ( 0 ). setMicros ( 0 ). setNanoFractions (( short ) 0 ). build ();
273
+ @ Override
274
+ public String toString () {
275
+ return toISO8601 ();
258
276
}
259
277
260
278
@ Override
@@ -264,53 +282,56 @@ public boolean equals(Object rhs) {
264
282
}
265
283
266
284
Interval anotherInterval = (Interval ) rhs ;
267
- return months () == anotherInterval .months ()
268
- && days () == anotherInterval .days ()
269
- && nanos ().equals (anotherInterval .nanos ());
285
+ return getMonths () == anotherInterval .getMonths ()
286
+ && getDays () == anotherInterval .getDays ()
287
+ && getNanoseconds ().equals (anotherInterval .getNanoseconds ());
270
288
}
271
289
272
290
@ Override
273
291
public int hashCode () {
274
292
int result = 17 ;
275
- result = 31 * result + Long .valueOf (months ()).hashCode ();
276
- result = 31 * result + Long .valueOf (days ()).hashCode ();
277
- result = 31 * result + nanos ().hashCode ();
293
+ result = 31 * result + Long .valueOf (getMonths ()).hashCode ();
294
+ result = 31 * result + Long .valueOf (getDays ()).hashCode ();
295
+ result = 31 * result + getNanoseconds ().hashCode ();
278
296
return result ;
279
297
}
280
298
281
- @ AutoValue . Builder
282
- public abstract static class Builder {
283
- abstract Builder months ( int months ) ;
284
-
285
- abstract Builder days ( int days ) ;
299
+ public static class Builder {
300
+ private int months = 0 ;
301
+ private int days = 0 ;
302
+ private long microseconds = 0 ;
303
+ private short nanoFractions = 0 ;
286
304
287
- abstract Builder micros (long micros );
288
-
289
- abstract Builder nanoFractions (short nanoFractions );
290
-
291
- public Builder setMonths (int months ) {
292
- return months (months );
305
+ Builder setMonths (int months ) {
306
+ this .months = months ;
307
+ return this ;
293
308
}
294
309
295
- public Builder setDays (int days ) {
296
- return days (days );
310
+ Builder setDays (int days ) {
311
+ this .days = days ;
312
+ return this ;
297
313
}
298
314
299
- public Builder setMicros (long micros ) {
300
- return micros (micros );
315
+ Builder setMicroseconds (long microseconds ) {
316
+ this .microseconds = microseconds ;
317
+ return this ;
301
318
}
302
319
303
- public Builder setNanoFractions (short nanoFractions ) {
320
+ Builder setNanoFractions (short nanoFractions ) {
304
321
if (nanoFractions <= -NANOS_PER_MICRO || nanoFractions >= NANOS_PER_MICRO ) {
305
322
throw SpannerExceptionFactory .newSpannerException (
306
323
ErrorCode .INVALID_ARGUMENT ,
307
324
String .format (
308
325
"NanoFractions must be between:[-%d, %d]" ,
309
326
NANOS_PER_MICRO - 1 , NANOS_PER_MICRO - 1 ));
310
327
}
311
- return nanoFractions (nanoFractions );
328
+
329
+ this .nanoFractions = nanoFractions ;
330
+ return this ;
312
331
}
313
332
314
- public abstract Interval build ();
333
+ public Interval build () {
334
+ return new Interval (months , days , microseconds , nanoFractions );
335
+ }
315
336
}
316
337
}
0 commit comments