org.h2.util.LegacyDateTimeUtils 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.MILLIS_PER_DAY;
import static org.h2.util.DateTimeUtils.NANOS_PER_SECOND;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import org.h2.engine.CastDataProvider;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueNull;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
/**
* Date and time utilities for {@link Date}, {@link Time}, and {@link Timestamp}
* classes.
*/
public final class LegacyDateTimeUtils {
/**
* Gregorian change date for a {@link java.util.GregorianCalendar} that
* represents a proleptic Gregorian calendar.
*/
public static final Date PROLEPTIC_GREGORIAN_CHANGE = new Date(Long.MIN_VALUE);
/**
* UTC time zone.
*/
public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
private LegacyDateTimeUtils() {
}
/**
* Get or create a date value for the given date.
*
* @param provider
* the cast information provider
* @param timeZone
* time zone, or {@code null} for default
* @param date
* the date
* @return the value
*/
public static ValueDate fromDate(CastDataProvider provider, TimeZone timeZone, Date date) {
long ms = date.getTime();
return ValueDate.fromDateValue(dateValueFromLocalMillis(
ms + (timeZone == null ? getTimeZoneOffsetMillis(provider, ms) : timeZone.getOffset(ms))));
}
/**
* Get or create a time value for the given time.
*
* @param provider
* the cast information provider
* @param timeZone
* time zone, or {@code null} for default
* @param time
* the time
* @return the value
*/
public static ValueTime fromTime(CastDataProvider provider, TimeZone timeZone, Time time) {
long ms = time.getTime();
return ValueTime.fromNanos(nanosFromLocalMillis(
ms + (timeZone == null ? getTimeZoneOffsetMillis(provider, ms) : timeZone.getOffset(ms))));
}
/**
* Get or create a timestamp value for the given timestamp.
*
* @param provider
* the cast information provider
* @param timeZone
* time zone, or {@code null} for default
* @param timestamp
* the timestamp
* @return the value
*/
public static ValueTimestamp fromTimestamp(CastDataProvider provider, TimeZone timeZone, Timestamp timestamp) {
long ms = timestamp.getTime();
return timestampFromLocalMillis(
ms + (timeZone == null ? getTimeZoneOffsetMillis(provider, ms) : timeZone.getOffset(ms)),
timestamp.getNanos() % 1_000_000);
}
/**
* Get or create a timestamp value for the given date/time in millis.
*
* @param provider
* the cast information provider
* @param ms
* the milliseconds
* @param nanos
* the nanoseconds
* @return the value
*/
public static ValueTimestamp fromTimestamp(CastDataProvider provider, long ms, int nanos) {
return timestampFromLocalMillis(ms + getTimeZoneOffsetMillis(provider, ms), nanos);
}
private static ValueTimestamp timestampFromLocalMillis(long ms, int nanos) {
long dateValue = dateValueFromLocalMillis(ms);
long timeNanos = nanos + nanosFromLocalMillis(ms);
return ValueTimestamp.fromDateValueAndNanos(dateValue, timeNanos);
}
/**
* Convert a local datetime in millis to an encoded date.
*
* @param ms
* the milliseconds
* @return the date value
*/
public static long dateValueFromLocalMillis(long ms) {
long absoluteDay = ms / MILLIS_PER_DAY;
// Round toward negative infinity
if (ms < 0 && (absoluteDay * MILLIS_PER_DAY != ms)) {
absoluteDay--;
}
return DateTimeUtils.dateValueFromAbsoluteDay(absoluteDay);
}
/**
* Convert a time in milliseconds in local time to the nanoseconds since
* midnight.
*
* @param ms
* the milliseconds
* @return the nanoseconds
*/
public static long nanosFromLocalMillis(long ms) {
ms %= MILLIS_PER_DAY;
if (ms < 0) {
ms += MILLIS_PER_DAY;
}
return ms * 1_000_000;
}
/**
* Get the date value converted to the specified time zone.
*
* @param provider the cast information provider
* @param timeZone the target time zone
* @param value the value to convert
* @return the date
*/
public static Date toDate(CastDataProvider provider, TimeZone timeZone, Value value) {
return value != ValueNull.INSTANCE
? new Date(getMillis(provider, timeZone, value.convertToDate(provider).getDateValue(), 0)) : null;
}
/**
* Get the time value converted to the specified time zone.
*
* @param provider the cast information provider
* @param timeZone the target time zone
* @param value the value to convert
* @return the time
*/
public static Time toTime(CastDataProvider provider, TimeZone timeZone, Value value) {
switch (value.getValueType()) {
case Value.NULL:
return null;
default:
value = value.convertTo(TypeInfo.TYPE_TIME, provider);
//$FALL-THROUGH$
case Value.TIME:
return new Time(
getMillis(provider, timeZone, DateTimeUtils.EPOCH_DATE_VALUE, ((ValueTime) value).getNanos()));
}
}
/**
* Get the timestamp value converted to the specified time zone.
*
* @param provider the cast information provider
* @param timeZone the target time zone
* @param value the value to convert
* @return the timestamp
*/
public static Timestamp toTimestamp(CastDataProvider provider, TimeZone timeZone, Value value) {
switch (value.getValueType()) {
case Value.NULL:
return null;
default:
value = value.convertTo(TypeInfo.TYPE_TIMESTAMP, provider);
//$FALL-THROUGH$
case Value.TIMESTAMP: {
ValueTimestamp v = (ValueTimestamp) value;
long timeNanos = v.getTimeNanos();
Timestamp ts = new Timestamp(getMillis(provider, timeZone, v.getDateValue(), timeNanos));
ts.setNanos((int) (timeNanos % NANOS_PER_SECOND));
return ts;
}
case Value.TIMESTAMP_TZ: {
ValueTimestampTimeZone v = (ValueTimestampTimeZone) value;
long timeNanos = v.getTimeNanos();
Timestamp ts = new Timestamp(DateTimeUtils.absoluteDayFromDateValue(v.getDateValue()) * MILLIS_PER_DAY
+ timeNanos / 1_000_000 - v.getTimeZoneOffsetSeconds() * 1_000);
ts.setNanos((int) (timeNanos % NANOS_PER_SECOND));
return ts;
}
}
}
/**
* Calculate the milliseconds since 1970-01-01 (UTC) for the given date and
* time (in the specified timezone).
*
* @param provider the cast information provider
* @param tz the timezone of the parameters, or null for the default
* timezone
* @param dateValue date value
* @param timeNanos nanoseconds since midnight
* @return the number of milliseconds (UTC)
*/
public static long getMillis(CastDataProvider provider, TimeZone tz, long dateValue, long timeNanos) {
return (tz == null ? provider != null ? provider.currentTimeZone() : DateTimeUtils.getTimeZone()
: TimeZoneProvider.ofId(tz.getID())).getEpochSecondsFromLocal(dateValue, timeNanos) * 1_000
+ timeNanos / 1_000_000 % 1_000;
}
/**
* Returns local time zone offset for a specified timestamp.
*
* @param provider the cast information provider
* @param ms milliseconds since Epoch in UTC
* @return local time zone offset
*/
public static int getTimeZoneOffsetMillis(CastDataProvider provider, long ms) {
long seconds = ms / 1_000;
// Round toward negative infinity
if (ms < 0 && (seconds * 1_000 != ms)) {
seconds--;
}
return (provider != null ? provider.currentTimeZone() : DateTimeUtils.getTimeZone())
.getTimeZoneOffsetUTC(seconds) * 1_000;
}
/**
* Convert a legacy Java object to a value.
*
* @param session
* the session
* @param x
* the value
* @return the value, or {@code null} if not supported
*/
public static Value legacyObjectToValue(CastDataProvider session, Object x) {
if (x instanceof Date) {
return fromDate(session, null, (Date) x);
} else if (x instanceof Time) {
return fromTime(session, null, (Time) x);
} else if (x instanceof Timestamp) {
return fromTimestamp(session, null, (Timestamp) x);
} else if (x instanceof java.util.Date) {
return fromTimestamp(session, ((java.util.Date) x).getTime(), 0);
} else if (x instanceof Calendar) {
Calendar gc = (Calendar) x;
long ms = gc.getTimeInMillis();
return timestampFromLocalMillis(ms + gc.getTimeZone().getOffset(ms), 0);
} else {
return null;
}
}
/**
* Converts the specified value to an object of the specified legacy type.
*
* @param the type
* @param type the class
* @param value the value
* @param provider the cast information provider
* @return an instance of the specified class, or {@code null} if not supported
*/
@SuppressWarnings("unchecked")
public static T valueToLegacyType(Class type, Value value, CastDataProvider provider) {
if (type == Date.class) {
return (T) toDate(provider, null, value);
} else if (type == Time.class) {
return (T) toTime(provider, null, value);
} else if (type == Timestamp.class) {
return (T) toTimestamp(provider, null, value);
} else if (type == java.util.Date.class) {
return (T) new java.util.Date(toTimestamp(provider, null, value).getTime());
} else if (type == Calendar.class) {
GregorianCalendar calendar = new GregorianCalendar();
calendar.setGregorianChange(PROLEPTIC_GREGORIAN_CHANGE);
calendar.setTime(toTimestamp(provider, calendar.getTimeZone(), value));
return (T) calendar;
} else {
return null;
}
}
/**
* Get the type information for the given legacy Java class.
*
* @param clazz
* the Java class
* @return the value type, or {@code null} if not supported
*/
public static TypeInfo legacyClassToType(Class> clazz) {
if (Date.class.isAssignableFrom(clazz)) {
return TypeInfo.TYPE_DATE;
} else if (Time.class.isAssignableFrom(clazz)) {
return TypeInfo.TYPE_TIME;
} else if (java.util.Date.class.isAssignableFrom(clazz) || Calendar.class.isAssignableFrom(clazz)) {
return TypeInfo.TYPE_TIMESTAMP;
} else{
return null;
}
}
}