@@ -59,8 +59,9 @@ public enum DateTimeUnit {
5959 WEEK_OF_WEEKYEAR ((byte ) 1 , "week" , IsoFields .WEEK_OF_WEEK_BASED_YEAR , true , TimeUnit .DAYS .toMillis (7 )) {
6060 private final long extraLocalOffsetLookup = TimeUnit .DAYS .toMillis (7 );
6161
62- long roundFloor (long utcMillis ) {
63- return DateUtils .roundWeekOfWeekYear (utcMillis );
62+ @ Override
63+ long roundFloor (long utcMillis , int multiplier ) {
64+ return DateUtils .roundWeekIntervalOfWeekYear (utcMillis , multiplier );
6465 }
6566
6667 @ Override
@@ -71,50 +72,62 @@ long extraLocalOffsetLookup() {
7172 YEAR_OF_CENTURY ((byte ) 2 , "year" , ChronoField .YEAR_OF_ERA , false , 12 ) {
7273 private final long extraLocalOffsetLookup = TimeUnit .DAYS .toMillis (366 );
7374
74- long roundFloor (long utcMillis ) {
75- return DateUtils .roundYear (utcMillis );
75+ @ Override
76+ long roundFloor (long utcMillis , int multiplier ) {
77+ return multiplier == 1 ? DateUtils .roundYear (utcMillis ) : DateUtils .roundYearInterval (utcMillis , multiplier );
7678 }
7779
80+ @ Override
7881 long extraLocalOffsetLookup () {
7982 return extraLocalOffsetLookup ;
8083 }
8184 },
8285 QUARTER_OF_YEAR ((byte ) 3 , "quarter" , IsoFields .QUARTER_OF_YEAR , false , 3 ) {
8386 private final long extraLocalOffsetLookup = TimeUnit .DAYS .toMillis (92 );
8487
85- long roundFloor (long utcMillis ) {
86- return DateUtils .roundQuarterOfYear (utcMillis );
88+ @ Override
89+ long roundFloor (long utcMillis , int multiplier ) {
90+ return multiplier == 1
91+ ? DateUtils .roundQuarterOfYear (utcMillis )
92+ : DateUtils .roundIntervalMonthOfYear (utcMillis , multiplier * 3 );
8793 }
8894
95+ @ Override
8996 long extraLocalOffsetLookup () {
9097 return extraLocalOffsetLookup ;
9198 }
9299 },
93100 MONTH_OF_YEAR ((byte ) 4 , "month" , ChronoField .MONTH_OF_YEAR , false , 1 ) {
94101 private final long extraLocalOffsetLookup = TimeUnit .DAYS .toMillis (31 );
95102
96- long roundFloor (long utcMillis ) {
97- return DateUtils .roundMonthOfYear (utcMillis );
103+ @ Override
104+ long roundFloor (long utcMillis , int multiplier ) {
105+ return multiplier == 1 ? DateUtils .roundMonthOfYear (utcMillis ) : DateUtils .roundIntervalMonthOfYear (utcMillis , multiplier );
98106 }
99107
108+ @ Override
100109 long extraLocalOffsetLookup () {
101110 return extraLocalOffsetLookup ;
102111 }
103112 },
104113 DAY_OF_MONTH ((byte ) 5 , "day" , ChronoField .DAY_OF_MONTH , true , ChronoField .DAY_OF_MONTH .getBaseUnit ().getDuration ().toMillis ()) {
105- long roundFloor (long utcMillis ) {
106- return DateUtils .roundFloor (utcMillis , this .ratio );
114+ @ Override
115+ long roundFloor (long utcMillis , int multiplier ) {
116+ return DateUtils .roundFloor (utcMillis , this .ratio * multiplier );
107117 }
108118
119+ @ Override
109120 long extraLocalOffsetLookup () {
110121 return ratio ;
111122 }
112123 },
113124 HOUR_OF_DAY ((byte ) 6 , "hour" , ChronoField .HOUR_OF_DAY , true , ChronoField .HOUR_OF_DAY .getBaseUnit ().getDuration ().toMillis ()) {
114- long roundFloor (long utcMillis ) {
115- return DateUtils .roundFloor (utcMillis , ratio );
125+ @ Override
126+ long roundFloor (long utcMillis , int multiplier ) {
127+ return DateUtils .roundFloor (utcMillis , ratio * multiplier );
116128 }
117129
130+ @ Override
118131 long extraLocalOffsetLookup () {
119132 return ratio ;
120133 }
@@ -126,10 +139,12 @@ long extraLocalOffsetLookup() {
126139 true ,
127140 ChronoField .MINUTE_OF_HOUR .getBaseUnit ().getDuration ().toMillis ()
128141 ) {
129- long roundFloor (long utcMillis ) {
130- return DateUtils .roundFloor (utcMillis , ratio );
142+ @ Override
143+ long roundFloor (long utcMillis , int multiplier ) {
144+ return DateUtils .roundFloor (utcMillis , ratio * multiplier );
131145 }
132146
147+ @ Override
133148 long extraLocalOffsetLookup () {
134149 return ratio ;
135150 }
@@ -141,10 +156,12 @@ long extraLocalOffsetLookup() {
141156 true ,
142157 ChronoField .SECOND_OF_MINUTE .getBaseUnit ().getDuration ().toMillis ()
143158 ) {
144- long roundFloor (long utcMillis ) {
145- return DateUtils .roundFloor (utcMillis , ratio );
159+ @ Override
160+ long roundFloor (long utcMillis , int multiplier ) {
161+ return DateUtils .roundFloor (utcMillis , ratio * multiplier );
146162 }
147163
164+ @ Override
148165 long extraLocalOffsetLookup () {
149166 return ratio ;
150167 }
@@ -171,10 +188,11 @@ long extraLocalOffsetLookup() {
171188 * This rounds down the supplied milliseconds since the epoch down to the next unit. In order to retain performance this method
172189 * should be as fast as possible and not try to convert dates to java-time objects if possible
173190 *
174- * @param utcMillis the milliseconds since the epoch
175- * @return the rounded down milliseconds since the epoch
191+ * @param utcMillis the milliseconds since the epoch
192+ * @param multiplier the factor by which the unit is multiplied
193+ * @return the rounded down milliseconds since the epoch
176194 */
177- abstract long roundFloor (long utcMillis );
195+ abstract long roundFloor (long utcMillis , int multiplier );
178196
179197 /**
180198 * When looking up {@link LocalTimeOffset} go this many milliseconds
@@ -329,17 +347,24 @@ public static class Builder {
329347
330348 private final DateTimeUnit unit ;
331349 private final long interval ;
350+ private final int multiplier ;
332351
333352 private ZoneId timeZone = ZoneOffset .UTC ;
334353 private long offset = 0 ;
335354
336355 public Builder (DateTimeUnit unit ) {
356+ this (unit , 1 );
357+ }
358+
359+ public Builder (DateTimeUnit unit , int multiplier ) {
337360 this .unit = unit ;
361+ this .multiplier = multiplier ;
338362 this .interval = -1 ;
339363 }
340364
341365 public Builder (TimeValue interval ) {
342366 this .unit = null ;
367+ this .multiplier = -1 ;
343368 if (interval .millis () < 1 ) throw new IllegalArgumentException ("Zero or negative time interval not supported" );
344369 this .interval = interval .millis ();
345370 }
@@ -365,7 +390,7 @@ public Builder offset(long offset) {
365390 public Rounding build () {
366391 Rounding rounding ;
367392 if (unit != null ) {
368- rounding = new TimeUnitRounding (unit , timeZone );
393+ rounding = new TimeUnitRounding (unit , multiplier , timeZone );
369394 } else {
370395 rounding = new TimeIntervalRounding (interval , timeZone );
371396 }
@@ -422,11 +447,17 @@ static class TimeUnitRounding extends Rounding {
422447 private final DateTimeUnit unit ;
423448 private final ZoneId timeZone ;
424449 private final boolean unitRoundsToMidnight ;
450+ private final int multiplier ;
425451
426452 TimeUnitRounding (DateTimeUnit unit , ZoneId timeZone ) {
453+ this (unit , 1 , timeZone );
454+ }
455+
456+ TimeUnitRounding (DateTimeUnit unit , int multiplier , ZoneId timeZone ) {
427457 this .unit = unit ;
428458 this .timeZone = timeZone ;
429459 this .unitRoundsToMidnight = this .unit .field .getBaseUnit ().getDuration ().toMillis () > 3600000L ;
460+ this .multiplier = multiplier ;
430461 }
431462
432463 TimeUnitRounding (StreamInput in ) throws IOException {
@@ -660,7 +691,7 @@ private class FixedToMidnightRounding extends TimeUnitPreparedRounding {
660691
661692 @ Override
662693 public long round (long utcMillis ) {
663- return offset .localToUtcInThisOffset (unit .roundFloor (offset .utcToLocalTime (utcMillis )));
694+ return offset .localToUtcInThisOffset (unit .roundFloor (offset .utcToLocalTime (utcMillis ), multiplier ));
664695 }
665696
666697 @ Override
@@ -686,7 +717,7 @@ private class FixedNotToMidnightRounding extends TimeUnitPreparedRounding {
686717
687718 @ Override
688719 public long round (long utcMillis ) {
689- return offset .localToUtcInThisOffset (unit .roundFloor (offset .utcToLocalTime (utcMillis )));
720+ return offset .localToUtcInThisOffset (unit .roundFloor (offset .utcToLocalTime (utcMillis ), multiplier ));
690721 }
691722
692723 @ Override
@@ -710,7 +741,7 @@ private class ToMidnightRounding extends TimeUnitPreparedRounding implements Loc
710741 @ Override
711742 public long round (long utcMillis ) {
712743 LocalTimeOffset offset = lookup .lookup (utcMillis );
713- return offset .localToUtc (unit .roundFloor (offset .utcToLocalTime (utcMillis )), this );
744+ return offset .localToUtc (unit .roundFloor (offset .utcToLocalTime (utcMillis ), multiplier ), this );
714745 }
715746
716747 @ Override
@@ -764,14 +795,14 @@ private class NotToMidnightRounding extends AbstractNotToMidnightRounding implem
764795 @ Override
765796 public long round (long utcMillis ) {
766797 LocalTimeOffset offset = lookup .lookup (utcMillis );
767- long roundedLocalMillis = unit .roundFloor (offset .utcToLocalTime (utcMillis ));
798+ long roundedLocalMillis = unit .roundFloor (offset .utcToLocalTime (utcMillis ), multiplier );
768799 return offset .localToUtc (roundedLocalMillis , this );
769800 }
770801
771802 @ Override
772803 public long inGap (long localMillis , Gap gap ) {
773804 // Round from just before the start of the gap
774- return gap .previous ().localToUtc (unit .roundFloor (gap .firstMissingLocalTime () - 1 ), this );
805+ return gap .previous ().localToUtc (unit .roundFloor (gap .firstMissingLocalTime () - 1 , multiplier ), this );
775806 }
776807
777808 @ Override
0 commit comments