Skip to content

Commit 8a8afe0

Browse files
committed
[#1442] Test TimeZoneStorage annotation
1 parent 6d925fb commit 8a8afe0

File tree

1 file changed

+308
-0
lines changed

1 file changed

+308
-0
lines changed
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
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+
25+
import org.junit.jupiter.api.BeforeEach;
26+
import org.junit.jupiter.api.Disabled;
27+
import org.junit.jupiter.api.Test;
28+
29+
import io.vertx.junit5.VertxTestContext;
30+
import jakarta.persistence.Column;
31+
import jakarta.persistence.Entity;
32+
import jakarta.persistence.Id;
33+
import jakarta.persistence.Table;
34+
import jakarta.persistence.Tuple;
35+
36+
import static org.assertj.core.api.Assertions.assertThat;
37+
import static org.hibernate.cfg.AvailableSettings.TIMEZONE_DEFAULT_STORAGE;
38+
39+
/**
40+
* Adapted from org.hibernate.orm.test.mapping.basic.TimeZoneStorageMappingTests
41+
*/
42+
public class TimeZoneStorageMappingTests extends BaseReactiveTest {
43+
44+
private static final ZoneOffset JVM_TIMEZONE_OFFSET = OffsetDateTime.now().getOffset();
45+
private static final OffsetTime OFFSET_TIME = OffsetTime.of(
46+
LocalTime.of(
47+
12,
48+
0,
49+
0
50+
),
51+
ZoneOffset.ofHoursMinutes( 5, 45 )
52+
);
53+
private static final OffsetDateTime OFFSET_DATE_TIME = OffsetDateTime.of(
54+
LocalDateTime.of(
55+
2022,
56+
3,
57+
1,
58+
12,
59+
0,
60+
0
61+
),
62+
ZoneOffset.ofHoursMinutes( 5, 45 )
63+
);
64+
private static final ZonedDateTime ZONED_DATE_TIME = ZonedDateTime.of(
65+
LocalDateTime.of(
66+
2022,
67+
3,
68+
1,
69+
12,
70+
0,
71+
0
72+
),
73+
ZoneOffset.ofHoursMinutes( 5, 45 )
74+
);
75+
private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern( "HH:mm:ssxxx" );
76+
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern( "dd/MM/yyyy 'at' HH:mm:ssxxx" );
77+
78+
79+
@Override
80+
protected Collection<Class<?>> annotatedEntities() {
81+
return List.of( TimeZoneStorageEntity.class );
82+
}
83+
84+
@Override
85+
protected void setProperties(Configuration configuration) {
86+
super.setProperties( configuration );
87+
configuration.setProperty( TIMEZONE_DEFAULT_STORAGE, "AUTO" );
88+
}
89+
90+
@BeforeEach
91+
public void populateDb(VertxTestContext context) {
92+
TimeZoneStorageEntity entity = new TimeZoneStorageEntity( 1, OFFSET_TIME, OFFSET_DATE_TIME, ZONED_DATE_TIME );
93+
94+
test( context, getMutinySessionFactory().withTransaction( (s, t) -> s.persist( entity ) ) );
95+
}
96+
97+
@Test
98+
public void testOffsetRetainedAuto(VertxTestContext context) {
99+
testOffsetRetained( context, "Auto" );
100+
}
101+
102+
@Test
103+
public void testOffsetRetainedColumn(VertxTestContext context) {
104+
testOffsetRetained( context, "Column" );
105+
}
106+
107+
@Test
108+
public void testOffsetRetainedFormatAuto(VertxTestContext context) {
109+
testOffsetRetainedFormat( context, "Auto" );
110+
}
111+
112+
@Test
113+
public void testOffsetRetainedFormatColumn(VertxTestContext context) {
114+
testOffsetRetainedFormat( context, "Column" );
115+
}
116+
117+
public void testOffsetRetained(VertxTestContext context, String suffix) {
118+
test( context, openSession()
119+
.thenCompose( session -> session.createQuery(
120+
"select " +
121+
"e.offsetTime" + suffix + ", " +
122+
"e.offsetDateTime" + suffix + ", " +
123+
"e.zonedDateTime" + suffix + ", " +
124+
"extract(offset from e.offsetTime" + suffix + "), " +
125+
"extract(offset from e.offsetDateTime" + suffix + "), " +
126+
"extract(offset from e.zonedDateTime" + suffix + "), " +
127+
"e.offsetTime" + suffix + " + 1 hour, " +
128+
"e.offsetDateTime" + suffix + " + 1 hour, " +
129+
"e.zonedDateTime" + suffix + " + 1 hour, " +
130+
"e.offsetTime" + suffix + " + 1 hour - e.offsetTime" + suffix + ", " +
131+
"e.offsetDateTime" + suffix + " + 1 hour - e.offsetDateTime" + suffix + ", " +
132+
"e.zonedDateTime" + suffix + " + 1 hour - e.zonedDateTime" + suffix + ", " +
133+
"1 from TimeZoneStorageEntity e " +
134+
"where e.offsetDateTime" + suffix + " = e.offsetDateTime" + suffix,
135+
Tuple.class
136+
).getResultList()
137+
.thenAccept( resultList -> {
138+
assertThat( resultList.get( 0 ).get( 0, OffsetTime.class ) ).isEqualTo( OFFSET_TIME );
139+
assertThat( resultList.get( 0 ).get( 1, OffsetDateTime.class ) ).isEqualTo( OFFSET_DATE_TIME );
140+
assertThat( resultList.get( 0 ).get( 2, ZonedDateTime.class ) ).isEqualTo( ZONED_DATE_TIME );
141+
assertThat( resultList.get( 0 ).get( 3, ZoneOffset.class ) ).isEqualTo( OFFSET_TIME.getOffset() );
142+
assertThat( resultList.get( 0 ).get( 4, ZoneOffset.class ) ).isEqualTo( OFFSET_DATE_TIME.getOffset() );
143+
assertThat( resultList.get( 0 ).get( 5, ZoneOffset.class ) ).isEqualTo( ZONED_DATE_TIME.getOffset() );
144+
assertThat( resultList.get( 0 ).get( 6, OffsetTime.class ) ).isEqualTo( OFFSET_TIME.plusHours( 1L ) );
145+
assertThat( resultList.get( 0 ).get( 7, OffsetDateTime.class ) ).isEqualTo( OFFSET_DATE_TIME.plusHours( 1L ) );
146+
assertThat( resultList.get( 0 ).get( 8, ZonedDateTime.class ) ).isEqualTo( ZONED_DATE_TIME.plusHours( 1L ) );
147+
assertThat( resultList.get( 0 ).get( 9, Duration.class ) ).isEqualTo( Duration.ofHours( 1L ) );
148+
assertThat( resultList.get( 0 ).get( 10, Duration.class ) ).isEqualTo( Duration.ofHours( 1L ) );
149+
assertThat( resultList.get( 0 ).get( 11, Duration.class ) ).isEqualTo( Duration.ofHours( 1L ) );
150+
} )
151+
)
152+
);
153+
}
154+
155+
public void testOffsetRetainedFormat(VertxTestContext context, String suffix) {
156+
test( context, openSession()
157+
.thenCompose( session -> session.createQuery(
158+
"select " +
159+
"format(e.offsetTime" + suffix + " as 'HH:mm:ssxxx'), " +
160+
"format(e.offsetDateTime" + suffix + " as 'dd/MM/yyyy ''at'' HH:mm:ssxxx'), " +
161+
"format(e.zonedDateTime" + suffix + " as 'dd/MM/yyyy ''at'' HH:mm:ssxxx'), " +
162+
"1 from TimeZoneStorageEntity e " +
163+
"where e.offsetDateTime" + suffix + " = e.offsetDateTime" + suffix,
164+
Tuple.class
165+
).getResultList()
166+
.thenAccept( resultList -> {
167+
assertThat( resultList.get( 0 ).get( 0, String.class ) ).isEqualTo( TIME_FORMATTER.format( OFFSET_TIME ) );
168+
assertThat( resultList.get( 0 ).get( 1, String.class ) ).isEqualTo( FORMATTER.format( OFFSET_DATE_TIME ) );
169+
assertThat( resultList.get( 0 ).get( 2, String.class ) ).isEqualTo( FORMATTER.format( ZONED_DATE_TIME ) );
170+
} )
171+
)
172+
);
173+
}
174+
175+
@Test
176+
public void testNormalize(VertxTestContext context) {
177+
test( context, openSession()
178+
.thenCompose( session -> session.createQuery(
179+
"select " +
180+
"e.offsetTimeNormalized, " +
181+
"e.offsetDateTimeNormalized, " +
182+
"e.zonedDateTimeNormalized, " +
183+
"e.offsetTimeNormalizedUtc, " +
184+
"e.offsetDateTimeNormalizedUtc, " +
185+
"e.zonedDateTimeNormalizedUtc " +
186+
"from TimeZoneStorageEntity e",
187+
Tuple.class
188+
).getResultList()
189+
.thenAccept( resultList -> {
190+
assertThat( resultList.get( 0 ).get( 0, OffsetTime.class ).toLocalTime()).isEqualTo( OFFSET_TIME.withOffsetSameInstant( JVM_TIMEZONE_OFFSET ).toLocalTime() );
191+
assertThat( resultList.get( 0 ).get( 0, OffsetTime.class ).getOffset()).isEqualTo( JVM_TIMEZONE_OFFSET );
192+
assertThat( resultList.get( 0 ).get( 1, OffsetDateTime.class ).toInstant()).isEqualTo( OFFSET_DATE_TIME.toInstant() );
193+
assertThat( resultList.get( 0 ).get( 2, ZonedDateTime.class ).toInstant()).isEqualTo( ZONED_DATE_TIME.toInstant() );
194+
assertThat( resultList.get( 0 ).get( 3, OffsetTime.class ).toLocalTime()).isEqualTo( OFFSET_TIME.withOffsetSameInstant( ZoneOffset.UTC ).toLocalTime() );
195+
assertThat( resultList.get( 0 ).get( 3, OffsetTime.class ).getOffset()).isEqualTo( ZoneOffset.UTC );
196+
assertThat( resultList.get( 0 ).get( 4, OffsetDateTime.class ).toInstant()).isEqualTo( OFFSET_DATE_TIME.toInstant() );
197+
assertThat( resultList.get( 0 ).get( 5, ZonedDateTime.class ).toInstant()).isEqualTo( ZONED_DATE_TIME.toInstant() );
198+
}
199+
)
200+
)
201+
);
202+
}
203+
204+
// This converted test fails during the getResultList()
205+
// Unsure how to enable the "@RequiresDialectFeature(....)" annotation which is an ORM JUnit construct
206+
@Disabled
207+
@Test
208+
// @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsFormat.class)
209+
// @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsTimezoneTypes.class, comment = "Extracting the offset usually only makes sense if the temporal retains the offset. On DBs that have native TZ support we test this anyway to make sure it's not broken'")
210+
public void testNormalizeOffset(VertxTestContext context) {
211+
test( context, openSession()
212+
.thenCompose( session -> session.createQuery(
213+
"select " +
214+
"extract(offset from e.offsetTimeNormalizedUtc), " +
215+
"extract(offset from e.offsetDateTimeNormalizedUtc), " +
216+
"extract(offset from e.zonedDateTimeNormalizedUtc) " +
217+
"from TimeZoneStorageEntity e",
218+
Tuple.class
219+
).getResultList()
220+
.thenAccept( resultList -> {
221+
assertThat( resultList.get( 0 ).get( 0, ZoneOffset.class ) ).isEqualTo( ZoneOffset.UTC );
222+
assertThat( resultList.get( 0 ).get( 1, ZoneOffset.class ) ).isEqualTo( ZoneOffset.UTC );
223+
assertThat( resultList.get( 0 ).get( 2, ZoneOffset.class ) ).isEqualTo( ZoneOffset.UTC );
224+
}
225+
)
226+
)
227+
);
228+
}
229+
230+
@Entity(name = "TimeZoneStorageEntity")
231+
@Table(name = "TimeZoneStorageEntity")
232+
public static class TimeZoneStorageEntity {
233+
@Id
234+
public Integer id;
235+
236+
//tag::time-zone-column-examples-mapping-example[]
237+
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
238+
@TimeZoneColumn(name = "birthtime_offset_offset")
239+
@Column(name = "birthtime_offset")
240+
public OffsetTime offsetTimeColumn;
241+
242+
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
243+
@TimeZoneColumn(name = "birthday_offset_offset")
244+
@Column(name = "birthday_offset")
245+
public OffsetDateTime offsetDateTimeColumn;
246+
247+
@TimeZoneStorage(TimeZoneStorageType.COLUMN)
248+
@TimeZoneColumn(name = "birthday_zoned_offset")
249+
@Column(name = "birthday_zoned")
250+
public ZonedDateTime zonedDateTimeColumn;
251+
//end::time-zone-column-examples-mapping-example[]
252+
253+
@TimeZoneStorage
254+
@Column(name = "birthtime_offset_auto")
255+
public OffsetTime offsetTimeAuto;
256+
257+
@TimeZoneStorage
258+
@Column(name = "birthday_offset_auto")
259+
public OffsetDateTime offsetDateTimeAuto;
260+
261+
@TimeZoneStorage
262+
@Column(name = "birthday_zoned_auto")
263+
public ZonedDateTime zonedDateTimeAuto;
264+
265+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE)
266+
@Column(name = "birthtime_offset_normalized")
267+
public OffsetTime offsetTimeNormalized;
268+
269+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE)
270+
@Column(name = "birthday_offset_normalized")
271+
public OffsetDateTime offsetDateTimeNormalized;
272+
273+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE)
274+
@Column(name = "birthday_zoned_normalized")
275+
public ZonedDateTime zonedDateTimeNormalized;
276+
277+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
278+
@Column(name = "birthtime_offset_utc")
279+
public OffsetTime offsetTimeNormalizedUtc;
280+
281+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
282+
@Column(name = "birthday_offset_utc")
283+
public OffsetDateTime offsetDateTimeNormalizedUtc;
284+
285+
@TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
286+
@Column(name = "birthday_zoned_utc")
287+
private ZonedDateTime zonedDateTimeNormalizedUtc;
288+
289+
public TimeZoneStorageEntity() {
290+
}
291+
292+
public TimeZoneStorageEntity(Integer id, OffsetTime offsetTime, OffsetDateTime offsetDateTime, ZonedDateTime zonedDateTime) {
293+
this.id = id;
294+
this.offsetTimeColumn = offsetTime;
295+
this.offsetDateTimeColumn = offsetDateTime;
296+
this.zonedDateTimeColumn = zonedDateTime;
297+
this.offsetTimeAuto = offsetTime;
298+
this.offsetDateTimeAuto = offsetDateTime;
299+
this.zonedDateTimeAuto = zonedDateTime;
300+
this.offsetTimeNormalized = offsetTime;
301+
this.offsetDateTimeNormalized = offsetDateTime;
302+
this.zonedDateTimeNormalized = zonedDateTime;
303+
this.offsetTimeNormalizedUtc = offsetTime;
304+
this.offsetDateTimeNormalizedUtc = offsetDateTime;
305+
this.zonedDateTimeNormalizedUtc = zonedDateTime;
306+
}
307+
}
308+
}

0 commit comments

Comments
 (0)