11/*
2- * Copyright 2021 Google LLC
2+ * Copyright 2024 Google LLC
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1616
1717package com .google .cloud .spanner ;
1818
19- import com .google .auto .value .AutoValue ;
2019import com .google .errorprone .annotations .Immutable ;
2120import java .io .Serializable ;
2221import java .math .BigDecimal ;
2524import java .util .regex .Pattern ;
2625import org .jetbrains .annotations .NotNull ;
2726
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+ */
2935@ 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+
3142 public static final long MONTHS_PER_YEAR = 12 ;
3243 public static final long DAYS_PER_MONTH = 30 ;
3344 public static final long HOURS_PER_DAY = 24 ;
@@ -48,103 +59,110 @@ public abstract class Interval implements Serializable {
4859 BigInteger .valueOf (MICROS_PER_MINUTE * NANOS_PER_MICRO );
4960 public static final BigInteger NANOS_PER_HOUR =
5061 BigInteger .valueOf (MICROS_PER_HOUR * NANOS_PER_MICRO );
62+ public static final Interval ZERO = Interval .builder ().build ();
5163
64+ /** Regex to ISO8601 formatted interval. `P[n]Y[n]M[n]DT[n]H[n]M[n(.[fraction])]S` */
5265 private static final Pattern INTERVAL_PATTERN =
5366 Pattern .compile (
5467 "^P(?!$)(-?\\ d+Y)?(-?\\ d+M)?(-?\\ d+D)?(T(?=-?.?\\ d)(-?\\ d+H)?(-?\\ d+M)?(-?((\\ d+(\\ .\\ d{1,9})?)|(\\ .\\ d{1,9}))S)?)?$" );
5568
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+
5676 /** Returns the months component of the interval. */
57- public abstract int months ();
77+ public int getMonths () {
78+ return months ;
79+ }
5880
5981 /** Returns the days component of the interval. */
60- public abstract int days ();
82+ public int getDays () {
83+ return days ;
84+ }
6185
6286 /** Returns the microseconds component of the interval. */
63- public abstract long micros ();
87+ public long getMicroseconds () {
88+ return microseconds ;
89+ }
6490
6591 /** 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 ;
7094 }
7195
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 ())
7599 .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 ();
77105 }
78106
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 ());
82117 }
83118
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 ())
87125 .multiply (BigInteger .valueOf (NANOS_PER_MICRO ))
88- .add (BigInteger .valueOf (nanoFractions ()));
126+ .add (BigInteger .valueOf (getNanoFractions ()));
89127 }
90128
91129 /** Creates an interval with specified number of months. */
92130 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 ();
94132 }
95133
96134 /** Creates an interval with specified number of days. */
97135 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 ();
99137 }
100138
101139 /** Creates an interval with specified number of seconds. */
102140 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 ();
109142 }
110143
111144 /** Creates an interval with specified number of milliseconds. */
112145 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 ();
119147 }
120148
121149 /** 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 ();
124152 }
125153
126154 /** Creates an interval with specified number of nanoseconds. */
127- public static Interval ofNanos (@ NotNull BigInteger nanos ) {
155+ public static Interval ofNanoseconds (@ NotNull BigInteger nanos ) {
128156 BigInteger micros = nanos .divide (BigInteger .valueOf (NANOS_PER_MICRO ));
129157 BigInteger nanoFractions = nanos .subtract (micros .multiply (BigInteger .valueOf (NANOS_PER_MICRO )));
130158 long microsValue = micros .longValueExact ();
131159 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 ();
138161 }
139162
140163 /** Creates an interval with specified number of months, days and microseconds. */
141164 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 ();
148166 }
149167
150168 /** 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
157175 return builder ()
158176 .setMonths (months )
159177 .setDays (days )
160- .setMicros (micros )
178+ .setMicroseconds (micros )
161179 .setNanoFractions (nanoFractions )
162180 .build ();
163181 }
@@ -195,17 +213,21 @@ public static Interval parseFromString(String interval) {
195213 return Interval .builder ()
196214 .setMonths (Math .toIntExact (totalMonths ))
197215 .setDays (Math .toIntExact (days ))
198- .setMicros (totalMicros .longValueExact ())
216+ .setMicroseconds (totalMicros .longValueExact ())
199217 .setNanoFractions (nanoFractions .shortValueExact ())
200218 .build ();
201219 }
202220
203221 /** 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+
205227 StringBuilder result = new StringBuilder ();
206228 result .append ("P" );
207229
208- long months = this .months ();
230+ long months = this .getMonths ();
209231 long years = months / MONTHS_PER_YEAR ;
210232 months = months - years * MONTHS_PER_YEAR ;
211233
@@ -217,11 +239,11 @@ public String ToISO8601() {
217239 result .append (String .format ("%dM" , months ));
218240 }
219241
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 ()));
222244 }
223245
224- BigInteger nanos = this .nanos ();
246+ BigInteger nanos = this .getNanoseconds ();
225247 BigInteger zero = BigInteger .valueOf (0 );
226248 if (nanos .compareTo (zero ) != 0 ) {
227249 result .append ("T" );
@@ -245,16 +267,12 @@ public String ToISO8601() {
245267 }
246268 }
247269
248- if (result .length () == 1 ) {
249- result .append ("0Y" );
250- }
251-
252270 return result .toString ();
253271 }
254272
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 ();
258276 }
259277
260278 @ Override
@@ -264,53 +282,56 @@ public boolean equals(Object rhs) {
264282 }
265283
266284 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 ());
270288 }
271289
272290 @ Override
273291 public int hashCode () {
274292 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 ();
278296 return result ;
279297 }
280298
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 ;
286304
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 ;
293308 }
294309
295- public Builder setDays (int days ) {
296- return days (days );
310+ Builder setDays (int days ) {
311+ this .days = days ;
312+ return this ;
297313 }
298314
299- public Builder setMicros (long micros ) {
300- return micros (micros );
315+ Builder setMicroseconds (long microseconds ) {
316+ this .microseconds = microseconds ;
317+ return this ;
301318 }
302319
303- public Builder setNanoFractions (short nanoFractions ) {
320+ Builder setNanoFractions (short nanoFractions ) {
304321 if (nanoFractions <= -NANOS_PER_MICRO || nanoFractions >= NANOS_PER_MICRO ) {
305322 throw SpannerExceptionFactory .newSpannerException (
306323 ErrorCode .INVALID_ARGUMENT ,
307324 String .format (
308325 "NanoFractions must be between:[-%d, %d]" ,
309326 NANOS_PER_MICRO - 1 , NANOS_PER_MICRO - 1 ));
310327 }
311- return nanoFractions (nanoFractions );
328+
329+ this .nanoFractions = nanoFractions ;
330+ return this ;
312331 }
313332
314- public abstract Interval build ();
333+ public Interval build () {
334+ return new Interval (months , days , microseconds , nanoFractions );
335+ }
315336 }
316337}
0 commit comments