org.h2.util.JSR310Utils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of h2-mvstore Show documentation
Show all versions of h2-mvstore Show documentation
Fork of h2database to maintain Java 8 compatibility
The newest version!
/*
* Copyright 2004-2023 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import static org.h2.util.DateTimeUtils.NANOS_PER_SECOND;
import static org.h2.util.DateTimeUtils.SECONDS_PER_DAY;
import static org.h2.util.DateTimeUtils.SHIFT_MONTH;
import static org.h2.util.DateTimeUtils.SHIFT_YEAR;
import static org.h2.util.DateTimeUtils.absoluteDayFromDateValue;
import static org.h2.util.DateTimeUtils.dateValue;
import static org.h2.util.DateTimeUtils.dateValueFromAbsoluteDay;
import static org.h2.util.DateTimeUtils.dayFromDateValue;
import static org.h2.util.DateTimeUtils.monthFromDateValue;
import static org.h2.util.DateTimeUtils.yearFromDateValue;
import java.math.BigInteger;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.Period;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import org.h2.api.ErrorCode;
import org.h2.api.IntervalQualifier;
import org.h2.engine.CastDataProvider;
import org.h2.message.DbException;
import org.h2.value.DataType;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueInterval;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimeTimeZone;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
/**
* This utility class provides access to JSR 310 classes.
*/
public class JSR310Utils {
private static final long MIN_DATE_VALUE = (-999_999_999L << SHIFT_YEAR)
+ (1 << SHIFT_MONTH) + 1;
private static final long MAX_DATE_VALUE = (999_999_999L << SHIFT_YEAR)
+ (12 << SHIFT_MONTH) + 31;
private static final long MIN_INSTANT_SECOND = -31_557_014_167_219_200L;
private static final long MAX_INSTANT_SECOND = 31_556_889_864_403_199L;
private JSR310Utils() {
// utility class
}
/**
* Converts a value to a LocalDate.
*
* This method should only be called from Java 8 or later version.
*
* @param value
* the value to convert
* @param provider
* the cast information provider
* @return the LocalDate
*/
public static LocalDate valueToLocalDate(Value value, CastDataProvider provider) {
long dateValue = value.convertToDate(provider).getDateValue();
if (dateValue > MAX_DATE_VALUE) {
return LocalDate.MAX;
} else if (dateValue < MIN_DATE_VALUE) {
return LocalDate.MIN;
}
return LocalDate.of(yearFromDateValue(dateValue), monthFromDateValue(dateValue),
dayFromDateValue(dateValue));
}
/**
* Converts a value to a LocalTime.
*
* This method should only be called from Java 8 or later version.
*
* @param value
* the value to convert
* @param provider
* the cast information provider
* @return the LocalTime
*/
public static LocalTime valueToLocalTime(Value value, CastDataProvider provider) {
return LocalTime.ofNanoOfDay(((ValueTime) value.convertTo(TypeInfo.TYPE_TIME, provider)).getNanos());
}
/**
* Converts a value to a LocalDateTime.
*
* This method should only be called from Java 8 or later version.
*
* @param value
* the value to convert
* @param provider
* the cast information provider
* @return the LocalDateTime
*/
public static LocalDateTime valueToLocalDateTime(Value value, CastDataProvider provider) {
ValueTimestamp valueTimestamp = (ValueTimestamp) value.convertTo(TypeInfo.TYPE_TIMESTAMP, provider);
return localDateTimeFromDateNanos(valueTimestamp.getDateValue(), valueTimestamp.getTimeNanos());
}
/**
* Converts a value to a Instant.
*
* This method should only be called from Java 8 or later version.
*
* @param value
* the value to convert
* @param provider
* the cast information provider
* @return the Instant
*/
public static Instant valueToInstant(Value value, CastDataProvider provider) {
ValueTimestampTimeZone valueTimestampTimeZone = (ValueTimestampTimeZone) value
.convertTo(TypeInfo.TYPE_TIMESTAMP_TZ, provider);
long timeNanos = valueTimestampTimeZone.getTimeNanos();
long epochSecond = absoluteDayFromDateValue(valueTimestampTimeZone.getDateValue())
* SECONDS_PER_DAY //
+ timeNanos / NANOS_PER_SECOND //
- valueTimestampTimeZone.getTimeZoneOffsetSeconds();
if (epochSecond > MAX_INSTANT_SECOND) {
return Instant.MAX;
} else if (epochSecond < MIN_INSTANT_SECOND) {
return Instant.MIN;
}
return Instant.ofEpochSecond(epochSecond, timeNanos % NANOS_PER_SECOND);
}
/**
* Converts a value to a OffsetDateTime.
*
* This method should only be called from Java 8 or later version.
*
* @param value
* the value to convert
* @param provider
* the cast information provider
* @return the OffsetDateTime
*/
public static OffsetDateTime valueToOffsetDateTime(Value value, CastDataProvider provider) {
ValueTimestampTimeZone v = (ValueTimestampTimeZone) value.convertTo(TypeInfo.TYPE_TIMESTAMP_TZ, provider);
return OffsetDateTime.of(localDateTimeFromDateNanos(v.getDateValue(), v.getTimeNanos()),
ZoneOffset.ofTotalSeconds(v.getTimeZoneOffsetSeconds()));
}
/**
* Converts a value to a ZonedDateTime.
*
* This method should only be called from Java 8 or later version.
*
* @param value
* the value to convert
* @param provider
* the cast information provider
* @return the ZonedDateTime
*/
public static ZonedDateTime valueToZonedDateTime(Value value, CastDataProvider provider) {
ValueTimestampTimeZone v = (ValueTimestampTimeZone) value.convertTo(TypeInfo.TYPE_TIMESTAMP_TZ, provider);
return ZonedDateTime.of(localDateTimeFromDateNanos(v.getDateValue(), v.getTimeNanos()),
ZoneOffset.ofTotalSeconds(v.getTimeZoneOffsetSeconds()));
}
/**
* Converts a value to a OffsetTime.
*
* This method should only be called from Java 8 or later version.
*
* @param value
* the value to convert
* @param provider
* the cast information provider
* @return the OffsetTime
*/
public static OffsetTime valueToOffsetTime(Value value, CastDataProvider provider) {
ValueTimeTimeZone valueTimeTimeZone = (ValueTimeTimeZone) value.convertTo(TypeInfo.TYPE_TIME_TZ, provider);
return OffsetTime.of(LocalTime.ofNanoOfDay(valueTimeTimeZone.getNanos()),
ZoneOffset.ofTotalSeconds(valueTimeTimeZone.getTimeZoneOffsetSeconds()));
}
/**
* Converts a value to a Period.
*
* This method should only be called from Java 8 or later version.
*
* @param value
* the value to convert
* @return the Period
*/
public static Period valueToPeriod(Value value) {
if (!(value instanceof ValueInterval)) {
value = value.convertTo(TypeInfo.TYPE_INTERVAL_YEAR_TO_MONTH);
}
if (!DataType.isYearMonthIntervalType(value.getValueType())) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, (Throwable) null, value.getString());
}
ValueInterval v = (ValueInterval) value;
IntervalQualifier qualifier = v.getQualifier();
boolean negative = v.isNegative();
long leading = v.getLeading();
long remaining = v.getRemaining();
int y = Value.convertToInt(IntervalUtils.yearsFromInterval(qualifier, negative, leading, remaining), null);
int m = Value.convertToInt(IntervalUtils.monthsFromInterval(qualifier, negative, leading, remaining), null);
return Period.of(y, m, 0);
}
/**
* Converts a value to a Duration.
*
* This method should only be called from Java 8 or later version.
*
* @param value
* the value to convert
* @return the Duration
*/
public static Duration valueToDuration(Value value) {
if (!(value instanceof ValueInterval)) {
value = value.convertTo(TypeInfo.TYPE_INTERVAL_DAY_TO_SECOND);
}
if (DataType.isYearMonthIntervalType(value.getValueType())) {
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, (Throwable) null, value.getString());
}
BigInteger[] dr = IntervalUtils.intervalToAbsolute((ValueInterval) value)
.divideAndRemainder(BigInteger.valueOf(1_000_000_000));
return Duration.ofSeconds(dr[0].longValue(), dr[1].longValue());
}
/**
* Converts a LocalDate to a Value.
*
* @param localDate
* the LocalDate to convert, not {@code null}
* @return the value
*/
public static ValueDate localDateToValue(LocalDate localDate) {
return ValueDate.fromDateValue(
dateValue(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()));
}
/**
* Converts a LocalTime to a Value.
*
* @param localTime
* the LocalTime to convert, not {@code null}
* @return the value
*/
public static ValueTime localTimeToValue(LocalTime localTime) {
return ValueTime.fromNanos(localTime.toNanoOfDay());
}
/**
* Converts a LocalDateTime to a Value.
*
* @param localDateTime
* the LocalDateTime to convert, not {@code null}
* @return the value
*/
public static ValueTimestamp localDateTimeToValue(LocalDateTime localDateTime) {
LocalDate localDate = localDateTime.toLocalDate();
return ValueTimestamp.fromDateValueAndNanos(
dateValue(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()),
localDateTime.toLocalTime().toNanoOfDay());
}
/**
* Converts a Instant to a Value.
*
* @param instant
* the Instant to convert, not {@code null}
* @return the value
*/
public static ValueTimestampTimeZone instantToValue(Instant instant) {
long epochSecond = instant.getEpochSecond();
int nano = instant.getNano();
long absoluteDay = epochSecond / 86_400;
// Round toward negative infinity
if (epochSecond < 0 && (absoluteDay * 86_400 != epochSecond)) {
absoluteDay--;
}
long timeNanos = (epochSecond - absoluteDay * 86_400) * 1_000_000_000 + nano;
return ValueTimestampTimeZone.fromDateValueAndNanos(dateValueFromAbsoluteDay(absoluteDay),
timeNanos, 0);
}
/**
* Converts a OffsetDateTime to a Value.
*
* @param offsetDateTime
* the OffsetDateTime to convert, not {@code null}
* @return the value
*/
public static ValueTimestampTimeZone offsetDateTimeToValue(OffsetDateTime offsetDateTime) {
LocalDateTime localDateTime = offsetDateTime.toLocalDateTime();
LocalDate localDate = localDateTime.toLocalDate();
return ValueTimestampTimeZone.fromDateValueAndNanos(
dateValue(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()),
localDateTime.toLocalTime().toNanoOfDay(), //
offsetDateTime.getOffset().getTotalSeconds());
}
/**
* Converts a ZonedDateTime to a Value.
*
* @param zonedDateTime
* the ZonedDateTime to convert, not {@code null}
* @return the value
*/
public static ValueTimestampTimeZone zonedDateTimeToValue(ZonedDateTime zonedDateTime) {
LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
LocalDate localDate = localDateTime.toLocalDate();
return ValueTimestampTimeZone.fromDateValueAndNanos(
dateValue(localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()),
localDateTime.toLocalTime().toNanoOfDay(), //
zonedDateTime.getOffset().getTotalSeconds());
}
/**
* Converts a OffsetTime to a Value.
*
* @param offsetTime
* the OffsetTime to convert, not {@code null}
* @return the value
*/
public static ValueTimeTimeZone offsetTimeToValue(OffsetTime offsetTime) {
return ValueTimeTimeZone.fromNanos(offsetTime.toLocalTime().toNanoOfDay(),
offsetTime.getOffset().getTotalSeconds());
}
private static LocalDateTime localDateTimeFromDateNanos(long dateValue, long timeNanos) {
if (dateValue > MAX_DATE_VALUE) {
return LocalDateTime.MAX;
} else if (dateValue < MIN_DATE_VALUE) {
return LocalDateTime.MIN;
}
return LocalDateTime.of(LocalDate.of(yearFromDateValue(dateValue),
monthFromDateValue(dateValue), dayFromDateValue(dateValue)),
LocalTime.ofNanoOfDay(timeNanos));
}
/**
* Converts a Period to a Value.
*
* @param period
* the Period to convert, not {@code null}
* @return the value
*/
public static ValueInterval periodToValue(Period period) {
int days = period.getDays();
if (days != 0) {
throw DbException.getInvalidValueException("Period.days", days);
}
int years = period.getYears();
int months = period.getMonths();
IntervalQualifier qualifier;
boolean negative = false;
long leading = 0L, remaining = 0L;
if (years == 0) {
if (months == 0L) {
// Use generic qualifier
qualifier = IntervalQualifier.YEAR_TO_MONTH;
} else {
qualifier = IntervalQualifier.MONTH;
leading = months;
if (leading < 0) {
leading = -leading;
negative = true;
}
}
} else {
if (months == 0L) {
qualifier = IntervalQualifier.YEAR;
leading = years;
if (leading < 0) {
leading = -leading;
negative = true;
}
} else {
qualifier = IntervalQualifier.YEAR_TO_MONTH;
leading = years * 12 + months;
if (leading < 0) {
leading = -leading;
negative = true;
}
remaining = leading % 12;
leading /= 12;
}
}
return ValueInterval.from(qualifier, negative, leading, remaining);
}
/**
* Converts a Duration to a Value.
*
* @param duration
* the Duration to convert, not {@code null}
* @return the value
*/
public static ValueInterval durationToValue(Duration duration) {
long seconds = duration.getSeconds();
int nano = duration.getNano();
boolean negative = seconds < 0;
seconds = Math.abs(seconds);
if (negative && nano != 0) {
nano = 1_000_000_000 - nano;
seconds--;
}
return ValueInterval.from(IntervalQualifier.SECOND, negative, seconds, nano);
}
}