Fix temporal round in tests and render calendar with milliseconds precision
This commit is contained in:
parent
7376a1cdfb
commit
a9be2e1584
|
@ -94,6 +94,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* A {@linkplain Dialect SQL dialect} for CockroachDB.
|
||||
|
@ -646,7 +647,7 @@ public class CockroachLegacyDialect extends Dialect {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "timestamp with time zone '" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( '\'' );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -106,6 +106,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* A legacy {@linkplain Dialect SQL dialect} for H2.
|
||||
|
@ -536,7 +537,7 @@ public class H2LegacyDialect extends Dialect {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "timestamp with time zone '" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( '\'' );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -133,6 +133,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* A {@linkplain Dialect SQL dialect} for PostgreSQL 8 and above.
|
||||
|
@ -1119,7 +1120,7 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "timestamp with time zone '" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( '\'' );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -86,6 +86,7 @@ import static org.hibernate.type.SqlTypes.*;
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* A dialect for Microsoft SQL Server 2000 and above
|
||||
|
@ -924,7 +925,7 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( "' as datetime2)" );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -83,6 +83,7 @@ import static org.hibernate.type.SqlTypes.VARBINARY;
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* An SQL dialect for SQLite.
|
||||
|
@ -713,7 +714,7 @@ public class SQLiteDialect extends Dialect {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "datetime(" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( ')' );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -104,6 +104,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* A {@linkplain Dialect SQL dialect} for CockroachDB.
|
||||
|
@ -664,7 +665,7 @@ public class CockroachDialect extends Dialect {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "timestamp with time zone '" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( '\'' );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -229,6 +229,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIME
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* Represents a dialect of SQL implemented by a particular RDBMS. Every
|
||||
|
@ -4380,7 +4381,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( JDBC_ESCAPE_START_TIMESTAMP );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( JDBC_ESCAPE_END );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -96,6 +96,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* A {@linkplain Dialect SQL dialect} for H2.
|
||||
|
@ -514,7 +515,7 @@ public class H2Dialect extends Dialect {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "timestamp with time zone '" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( '\'' );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -119,6 +119,7 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* A {@linkplain Dialect SQL dialect} for PostgreSQL 8 and above.
|
||||
|
@ -1104,7 +1105,7 @@ public class PostgreSQLDialect extends Dialect {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "timestamp with time zone '" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( '\'' );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -103,6 +103,7 @@ import static org.hibernate.type.SqlTypes.VARCHAR;
|
|||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
|
||||
|
||||
/**
|
||||
* A dialect for Microsoft SQL Server 2008 and above
|
||||
|
@ -932,7 +933,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( "' as datetime2)" );
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -14,12 +14,14 @@ import java.time.LocalTime;
|
|||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
|
||||
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
|
||||
|
@ -41,8 +43,10 @@ public final class DateTimeUtils {
|
|||
public static final String FORMAT_STRING_TIMESTAMP = "yyyy-MM-dd HH:mm:ss";
|
||||
public static final String FORMAT_STRING_TIMESTAMP_WITH_MILLIS = FORMAT_STRING_TIMESTAMP + ".SSS";
|
||||
public static final String FORMAT_STRING_TIMESTAMP_WITH_MICROS = FORMAT_STRING_TIMESTAMP + ".SSSSSS";
|
||||
public static final String FORMAT_STRING_TIMESTAMP_WITH_NANOS = FORMAT_STRING_TIMESTAMP + ".SSSSSSSSS";
|
||||
public static final String FORMAT_STRING_TIMESTAMP_WITH_MILLIS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_MILLIS + "XXX";
|
||||
public static final String FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_MICROS + "XXX";
|
||||
public static final String FORMAT_STRING_TIMESTAMP_WITH_NANOS_AND_OFFSET = FORMAT_STRING_TIMESTAMP_WITH_NANOS + "XXX";
|
||||
|
||||
public static final DateTimeFormatter DATE_TIME_FORMATTER_DATE = DateTimeFormatter.ofPattern( FORMAT_STRING_DATE, Locale.ENGLISH );
|
||||
public static final DateTimeFormatter DATE_TIME_FORMATTER_TIME_WITH_OFFSET = DateTimeFormatter.ofPattern( FORMAT_STRING_TIME_WITH_OFFSET, Locale.ENGLISH );
|
||||
|
@ -55,6 +59,10 @@ public final class DateTimeUtils {
|
|||
FORMAT_STRING_TIMESTAMP_WITH_MICROS,
|
||||
Locale.ENGLISH
|
||||
);
|
||||
public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_NANOS = DateTimeFormatter.ofPattern(
|
||||
FORMAT_STRING_TIMESTAMP_WITH_NANOS,
|
||||
Locale.ENGLISH
|
||||
);
|
||||
public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET = DateTimeFormatter.ofPattern(
|
||||
FORMAT_STRING_TIMESTAMP_WITH_MILLIS_AND_OFFSET,
|
||||
Locale.ENGLISH
|
||||
|
@ -63,6 +71,10 @@ public final class DateTimeUtils {
|
|||
FORMAT_STRING_TIMESTAMP_WITH_MICROS_AND_OFFSET,
|
||||
Locale.ENGLISH
|
||||
);
|
||||
public static final DateTimeFormatter DATE_TIME_FORMATTER_TIMESTAMP_WITH_NANOS_AND_OFFSET = DateTimeFormatter.ofPattern(
|
||||
FORMAT_STRING_TIMESTAMP_WITH_NANOS_AND_OFFSET,
|
||||
Locale.ENGLISH
|
||||
);
|
||||
|
||||
public static final String JDBC_ESCAPE_START_DATE = "{d '";
|
||||
public static final String JDBC_ESCAPE_START_TIME = "{t '";
|
||||
|
@ -96,12 +108,6 @@ public final class DateTimeUtils {
|
|||
Locale.ENGLISH
|
||||
)
|
||||
);
|
||||
private static final ThreadLocal<SimpleDateFormat> TIMESTAMP_WITH_MICROS_FORMAT = ThreadLocal.withInitial(
|
||||
() -> new SimpleDateFormat(
|
||||
FORMAT_STRING_TIMESTAMP_WITH_MICROS,
|
||||
Locale.ENGLISH
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Pattern used for parsing literal offset datetimes in HQL.
|
||||
|
@ -120,45 +126,34 @@ public final class DateTimeUtils {
|
|||
.appendOffset("+HH:mm", "+00")
|
||||
.toFormatter();
|
||||
|
||||
public static void appendAsTimestampWithNanos(
|
||||
SqlAppender appender,
|
||||
TemporalAccessor temporalAccessor,
|
||||
boolean supportsOffset,
|
||||
TimeZone jdbcTimeZone) {
|
||||
appendAsTimestamp(
|
||||
appender,
|
||||
temporalAccessor,
|
||||
supportsOffset,
|
||||
jdbcTimeZone,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_NANOS,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_NANOS_AND_OFFSET
|
||||
);
|
||||
}
|
||||
|
||||
public static void appendAsTimestampWithMicros(
|
||||
SqlAppender appender,
|
||||
TemporalAccessor temporalAccessor,
|
||||
boolean supportsOffset,
|
||||
TimeZone jdbcTimeZone) {
|
||||
if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) {
|
||||
if ( supportsOffset ) {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET.formatTo( temporalAccessor, appender );
|
||||
}
|
||||
else {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.formatTo(
|
||||
LocalDateTime.ofInstant(
|
||||
Instant.from( temporalAccessor ),
|
||||
jdbcTimeZone.toZoneId()
|
||||
),
|
||||
appender
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ( temporalAccessor instanceof Instant ) {
|
||||
if ( supportsOffset ) {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET.formatTo(
|
||||
( (Instant) temporalAccessor ).atZone( jdbcTimeZone.toZoneId() ),
|
||||
appender
|
||||
);
|
||||
}
|
||||
else {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.formatTo(
|
||||
LocalDateTime.ofInstant(
|
||||
(Instant) temporalAccessor,
|
||||
jdbcTimeZone.toZoneId()
|
||||
),
|
||||
appender
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS.formatTo( temporalAccessor, appender );
|
||||
}
|
||||
appendAsTimestamp(
|
||||
appender,
|
||||
temporalAccessor,
|
||||
supportsOffset,
|
||||
jdbcTimeZone,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET
|
||||
);
|
||||
}
|
||||
|
||||
public static void appendAsTimestampWithMillis(
|
||||
|
@ -166,12 +161,29 @@ public final class DateTimeUtils {
|
|||
TemporalAccessor temporalAccessor,
|
||||
boolean supportsOffset,
|
||||
TimeZone jdbcTimeZone) {
|
||||
appendAsTimestamp(
|
||||
appender,
|
||||
temporalAccessor,
|
||||
supportsOffset,
|
||||
jdbcTimeZone,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET
|
||||
);
|
||||
}
|
||||
|
||||
private static void appendAsTimestamp(
|
||||
SqlAppender appender,
|
||||
TemporalAccessor temporalAccessor,
|
||||
boolean supportsOffset,
|
||||
TimeZone jdbcTimeZone,
|
||||
DateTimeFormatter format,
|
||||
DateTimeFormatter formatWithOffset) {
|
||||
if ( temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) {
|
||||
if ( supportsOffset ) {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET.formatTo( temporalAccessor, appender );
|
||||
formatWithOffset.formatTo( temporalAccessor, appender );
|
||||
}
|
||||
else {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.formatTo(
|
||||
format.formatTo(
|
||||
LocalDateTime.ofInstant(
|
||||
Instant.from( temporalAccessor ),
|
||||
jdbcTimeZone.toZoneId()
|
||||
|
@ -182,13 +194,13 @@ public final class DateTimeUtils {
|
|||
}
|
||||
else if ( temporalAccessor instanceof Instant ) {
|
||||
if ( supportsOffset ) {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS_AND_OFFSET.formatTo(
|
||||
formatWithOffset.formatTo(
|
||||
( (Instant) temporalAccessor ).atZone( jdbcTimeZone.toZoneId() ),
|
||||
appender
|
||||
);
|
||||
}
|
||||
else {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.formatTo(
|
||||
format.formatTo(
|
||||
LocalDateTime.ofInstant(
|
||||
(Instant) temporalAccessor,
|
||||
jdbcTimeZone.toZoneId()
|
||||
|
@ -198,7 +210,7 @@ public final class DateTimeUtils {
|
|||
}
|
||||
}
|
||||
else {
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MILLIS.formatTo( temporalAccessor, appender );
|
||||
format.formatTo( temporalAccessor, appender );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,22 +253,54 @@ public final class DateTimeUtils {
|
|||
}
|
||||
|
||||
public static void appendAsTimestampWithMicros(SqlAppender appender, Date date, TimeZone jdbcTimeZone) {
|
||||
final SimpleDateFormat simpleDateFormat;
|
||||
if ( date instanceof Timestamp ) {
|
||||
// java.sql.Timestamp supports micro sec
|
||||
simpleDateFormat = TIMESTAMP_WITH_MICROS_FORMAT.get();
|
||||
// java.sql.Timestamp supports nano sec
|
||||
appendAsTimestamp(
|
||||
appender,
|
||||
date.toInstant().atZone( jdbcTimeZone.toZoneId() ),
|
||||
false,
|
||||
jdbcTimeZone,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_MICROS_AND_OFFSET
|
||||
);
|
||||
}
|
||||
else {
|
||||
// java.util.Date supports only milli sec
|
||||
simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get();
|
||||
final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get();
|
||||
final TimeZone originalTimeZone = simpleDateFormat.getTimeZone();
|
||||
try {
|
||||
simpleDateFormat.setTimeZone( jdbcTimeZone );
|
||||
appender.appendSql( simpleDateFormat.format( date ) );
|
||||
}
|
||||
finally {
|
||||
simpleDateFormat.setTimeZone( originalTimeZone );
|
||||
}
|
||||
}
|
||||
final TimeZone originalTimeZone = simpleDateFormat.getTimeZone();
|
||||
try {
|
||||
simpleDateFormat.setTimeZone( jdbcTimeZone );
|
||||
appender.appendSql( simpleDateFormat.format( date ) );
|
||||
}
|
||||
|
||||
public static void appendAsTimestampWithNanos(SqlAppender appender, Date date, TimeZone jdbcTimeZone) {
|
||||
if ( date instanceof Timestamp ) {
|
||||
// java.sql.Timestamp supports nano sec
|
||||
appendAsTimestamp(
|
||||
appender,
|
||||
date.toInstant().atZone( jdbcTimeZone.toZoneId() ),
|
||||
false,
|
||||
jdbcTimeZone,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_NANOS,
|
||||
DATE_TIME_FORMATTER_TIMESTAMP_WITH_NANOS_AND_OFFSET
|
||||
);
|
||||
}
|
||||
finally {
|
||||
simpleDateFormat.setTimeZone( originalTimeZone );
|
||||
else {
|
||||
// java.util.Date supports only milli sec
|
||||
final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get();
|
||||
final TimeZone originalTimeZone = simpleDateFormat.getTimeZone();
|
||||
try {
|
||||
simpleDateFormat.setTimeZone( jdbcTimeZone );
|
||||
appender.appendSql( simpleDateFormat.format( date ) );
|
||||
}
|
||||
finally {
|
||||
simpleDateFormat.setTimeZone( originalTimeZone );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,6 +347,12 @@ public final class DateTimeUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calendar has no microseconds.
|
||||
*
|
||||
* @deprecated use {@link #appendAsTimestampWithMillis(SqlAppender, Calendar, TimeZone)} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void appendAsTimestampWithMicros(SqlAppender appender, Calendar calendar, TimeZone jdbcTimeZone) {
|
||||
// it is possible to use micro sec resolution with java.util.Date
|
||||
final SimpleDateFormat simpleDateFormat = TIMESTAMP_WITH_MILLIS_FORMAT.get();
|
||||
|
@ -352,4 +402,45 @@ public final class DateTimeUtils {
|
|||
appender.appendSql( LOCAL_TIME_FORMAT.get().format( calendar.getTime() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the same conversion that databases do when they encounter a timestamp with a higher precision
|
||||
* than what is supported by a column, which is to round the excess fractions.
|
||||
*/
|
||||
public static <T extends Temporal> T roundToDefaultPrecision(T temporal, Dialect d) {
|
||||
final int defaultTimestampPrecision = d.getDefaultTimestampPrecision();
|
||||
if ( defaultTimestampPrecision >= 9 || !temporal.isSupported( ChronoField.NANO_OF_SECOND ) ) {
|
||||
return temporal;
|
||||
}
|
||||
final int precisionMask = pow10( 9 - defaultTimestampPrecision );
|
||||
final int nano = temporal.get( ChronoField.NANO_OF_SECOND );
|
||||
final int nanosToRound = nano % precisionMask;
|
||||
final int finalNano = nano - nanosToRound + ( nanosToRound >= ( precisionMask >> 1 ) ? precisionMask : 0 );
|
||||
//noinspection unchecked
|
||||
return (T) temporal.with( ChronoField.NANO_OF_SECOND, finalNano );
|
||||
}
|
||||
|
||||
private static int pow10(int exponent) {
|
||||
switch ( exponent ) {
|
||||
case 0:
|
||||
return 1;
|
||||
case 1:
|
||||
return 10;
|
||||
case 2:
|
||||
return 100;
|
||||
case 3:
|
||||
return 1000;
|
||||
case 4:
|
||||
return 10000;
|
||||
case 5:
|
||||
return 100000;
|
||||
case 6:
|
||||
return 1000000;
|
||||
case 7:
|
||||
return 10000000;
|
||||
case 8:
|
||||
return 100000000;
|
||||
default:
|
||||
return (int) Math.pow( 10, exponent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
|
@ -37,14 +40,21 @@ public class AutoZonedTest {
|
|||
});
|
||||
scope.inSession( s -> {
|
||||
Zoned z = s.find(Zoned.class, id);
|
||||
if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof SybaseDialect) {
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
if ( dialect instanceof SybaseDialect ) {
|
||||
// Sybase with jTDS driver has 1/300th sec precision
|
||||
assertEquals( nowZoned.toInstant().truncatedTo(ChronoUnit.SECONDS), z.zonedDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS) );
|
||||
assertEquals( nowOffset.toInstant().truncatedTo(ChronoUnit.SECONDS), z.offsetDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS) );
|
||||
}
|
||||
else {
|
||||
assertEquals( nowZoned.toInstant(), z.zonedDateTime.toInstant() );
|
||||
assertEquals( nowOffset.toInstant(), z.offsetDateTime.toInstant() );
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
|
||||
);
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
|
||||
);
|
||||
}
|
||||
assertEquals( nowZoned.toOffsetDateTime().getOffset(), z.zonedDateTime.toOffsetDateTime().getOffset() );
|
||||
assertEquals( nowOffset.getOffset(), z.offsetDateTime.getOffset() );
|
||||
|
|
|
@ -4,7 +4,10 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
|
@ -37,14 +40,21 @@ public class ColumnZonedTest {
|
|||
});
|
||||
scope.inSession( s -> {
|
||||
Zoned z = s.find(Zoned.class, id);
|
||||
if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof SybaseDialect) {
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
if ( dialect instanceof SybaseDialect) {
|
||||
// Sybase with jTDS driver has 1/300th sec precision
|
||||
assertEquals( nowZoned.toInstant().truncatedTo(ChronoUnit.SECONDS), z.zonedDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS) );
|
||||
assertEquals( nowOffset.toInstant().truncatedTo(ChronoUnit.SECONDS), z.offsetDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS) );
|
||||
}
|
||||
else {
|
||||
assertEquals( nowZoned.toInstant(), z.zonedDateTime.toInstant() );
|
||||
assertEquals( nowOffset.toInstant(), z.offsetDateTime.toInstant() );
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
|
||||
);
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
|
||||
);
|
||||
}
|
||||
assertEquals( nowZoned.toOffsetDateTime().getOffset(), z.zonedDateTime.toOffsetDateTime().getOffset() );
|
||||
assertEquals( nowOffset.getOffset(), z.offsetDateTime.getOffset() );
|
||||
|
|
|
@ -3,8 +3,12 @@ package org.hibernate.orm.test.timezones;
|
|||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.dialect.TimeZoneSupport;
|
||||
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
|
@ -34,16 +38,23 @@ public class DefaultZonedTest {
|
|||
});
|
||||
scope.inSession( s -> {
|
||||
Zoned z = s.find(Zoned.class, id);
|
||||
if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof SybaseDialect) {
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
if ( dialect instanceof SybaseDialect) {
|
||||
// Sybase with jTDS driver has 1/300th sec precision
|
||||
assertEquals( nowZoned.toInstant().truncatedTo(ChronoUnit.SECONDS), z.zonedDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS) );
|
||||
assertEquals( nowOffset.toInstant().truncatedTo(ChronoUnit.SECONDS), z.offsetDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS) );
|
||||
}
|
||||
else {
|
||||
assertEquals( nowZoned.toInstant(), z.zonedDateTime.toInstant() );
|
||||
assertEquals( nowOffset.toInstant(), z.offsetDateTime.toInstant() );
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
|
||||
);
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
|
||||
);
|
||||
}
|
||||
if ( scope.getSessionFactory().getJdbcServices().getDialect().getTimeZoneSupport() == TimeZoneSupport.NATIVE ) {
|
||||
if ( dialect.getTimeZoneSupport() == TimeZoneSupport.NATIVE ) {
|
||||
assertEquals( nowZoned.toOffsetDateTime().getOffset(), z.zonedDateTime.toOffsetDateTime().getOffset() );
|
||||
assertEquals( nowOffset.getOffset(), z.offsetDateTime.getOffset() );
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
|
@ -29,7 +32,7 @@ public class JDBCTimeZoneZonedTest {
|
|||
|
||||
@Test void test(SessionFactoryScope scope) {
|
||||
ZonedDateTime nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") );
|
||||
OffsetDateTime nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) );
|
||||
OffsetDateTime nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours( 3) );
|
||||
long id = scope.fromTransaction( s-> {
|
||||
Zoned z = new Zoned();
|
||||
z.zonedDateTime = nowZoned;
|
||||
|
@ -41,14 +44,21 @@ public class JDBCTimeZoneZonedTest {
|
|||
Zoned z = s.find(Zoned.class, id);
|
||||
ZoneId systemZone = ZoneId.systemDefault();
|
||||
ZoneOffset systemOffset = systemZone.getRules().getOffset( Instant.now() );
|
||||
if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof SybaseDialect) {
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
if ( dialect instanceof SybaseDialect) {
|
||||
// Sybase with jTDS driver has 1/300th sec precision
|
||||
assertEquals( nowZoned.toInstant().truncatedTo(ChronoUnit.SECONDS), z.zonedDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS));
|
||||
assertEquals( nowOffset.toInstant().truncatedTo(ChronoUnit.SECONDS), z.offsetDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS));
|
||||
}
|
||||
else {
|
||||
assertEquals( nowZoned.withZoneSameInstant(systemZone), z.zonedDateTime );
|
||||
assertEquals( nowOffset.withOffsetSameInstant(systemOffset), z.offsetDateTime );
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
|
||||
);
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
|
||||
);
|
||||
}
|
||||
assertEquals( systemZone, z.zonedDateTime.getZone() );
|
||||
assertEquals( systemOffset, z.offsetDateTime.getOffset() );
|
||||
|
|
|
@ -4,7 +4,10 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
|
@ -28,7 +31,7 @@ public class PassThruZonedTest {
|
|||
|
||||
@Test void test(SessionFactoryScope scope) {
|
||||
ZonedDateTime nowZoned = ZonedDateTime.now().withZoneSameInstant( ZoneId.of("CET") );
|
||||
OffsetDateTime nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours(3) );
|
||||
OffsetDateTime nowOffset = OffsetDateTime.now().withOffsetSameInstant( ZoneOffset.ofHours( 3) );
|
||||
long id = scope.fromTransaction( s-> {
|
||||
Zoned z = new Zoned();
|
||||
z.zonedDateTime = nowZoned;
|
||||
|
@ -40,14 +43,21 @@ public class PassThruZonedTest {
|
|||
Zoned z = s.find(Zoned.class, id);
|
||||
ZoneId systemZone = ZoneId.systemDefault();
|
||||
ZoneOffset systemOffset = systemZone.getRules().getOffset( Instant.now() );
|
||||
if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof SybaseDialect) {
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
if ( dialect instanceof SybaseDialect) {
|
||||
// Sybase with jTDS driver has 1/300th sec precision
|
||||
assertEquals( nowZoned.toInstant().truncatedTo(ChronoUnit.SECONDS), z.zonedDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS));
|
||||
assertEquals( nowOffset.toInstant().truncatedTo(ChronoUnit.SECONDS), z.offsetDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS));
|
||||
}
|
||||
else {
|
||||
assertEquals( nowZoned.withZoneSameInstant(systemZone), z.zonedDateTime );
|
||||
assertEquals( nowOffset.withOffsetSameInstant(systemOffset), z.offsetDateTime );
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
|
||||
);
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
|
||||
);
|
||||
}
|
||||
assertEquals( systemZone, z.zonedDateTime.getZone() );
|
||||
assertEquals( systemOffset, z.offsetDateTime.getOffset() );
|
||||
|
|
|
@ -4,7 +4,10 @@ import jakarta.persistence.Entity;
|
|||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.type.descriptor.DateTimeUtils;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
|
@ -37,14 +40,21 @@ public class UTCNormalizedZonedTest {
|
|||
});
|
||||
scope.inSession( s-> {
|
||||
Zoned z = s.find(Zoned.class, id);
|
||||
if ( scope.getSessionFactory().getJdbcServices().getDialect() instanceof SybaseDialect) {
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
if ( dialect instanceof SybaseDialect) {
|
||||
// Sybase with jTDS driver has 1/300th sec precision
|
||||
assertEquals( nowZoned.toInstant().truncatedTo(ChronoUnit.SECONDS), z.zonedDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS) );
|
||||
assertEquals( nowOffset.toInstant().truncatedTo(ChronoUnit.SECONDS), z.offsetDateTime.toInstant().truncatedTo(ChronoUnit.SECONDS) );
|
||||
}
|
||||
else {
|
||||
assertEquals( nowZoned.toInstant(), z.zonedDateTime.toInstant() );
|
||||
assertEquals( nowOffset.toInstant(), z.offsetDateTime.toInstant() );
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowZoned.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.zonedDateTime.toInstant(), dialect )
|
||||
);
|
||||
assertEquals(
|
||||
DateTimeUtils.roundToDefaultPrecision( nowOffset.toInstant(), dialect ),
|
||||
DateTimeUtils.roundToDefaultPrecision( z.offsetDateTime.toInstant(), dialect )
|
||||
);
|
||||
}
|
||||
assertEquals( ZoneId.of("Z"), z.zonedDateTime.getZone() );
|
||||
assertEquals( ZoneOffset.ofHours(0), z.offsetDateTime.getOffset() );
|
||||
|
|
Loading…
Reference in New Issue