Skip to content

Commit 525b9c5

Browse files
committed
Added edge case tests provided by @kdubb
`java.sql.Date` and `java.sql.Timestamp` cannot hold a value that does not exist in the system's time zone and alter it implicitly. e.g. - In Pacific/Apia, `2011-12-30` becomes `2011-12-31` - In America/Los_Angeles, `2019-03-10 02:30` becomes `2019-03-10 03:30`
1 parent 87b8ebe commit 525b9c5

File tree

5 files changed

+273
-0
lines changed

5 files changed

+273
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--
2+
-- Copyright 2009-2019 the original author or authors.
3+
--
4+
-- Licensed under the Apache License, Version 2.0 (the "License");
5+
-- you may not use this file except in compliance with the License.
6+
-- You may obtain a copy of the License at
7+
--
8+
-- http://www.apache.org/licenses/LICENSE-2.0
9+
--
10+
-- Unless required by applicable law or agreed to in writing, software
11+
-- distributed under the License is distributed on an "AS IS" BASIS,
12+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
-- See the License for the specific language governing permissions and
14+
-- limitations under the License.
15+
--
16+
17+
drop table records if exists;
18+
19+
create table records (
20+
id int,
21+
ts timestamp(9),
22+
d date
23+
);
24+
25+
insert into records (id, ts, d) values
26+
(1, '2019-03-10 02:30:00', '2011-12-30');
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright 2009-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.timezone_edge_case;
17+
18+
import org.apache.ibatis.annotations.Insert;
19+
import org.apache.ibatis.annotations.Select;
20+
21+
public interface Mapper {
22+
23+
@Select("select id, ts, d from records where id = #{id}")
24+
Record selectById(Integer id);
25+
26+
@Insert("insert into records (id, ts, d) values (#{id}, #{ts}, #{d})")
27+
int insert(Record record);
28+
29+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright 2009-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.timezone_edge_case;
17+
18+
import java.time.LocalDate;
19+
import java.time.LocalDateTime;
20+
21+
public class Record {
22+
23+
private Integer id;
24+
25+
private LocalDateTime ts;
26+
private LocalDate d;
27+
28+
public Integer getId() {
29+
return id;
30+
}
31+
32+
public void setId(Integer id) {
33+
this.id = id;
34+
}
35+
36+
public LocalDateTime getTs() {
37+
return ts;
38+
}
39+
40+
public void setTs(LocalDateTime ts) {
41+
this.ts = ts;
42+
}
43+
44+
public LocalDate getD() {
45+
return d;
46+
}
47+
48+
public void setD(LocalDate d) {
49+
this.d = d;
50+
}
51+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/**
2+
* Copyright 2009-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.ibatis.submitted.timezone_edge_case;
17+
18+
import static org.junit.jupiter.api.Assertions.*;
19+
20+
import java.io.Reader;
21+
import java.sql.Connection;
22+
import java.sql.ResultSet;
23+
import java.sql.Statement;
24+
import java.time.LocalDate;
25+
import java.time.LocalDateTime;
26+
import java.time.LocalTime;
27+
import java.util.TimeZone;
28+
29+
import org.apache.ibatis.BaseDataTest;
30+
import org.apache.ibatis.io.Resources;
31+
import org.apache.ibatis.session.SqlSession;
32+
import org.apache.ibatis.session.SqlSessionFactory;
33+
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
34+
import org.junit.jupiter.api.AfterEach;
35+
import org.junit.jupiter.api.BeforeAll;
36+
import org.junit.jupiter.api.BeforeEach;
37+
import org.junit.jupiter.api.Test;
38+
39+
public class TimezoneEdgeCaseTest {
40+
41+
private static SqlSessionFactory sqlSessionFactory;
42+
private TimeZone timeZone;
43+
44+
@BeforeAll
45+
static void setUp() throws Exception {
46+
try (Reader reader = Resources
47+
.getResourceAsReader("org/apache/ibatis/submitted/timezone_edge_case/mybatis-config.xml")) {
48+
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
49+
}
50+
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
51+
"org/apache/ibatis/submitted/timezone_edge_case/CreateDB.sql");
52+
}
53+
54+
@BeforeEach
55+
void saveTimeZone() {
56+
timeZone = TimeZone.getDefault();
57+
}
58+
59+
@AfterEach
60+
void restoreTimeZone() {
61+
TimeZone.setDefault(timeZone);
62+
}
63+
64+
@Test
65+
void shouldSelectNonExistentLocalTimestampAsIs() {
66+
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
67+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
68+
Mapper mapper = sqlSession.getMapper(Mapper.class);
69+
Record record = mapper.selectById(1);
70+
assertEquals(LocalDateTime.of(LocalDate.of(2019, 3, 10), LocalTime.of(2, 30)), record.getTs());
71+
}
72+
}
73+
74+
@Test
75+
void shouldInsertNonExistentLocalTimestampAsIs() throws Exception {
76+
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
77+
LocalDateTime localDateTime = LocalDateTime.of(LocalDate.of(2019, 3, 10), LocalTime.of(2, 30));
78+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
79+
Mapper mapper = sqlSession.getMapper(Mapper.class);
80+
Record record = new Record();
81+
record.setId(2);
82+
record.setTs(localDateTime);
83+
mapper.insert(record);
84+
sqlSession.commit();
85+
}
86+
try (SqlSession sqlSession = sqlSessionFactory.openSession();
87+
Connection con = sqlSession.getConnection();
88+
Statement stmt = con.createStatement();
89+
ResultSet rs = stmt.executeQuery("select count(*) from records where id = 2 and ts = '2019-03-10 02:30:00'")) {
90+
rs.next();
91+
assertEquals(1, rs.getInt(1));
92+
}
93+
}
94+
95+
@Test
96+
void shouldSelectNonExistentLocalDateAsIs() {
97+
TimeZone.setDefault(TimeZone.getTimeZone("Pacific/Apia"));
98+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
99+
Mapper mapper = sqlSession.getMapper(Mapper.class);
100+
Record record = mapper.selectById(1);
101+
assertEquals(LocalDate.of(2011, 12, 30), record.getD());
102+
}
103+
}
104+
105+
@Test
106+
void shouldInsertNonExistentLocalDateAsIs() throws Exception {
107+
TimeZone.setDefault(TimeZone.getTimeZone("Pacific/Apia"));
108+
LocalDate localDate = LocalDate.of(2011, 12, 30);
109+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
110+
Mapper mapper = sqlSession.getMapper(Mapper.class);
111+
Record record = new Record();
112+
record.setId(3);
113+
record.setD(localDate);
114+
mapper.insert(record);
115+
sqlSession.commit();
116+
}
117+
try (SqlSession sqlSession = sqlSessionFactory.openSession();
118+
Connection con = sqlSession.getConnection();
119+
Statement stmt = con.createStatement();
120+
ResultSet rs = stmt.executeQuery("select count(*) from records where id = 3 and d = '2011-12-30'")) {
121+
rs.next();
122+
assertEquals(1, rs.getInt(1));
123+
}
124+
}
125+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<!--
3+
4+
Copyright 2009-2019 the original author or authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
-->
19+
<!DOCTYPE configuration
20+
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
21+
"http://mybatis.org/dtd/mybatis-3-config.dtd">
22+
23+
<configuration>
24+
25+
<environments default="development">
26+
<environment id="development">
27+
<transactionManager type="JDBC">
28+
<property name="" value="" />
29+
</transactionManager>
30+
<dataSource type="UNPOOLED">
31+
<property name="driver" value="org.hsqldb.jdbcDriver" />
32+
<property name="url" value="jdbc:hsqldb:mem:tzedge" />
33+
<property name="username" value="sa" />
34+
</dataSource>
35+
</environment>
36+
</environments>
37+
38+
<mappers>
39+
<mapper class="org.apache.ibatis.submitted.timezone_edge_case.Mapper" />
40+
</mappers>
41+
42+
</configuration>

0 commit comments

Comments
 (0)