| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.apache.iotdb.tsfile.utils; |
| |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.nio.ByteBuffer; |
| import java.time.Instant; |
| import java.time.LocalDateTime; |
| import java.time.ZoneId; |
| import java.util.Objects; |
| import java.util.TimeZone; |
| import java.util.concurrent.TimeUnit; |
| |
| public class TimeDuration implements Serializable { |
| // month part of time duration |
| public final int monthDuration; |
| // non-month part of time duration, its precision is same as current time_precision |
| public final long nonMonthDuration; |
| |
| public TimeDuration(int monthDuration, long nonMonthDuration) { |
| this.monthDuration = monthDuration; |
| this.nonMonthDuration = nonMonthDuration; |
| } |
| |
| public boolean containsMonth() { |
| return monthDuration != 0; |
| } |
| |
| /** |
| * Convert monthDuration to current precision duration, then add currPrecisionDuration field. |
| * Think month as 30 days. |
| * |
| * @return the total duration of this timeDuration in current precision |
| */ |
| public long getTotalDuration(TimeUnit currPrecision) { |
| return currPrecision.convert(monthDuration * 30 * 86400_000L, TimeUnit.MILLISECONDS) |
| + nonMonthDuration; |
| } |
| |
| /** Think month as 31 days. */ |
| public long getMaxTotalDuration(TimeUnit currPrecision) { |
| return currPrecision.convert(monthDuration * 31 * 86400_000L, TimeUnit.MILLISECONDS) |
| + nonMonthDuration; |
| } |
| |
| public boolean isGreaterThan(TimeDuration right) { |
| if (this.monthDuration > right.monthDuration) { |
| return true; |
| } else if (this.monthDuration == right.monthDuration) { |
| return this.nonMonthDuration > right.nonMonthDuration; |
| } |
| return false; |
| } |
| |
| public TimeDuration merge(TimeDuration other) { |
| return new TimeDuration( |
| this.monthDuration + other.monthDuration, this.nonMonthDuration + other.nonMonthDuration); |
| } |
| |
| public TimeDuration multiple(long times) { |
| return new TimeDuration((int) (monthDuration * times), nonMonthDuration * times); |
| } |
| |
| /** Think month as 28 days. */ |
| public long getMinTotalDuration(TimeUnit currPrecision) { |
| return currPrecision.convert(monthDuration * 28 * 86400_000L, TimeUnit.MILLISECONDS) |
| + nonMonthDuration; |
| } |
| |
| public void serialize(ByteBuffer buffer) { |
| ReadWriteIOUtils.write(monthDuration, buffer); |
| ReadWriteIOUtils.write(nonMonthDuration, buffer); |
| } |
| |
| public void serialize(DataOutputStream stream) throws IOException { |
| ReadWriteIOUtils.write(monthDuration, stream); |
| ReadWriteIOUtils.write(nonMonthDuration, stream); |
| } |
| |
| public static TimeDuration deserialize(ByteBuffer buffer) { |
| return new TimeDuration(ReadWriteIOUtils.readInt(buffer), ReadWriteIOUtils.readLong(buffer)); |
| } |
| |
| /** Get a series of time which duration contains month. */ |
| public static long[] getConsecutiveTimesIntervalByMonth( |
| long startTime, |
| TimeDuration duration, |
| int length, |
| TimeZone timeZone, |
| TimeUnit currPrecision) { |
| long[] result = new long[length]; |
| result[0] = startTime; |
| for (int i = 1; i < length; i++) { |
| result[i] = getStartTime(startTime, duration.multiple(i), currPrecision, timeZone.toZoneId()); |
| } |
| return result; |
| } |
| |
| /** |
| * Add time duration contains natural months to startTime. |
| * |
| * <p>Attention: This method does not support accumulation. If you need to calculate the date two |
| * months after the start time, just add two months directly through duration, rather than adding |
| * one month first and then adding another month in a loop, it will get wrong result. |
| * |
| * <p>There is an example: |
| * |
| * <pre> |
| * 1.30 + 2mo = 3.30(right) |
| * 1.30 + 1mo = 2.28, 2.28 + 1mo = 3.28(wrong) |
| * </pre> |
| * |
| * @param startTime start time |
| * @param duration one duration |
| * @return the time after durations elapsed |
| */ |
| public static long calcPositiveIntervalByMonth( |
| long startTime, TimeDuration duration, TimeZone timeZone, TimeUnit currPrecision) { |
| return getStartTime(startTime, duration, currPrecision, timeZone.toZoneId()); |
| } |
| |
| private static long getStartTime( |
| long startTime, TimeDuration duration, TimeUnit currPrecision, ZoneId zoneId) { |
| long coarserThanMsPart = getCoarserThanMsPart(startTime, currPrecision); |
| LocalDateTime localDateTime = |
| LocalDateTime.ofInstant(Instant.ofEpochMilli(coarserThanMsPart), zoneId); |
| localDateTime = localDateTime.plusMonths(duration.monthDuration); |
| return currPrecision.convert( |
| localDateTime.atZone(zoneId).toInstant().toEpochMilli(), TimeUnit.MILLISECONDS) |
| + getFinerThanMsPart(startTime, currPrecision) |
| + duration.nonMonthDuration; |
| } |
| |
| private static long getCoarserThanMsPart(long time, TimeUnit currPrecision) { |
| return TimeUnit.MILLISECONDS.convert(time, currPrecision); |
| } |
| |
| private static long getFinerThanMsPart(long time, TimeUnit currPrecision) { |
| switch (currPrecision) { |
| case MILLISECONDS: |
| return 0; |
| case MICROSECONDS: |
| return time % 1000; |
| case NANOSECONDS: |
| return time % 1000_000; |
| default: |
| throw new UnsupportedOperationException(); |
| } |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| TimeDuration that = (TimeDuration) o; |
| return monthDuration == that.monthDuration && nonMonthDuration == that.nonMonthDuration; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(monthDuration, nonMonthDuration); |
| } |
| |
| @Override |
| public String toString() { |
| return "TimeDuration{" |
| + (monthDuration > 0 ? monthDuration + "mo, " : "") |
| + (nonMonthDuration > 0 ? nonMonthDuration : "") |
| + '}'; |
| } |
| } |