Skip to content

Commit f24a290

Browse files
committed
[#1442] Add test for TimeZoneStorage
1 parent 6d925fb commit f24a290

File tree

1 file changed

+322
-0
lines changed

1 file changed

+322
-0
lines changed
Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.timezones;
7+
8+
import java.time.Duration;
9+
import java.time.LocalDateTime;
10+
import java.time.LocalTime;
11+
import java.time.OffsetDateTime;
12+
import java.time.OffsetTime;
13+
import java.time.ZoneOffset;
14+
import java.time.ZonedDateTime;
15+
import java.time.format.DateTimeFormatter;
16+
import java.util.Collection;
17+
import java.util.List;
18+
19+
import org.hibernate.annotations.TimeZoneColumn;
20+
import org.hibernate.annotations.TimeZoneStorage;
21+
import org.hibernate.annotations.TimeZoneStorageType;
22+
import org.hibernate.cfg.Configuration;
23+
import org.hibernate.reactive.BaseReactiveTest;
24+
import org.hibernate.reactive.testing.DBSelectionExtension;
25+
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.Test;
28+
import org.junit.jupiter.api.condition.DisabledIf;
29+
import org.junit.jupiter.api.extension.RegisterExtension;
30+
31+
import io.vertx.junit5.Timeout;
32+
import io.vertx.junit5.VertxTestContext;
33+
import jakarta.persistence.Column;
34+
import jakarta.persistence.Entity;
35+
import jakarta.persistence.Id;
36+
import jakarta.persistence.Table;
37+
import jakarta.persistence.Tuple;
38+
39+
import static java.util.concurrent.TimeUnit.MINUTES;
40+
import static org.assertj.core.api.Assertions.assertThat;
41+
import static org.hibernate.cfg.AvailableSettings.TIMEZONE_DEFAULT_STORAGE;
42+
import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.DB2;
43+
import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.ORACLE;
44+
import static org.hibernate.reactive.containers.DatabaseConfiguration.DBType.SQLSERVER;
45+
import static org.hibernate.reactive.containers.DatabaseConfiguration.dbType;
46+
47+
/**
48+
* Adapted from org.hibernate.orm.test.mapping.basic.TimeZoneStorageMappingTests
49+
*
50+
* <p>
51+
* Note that the tests below do not use ORM's annotations below to calculate
52+
* if a Dialect supports Format or Timezone types:
53+
* @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsFormat.class)
54+
* @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsTimezoneTypes.class....)
55+
* </p>
56+
* <p>
57+
* It appears that DB2, SQLServer and Oracle do not yet support FORMAT and none of reactive's supported Dialects
58+
* support Timezone Types via Offset, so the ORM's testNormalizeOffset(...) method is not included in these tests.
59+
* </p>
60+
*/
61+
@Timeout(value = 10, timeUnit = MINUTES)
62+
public class TimeZoneStorageMappingTest extends BaseReactiveTest {
63+
64+
public static Configuration configuration;
65+
66+
@RegisterExtension
67+
public DBSelectionExtension selectionRule = DBSelectionExtension.skipTestsFor( DB2, SQLSERVER );
68+
69+
private static final ZoneOffset JVM_TIMEZONE_OFFSET = OffsetDateTime.now().getOffset();
70+
private static final OffsetTime OFFSET_TIME = OffsetTime.of(
71+
LocalTime.of(
72+
12,
73+
0,
74+
0
75+
),
76+
ZoneOffset.ofHoursMinutes( 5, 45 )
77+
);
78+
private static final OffsetDateTime OFFSET_DATE_TIME = OffsetDateTime.of(
79+
LocalDateTime.of(
80+
2022,
81+
3,
82+
1,
83+
12,
84+
0,
85+
0
86+
),
87+
ZoneOffset.ofHoursMinutes( 5, 45 )
88+
);
89+
private static final ZonedDateTime ZONED_DATE_TIME = ZonedDateTime.of(
90+
LocalDateTime.of(
91+
2022,
92+
3,
93+
1,
94+
12,
95+
0,
96+
0
97+
),
98+
ZoneOffset.ofHoursMinutes( 5, 45 )
99+
);
100+
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern( "HH:mm:ssxxx" );
101+
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( "dd/MM/yyyy 'at' HH:mm:ssxxx" );
102+
103+
@Override
104+
protected Collection<Class<?>> annotatedEntities() {
105+
return List.of( TimeZoneStorageEntity.class );
106+
}
107+
108+
@Override
109+
protected Configuration constructConfiguration() {
110+
configuration = super.constructConfiguration();
111+
return configuration;
112+
}
113+
114+
public static boolean skipTestOffsetRetainedAuto() {
115+
return dbType() == ORACLE;
116+
}
117+
118+
public static boolean skipTestOffsetRetainedFormatAuto() {
119+
return dbType() == ORACLE;
120+
}
121+
122+
@Override
123+
protected void setProperties(Configuration configuration) {
124+
super.setProperties( configuration );
125+
configuration.setProperty( TIMEZONE_DEFAULT_STORAGE, "AUTO" );
126+
}
127+
128+
@BeforeEach
129+
public void populateDb(VertxTestContext context) {
130+
TimeZoneStorageEntity entity = new TimeZoneStorageEntity( 1, OFFSET_TIME, OFFSET_DATE_TIME, ZONED_DATE_TIME );
131+
132+
test( context, getMutinySessionFactory().withTransaction( (s, t) -> s.persist( entity ) ) );
133+
}
134+
135+
@DisabledIf( "skipTestOffsetRetainedAuto" )
136+
@Test
137+
public void testOffsetRetainedAuto(VertxTestContext context) {
138+
testOffsetRetained( context, "Auto" );
139+
}
140+
141+
@Test
142+
public void testOffsetRetainedColumn(VertxTestContext context) {
143+
testOffsetRetained( context, "Column" );
144+
}
145+
146+
@DisabledIf( "skipTestOffsetRetainedFormatAuto" )
147+
@Test
148+
public void testOffsetRetainedFormatAuto(VertxTestContext context) {
149+
testOffsetRetainedFormat( context, "Auto" );
150+
}
151+
152+
@Test
153+
public void testOffsetRetainedFormatColumn(VertxTestContext context) {
154+
testOffsetRetainedFormat( context, "Column" );
155+
}
156+
157+
public void testOffsetRetained(VertxTestContext context, String suffix) {
158+
test( context, openSession()
159+
.thenCompose( session -> session.createQuery(
160+
"select " +
161+
"e.offsetTime" + suffix + ", " +
162+
"e.offsetDateTime" + suffix + ", " +
163+
"e.zonedDateTime" + suffix + ", " +
164+
"extract(offset from e.offsetTime" + suffix + "), " +
165+
"extract(offset from e.offsetDateTime" + suffix + "), " +
166+
"extract(offset from e.zonedDateTime" + suffix + "), " +
167+
"e.offsetTime" + suffix + " + 1 hour, " +
168+
"e.offsetDateTime" + suffix + " + 1 hour, " +
169+
"e.zonedDateTime" + suffix + " + 1 hour, " +
170+
"e.offsetTime" + suffix + " + 1 hour - e.offsetTime" + suffix + ", " +
171+
"e.offsetDateTime" + suffix + " + 1 hour - e.offsetDateTime" + suffix + ", " +
172+
"e.zonedDateTime" + suffix + " + 1 hour - e.zonedDateTime" + suffix + ", " +
173+
"1 from TimeZoneStorageEntity e " +
174+
"where e.offsetDateTime" + suffix + " = e.offsetDateTime" + suffix,
175+
Tuple.class
176+
).getSingleResult()
177+
.thenAccept( result -> {
178+
assertThat( result.get( 0, OffsetTime.class ) ).isEqualTo( OFFSET_TIME );
179+
assertThat( result.get( 1, OffsetDateTime.class ) ).isEqualTo( OFFSET_DATE_TIME );
180+
assertThat( result.get( 2, ZonedDateTime.class ) ).isEqualTo( ZONED_DATE_TIME );
181+
assertThat( result.get( 3, ZoneOffset.class ) ).isEqualTo( OFFSET_TIME.getOffset() );
182+
assertThat( result.get( 4, ZoneOffset.class ) ).isEqualTo( OFFSET_DATE_TIME.getOffset() );
183+
assertThat( result.get( 5, ZoneOffset.class ) ).isEqualTo( ZONED_DATE_TIME.getOffset() );
184+
assertThat( result.get( 6, OffsetTime.class ) ).isEqualTo( OFFSET_TIME.plusHours( 1L ) );
185+
assertThat( result.get( 7, OffsetDateTime.class ) ).isEqualTo( OFFSET_DATE_TIME.plusHours( 1L ) );
186+
assertThat( result.get( 8, ZonedDateTime.class ) ).isEqualTo( ZONED_DATE_TIME.plusHours( 1L ) );
187+
assertThat( result.get( 9, Duration.class ) ).isEqualTo( Duration.ofHours( 1L ) );
188+
assertThat( result.get( 10, Duration.class ) ).isEqualTo( Duration.ofHours( 1L ) );
189+
assertThat( result.get( 11, Duration.class ) ).isEqualTo( Duration.ofHours( 1L ) );
190+
} )
191+
)
192+
);
193+
}
194+
195+
public void testOffsetRetainedFormat(VertxTestContext context, String suffix) {
196+
test( context, openSession()
197+
.thenCompose( session -> session.createQuery(
198+
"select " +
199+
"format(e.offsetTime" + suffix + " as 'HH:mm:ssxxx'), " +
200+
"format(e.offsetDateTime" + suffix + " as 'dd/MM/yyyy ''at'' HH:mm:ssxxx'), " +
201+
"format(e.zonedDateTime" + suffix + " as 'dd/MM/yyyy ''at'' HH:mm:ssxxx'), " +
202+
"1 from TimeZoneStorageEntity e " +
203+
"where e.offsetDateTime" + suffix + " = e.offsetDateTime" + suffix,
204+
Tuple.class
205+
).getSingleResult()
206+
.thenAccept( result -> {
207+
assertThat( result.get( 0, String.class ) ).isEqualTo( TIME_FORMATTER.format( OFFSET_TIME ) );
208+
assertThat( result.get( 1, String.class ) ).isEqualTo( FORMATTER.format( OFFSET_DATE_TIME ) );
209+
assertThat( result.get( 2, String.class ) ).isEqualTo( FORMATTER.format( ZONED_DATE_TIME ) );
210+
} )
211+
)
212+
);
213+
}
214+
215+
@Test
216+
public void testNormalize(VertxTestContext context) {
217+
test( context, openSession()
218+
.thenCompose( session -> session.createQuery(
219+
"select " +
220+
"e.offsetTimeNormalized, " +
221+
"e.offsetDateTimeNormalized, " +
222+
"e.zonedDateTimeNormalized, " +
223+
"e.offsetTimeNormalizedUtc, " +
224+
"e.offsetDateTimeNormalizedUtc, " +
225+
"e.zonedDateTimeNormalizedUtc " +
226+
"from TimeZoneStorageEntity e",
227+
Tuple.class
228+
).getSingleResult()
229+
.thenAccept( result -> {
230+
assertThat( result.get( 0, OffsetTime.class ).toLocalTime()).isEqualTo( OFFSET_TIME.withOffsetSameInstant( JVM_TIMEZONE_OFFSET ).toLocalTime() );
231+
assertThat( result.get( 0, OffsetTime.class ).getOffset()).isEqualTo( JVM_TIMEZONE_OFFSET );
232+
assertThat( result.get( 1, OffsetDateTime.class ).toInstant()).isEqualTo( OFFSET_DATE_TIME.toInstant() );
233+
assertThat( result.get( 2, ZonedDateTime.class ).toInstant()).isEqualTo( ZONED_DATE_TIME.toInstant() );
234+
assertThat( result.get( 3, OffsetTime.class ).toLocalTime()).isEqualTo( OFFSET_TIME.withOffsetSameInstant( ZoneOffset.UTC ).toLocalTime() );
235+
assertThat( result.get( 3, OffsetTime.class ).getOffset()).isEqualTo( ZoneOffset.UTC );
236+
assertThat( result.get( 4, OffsetDateTime.class ).toInstant()).isEqualTo( OFFSET_DATE_TIME.toInstant() );
237+
assertThat( result.get( 5, ZonedDateTime.class ).toInstant()).isEqualTo( ZONED_DATE_TIME.toInstant() );
238+
}
239+
)
240+
)
241+
);
242+
}
243+
244+
@Entity(name = "TimeZoneStorageEntity")
245+
@Table(name = "TimeZoneStorageEntity")
246+
public static class TimeZoneStorageEntity {
247+
@Id
248+
public Integer id;
249+
250+
//tag::time-zone-column-examples-mapping-example[]
251+
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
252+
@TimeZoneColumn(name = "birthtime_offset_offset")
253+
@Column(name = "birthtime_offset")
254+
public OffsetTime offsetTimeColumn;
255+
256+
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
257+
@TimeZoneColumn(name = "birthday_offset_offset")
258+
@Column(name = "birthday_offset")
259+
public OffsetDateTime offsetDateTimeColumn;
260+
261+
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
262+
@TimeZoneColumn(name = "birthday_zoned_offset")
263+
@Column(name = "birthday_zoned")
264+
public ZonedDateTime zonedDateTimeColumn;
265+
//end::time-zone-column-examples-mapping-example[]
266+
267+
@TimeZoneStorage
268+
@Column(name = "birthtime_offset_auto")
269+
public OffsetTime offsetTimeAuto;
270+
271+
@TimeZoneStorage
272+
@Column(name = "birthday_offset_auto")
273+
public OffsetDateTime offsetDateTimeAuto;
274+
275+
@TimeZoneStorage
276+
@Column(name = "birthday_zoned_auto")
277+
public ZonedDateTime zonedDateTimeAuto;
278+
279+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE)
280+
@Column(name = "birthtime_offset_normalized")
281+
public OffsetTime offsetTimeNormalized;
282+
283+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE)
284+
@Column(name = "birthday_offset_normalized")
285+
public OffsetDateTime offsetDateTimeNormalized;
286+
287+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE)
288+
@Column(name = "birthday_zoned_normalized")
289+
public ZonedDateTime zonedDateTimeNormalized;
290+
291+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
292+
@Column(name = "birthtime_offset_utc")
293+
public OffsetTime offsetTimeNormalizedUtc;
294+
295+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
296+
@Column(name = "birthday_offset_utc")
297+
public OffsetDateTime offsetDateTimeNormalizedUtc;
298+
299+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
300+
@Column(name = "birthday_zoned_utc")
301+
private ZonedDateTime zonedDateTimeNormalizedUtc;
302+
303+
public TimeZoneStorageEntity() {
304+
}
305+
306+
public TimeZoneStorageEntity(Integer id, OffsetTime offsetTime, OffsetDateTime offsetDateTime, ZonedDateTime zonedDateTime) {
307+
this.id = id;
308+
this.offsetTimeColumn = offsetTime;
309+
this.offsetDateTimeColumn = offsetDateTime;
310+
this.zonedDateTimeColumn = zonedDateTime;
311+
this.offsetTimeAuto = offsetTime;
312+
this.offsetDateTimeAuto = offsetDateTime;
313+
this.zonedDateTimeAuto = zonedDateTime;
314+
this.offsetTimeNormalized = offsetTime;
315+
this.offsetDateTimeNormalized = offsetDateTime;
316+
this.zonedDateTimeNormalized = zonedDateTime;
317+
this.offsetTimeNormalizedUtc = offsetTime;
318+
this.offsetDateTimeNormalizedUtc = offsetDateTime;
319+
this.zonedDateTimeNormalizedUtc = zonedDateTime;
320+
}
321+
}
322+
}

0 commit comments

Comments
 (0)