14 changed files with 370 additions and 34 deletions
@ -0,0 +1,92 @@ |
|||
/** |
|||
* Copyright © 2016-2024 The Thingsboard Authors |
|||
* |
|||
* 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 org.thingsboard.server.common.data.kv; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.Getter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.jetbrains.annotations.NotNull; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
|
|||
import java.time.DateTimeException; |
|||
import java.time.ZoneId; |
|||
import java.time.zone.ZoneRulesException; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@AllArgsConstructor |
|||
@EqualsAndHashCode |
|||
@Slf4j |
|||
public class AggregationParams { |
|||
@Getter |
|||
private final Aggregation aggregation; |
|||
@Getter |
|||
private final IntervalType intervalType; |
|||
@Getter |
|||
private final ZoneId tzId; |
|||
|
|||
private final long interval; |
|||
|
|||
public static AggregationParams none() { |
|||
return new AggregationParams(Aggregation.NONE, null, null, 0L); |
|||
} |
|||
|
|||
public static AggregationParams milliseconds(Aggregation aggregationType, long aggregationIntervalMs) { |
|||
return new AggregationParams(aggregationType, IntervalType.MILLISECONDS, null, aggregationIntervalMs); |
|||
} |
|||
|
|||
public static AggregationParams calendar(Aggregation aggregationType, IntervalType intervalType, String tzIdStr) { |
|||
return calendar(aggregationType, intervalType, getZoneId(tzIdStr)); |
|||
} |
|||
|
|||
public static AggregationParams calendar(Aggregation aggregationType, IntervalType intervalType, ZoneId tzId) { |
|||
return new AggregationParams(aggregationType, intervalType, tzId, 0L); |
|||
} |
|||
|
|||
public static AggregationParams of(Aggregation aggregation, IntervalType intervalType, ZoneId tzId, long interval) { |
|||
return new AggregationParams(aggregation, intervalType, tzId, interval); |
|||
} |
|||
|
|||
public long getInterval() { |
|||
if (intervalType == null) { |
|||
return 0L; |
|||
} else { |
|||
switch (intervalType) { |
|||
case WEEK: |
|||
case WEEK_ISO: |
|||
return TimeUnit.DAYS.toMillis(7); |
|||
case MONTH: |
|||
return TimeUnit.DAYS.toMillis(30); |
|||
case QUARTER: |
|||
return TimeUnit.DAYS.toMillis(90); |
|||
default: |
|||
return interval; |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static ZoneId getZoneId(String tzIdStr) { |
|||
if (StringUtils.isEmpty(tzIdStr)) { |
|||
return ZoneId.systemDefault(); |
|||
} |
|||
try { |
|||
return ZoneId.of(tzIdStr); |
|||
} catch (DateTimeException e) { |
|||
log.warn("[{}] Failed to convert the time zone. Fallback to default.", tzIdStr); |
|||
return ZoneId.systemDefault(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* Copyright © 2016-2024 The Thingsboard Authors |
|||
* |
|||
* 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 org.thingsboard.server.dao.util; |
|||
|
|||
import org.thingsboard.server.common.data.kv.IntervalType; |
|||
|
|||
import java.time.Instant; |
|||
import java.time.ZoneId; |
|||
import java.time.ZonedDateTime; |
|||
import java.time.temporal.ChronoUnit; |
|||
import java.time.temporal.IsoFields; |
|||
import java.time.temporal.WeekFields; |
|||
|
|||
public class TimeUtils { |
|||
|
|||
public static long calculateIntervalEnd(long startTs, IntervalType intervalType, ZoneId tzId) { |
|||
var startTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(startTs), tzId); |
|||
switch (intervalType) { |
|||
case WEEK: |
|||
return startTime.truncatedTo(ChronoUnit.DAYS).with(WeekFields.SUNDAY_START.dayOfWeek(), 1).plusDays(7).toInstant().toEpochMilli(); |
|||
case WEEK_ISO: |
|||
return startTime.truncatedTo(ChronoUnit.DAYS).with(WeekFields.ISO.dayOfWeek(), 1).plusDays(7).toInstant().toEpochMilli(); |
|||
case MONTH: |
|||
return startTime.truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1).plusMonths(1).toInstant().toEpochMilli(); |
|||
case QUARTER: |
|||
return startTime.truncatedTo(ChronoUnit.DAYS).with(IsoFields.DAY_OF_QUARTER, 1).plusMonths(3).toInstant().toEpochMilli(); |
|||
default: |
|||
throw new RuntimeException("Not supported!"); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
/** |
|||
* Copyright © 2016-2024 The Thingsboard Authors |
|||
* |
|||
* 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 org.thingsboard.server.dao.util; |
|||
|
|||
import org.junit.jupiter.api.Test; |
|||
import org.thingsboard.server.common.data.kv.IntervalType; |
|||
|
|||
import java.time.ZoneId; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
|
|||
class TimeUtilsTest { |
|||
|
|||
@Test |
|||
void testWeekEnd() { |
|||
long ts = 1704899727000L; // Wednesday, January 10 15:15:27 GMT
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.WEEK, ZoneId.of("Europe/Kyiv"))).isEqualTo(1705183200000L); // Sunday, January 14, 2024 0:00:00 GMT+02:00
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.WEEK_ISO, ZoneId.of("Europe/Kyiv"))).isEqualTo(1705269600000L); // Monday, January 15, 2024 0:00:00 GMT+02:00
|
|||
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.WEEK, ZoneId.of("Europe/Amsterdam"))).isEqualTo(1705186800000L); // Sunday, January 14, 2024 0:00:00 GMT+01:00
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.WEEK_ISO, ZoneId.of("Europe/Amsterdam"))).isEqualTo(1705273200000L); // Monday, January 15, 2024 0:00:00 GMT+01:00
|
|||
|
|||
ts = 1704621600000L; // Sunday, January 7, 2024 12:00:00 GMT+02:00
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.WEEK, ZoneId.of("Europe/Kyiv"))).isEqualTo(1705183200000L); // Sunday, January 14, 2024 0:00:00 GMT+02:00
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.WEEK_ISO, ZoneId.of("Europe/Kyiv"))).isEqualTo(1704664800000L); // Monday, January 8, 2024 0:00:00 GMT+02:00
|
|||
} |
|||
|
|||
|
|||
@Test |
|||
void testMonthEnd() { |
|||
long ts = 1704899727000L; // Wednesday, January 10 15:15:27 GMT
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.MONTH, ZoneId.of("Europe/Kyiv"))).isEqualTo(1706738400000L); // Thursday, February 1, 2024 0:00:00 GMT+02:00
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.MONTH, ZoneId.of("Europe/Amsterdam"))).isEqualTo(1706742000000L); // Monday, January 15, 2024 0:00:00 GMT+02:00
|
|||
} |
|||
|
|||
@Test |
|||
void testQuarterEnd() { |
|||
long ts = 1704899727000L; // Wednesday, January 10 15:15:27 GMT
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.QUARTER, ZoneId.of("Europe/Kyiv"))).isEqualTo(1711918800000L); // Monday, April 1, 2024 0:00:00 GMT+03:00 DST
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.QUARTER, ZoneId.of("Europe/Amsterdam"))).isEqualTo(1711922400000L); // Monday, April 1, 2024 1:00:00 GMT+03:00 DST
|
|||
|
|||
ts = 1711929600000L; // Monday, April 1, 2024 3:00:00 GMT+03:00
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.QUARTER, ZoneId.of("Europe/Kyiv"))).isEqualTo(1719781200000L); // Monday, July 1, 2024 0:00:00 GMT+03:00 DST
|
|||
assertThat(TimeUtils.calculateIntervalEnd(ts, IntervalType.QUARTER, ZoneId.of("America/New_York"))).isEqualTo(1711944000000L); // Monday, April 1, 2024 7:00:00 GMT+03:00 DST
|
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue