Skip to content

Commit fc7b1ba

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

File tree

1 file changed

+299
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)