Skip to content

Commit 079f333

Browse files
committed
Use java.time for parsing/formatting of temporal types.
1 parent 832d8ef commit 079f333

File tree

5 files changed

+106
-109
lines changed

5 files changed

+106
-109
lines changed

src/main/java/com/github/pgasync/impl/conversion/TemporalConversions.java

+57-64
Original file line numberDiff line numberDiff line change
@@ -6,54 +6,51 @@
66
import java.sql.Date;
77
import java.sql.Time;
88
import java.sql.Timestamp;
9-
import java.text.DateFormat;
10-
import java.text.ParseException;
11-
import java.text.SimpleDateFormat;
12-
import java.util.TimeZone;
9+
import java.time.*;
10+
import java.time.format.DateTimeFormatter;
11+
import java.time.format.DateTimeFormatterBuilder;
12+
import java.time.format.DateTimeParseException;
1313

1414
import static java.nio.charset.StandardCharsets.UTF_8;
15+
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
16+
import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME;
1517

1618
/**
1719
* @author Antti Laisi
1820
*
19-
* TODO: Add support for Java 8 temporal types and use new parsers.
21+
* TODO: Add support for Java 8 temporal types.
2022
*/
2123
enum TemporalConversions {
2224
;
25+
static final DateTimeFormatter TIMESTAMP_FORMAT = new DateTimeFormatterBuilder()
26+
.parseCaseInsensitive()
27+
.append(ISO_LOCAL_DATE)
28+
.appendLiteral(' ')
29+
.append(ISO_LOCAL_TIME)
30+
.toFormatter();
2331

24-
static final ThreadLocal<DateFormat> DATE_FORMAT = new ThreadLocal<DateFormat>() {
25-
@Override
26-
protected DateFormat initialValue() {
27-
return zoned(new SimpleDateFormat("yyyy-MM-dd"));
28-
}
29-
};
30-
static final ThreadLocal<DateFormat> TIME_FORMAT = new ThreadLocal<DateFormat>() {
31-
@Override
32-
protected DateFormat initialValue() {
33-
return zoned(new SimpleDateFormat("HH:mm:ss.SSS"));
34-
}
35-
};
36-
static final ThreadLocal<DateFormat> TIME_FORMAT_NO_MILLIS = new ThreadLocal<DateFormat>() {
37-
@Override
38-
protected DateFormat initialValue() {
39-
return zoned(new SimpleDateFormat("HH:mm:ss"));
40-
}
41-
};
42-
static final ThreadLocal<DateFormat> TIMESTAMP_FORMAT = new ThreadLocal<DateFormat>() {
43-
@Override
44-
protected DateFormat initialValue() {
45-
return zoned(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"));
46-
}
47-
};
32+
static final DateTimeFormatter TIMESTAMPZ_FORMAT = new DateTimeFormatterBuilder()
33+
.parseCaseInsensitive()
34+
.append(ISO_LOCAL_DATE)
35+
.appendLiteral(' ')
36+
.append(ISO_LOCAL_TIME)
37+
.appendOffset("+HH:mm", "")
38+
.toFormatter();
39+
40+
static final DateTimeFormatter TIMEZ_FORMAT = new DateTimeFormatterBuilder()
41+
.parseCaseInsensitive()
42+
.append(ISO_LOCAL_TIME)
43+
.appendOffset("+HH:mm", "")
44+
.toFormatter();
4845

4946
static Date toDate(Oid oid, byte[] value) {
5047
switch (oid) {
5148
case UNSPECIFIED: // fallthrough
5249
case DATE:
5350
String date = new String(value, UTF_8);
5451
try {
55-
return new Date(DATE_FORMAT.get().parse(date).getTime());
56-
} catch (ParseException e) {
52+
return Date.valueOf(LocalDate.parse(date, ISO_LOCAL_DATE));
53+
} catch (DateTimeParseException e) {
5754
throw new SqlException("Invalid date: " + date);
5855
}
5956
default:
@@ -62,52 +59,48 @@ static Date toDate(Oid oid, byte[] value) {
6259
}
6360

6461
static Time toTime(Oid oid, byte[] value) {
65-
switch (oid) {
66-
case UNSPECIFIED: // fallthrough
67-
case TIMETZ: // fallthrough
68-
case TIME:
69-
String time = new String(value, UTF_8);
70-
try {
71-
DateFormat format = time.length() == 8 ? TIME_FORMAT_NO_MILLIS.get() : TIME_FORMAT.get();
72-
return new Time(format.parse(time).getTime());
73-
} catch (ParseException e) {
74-
throw new SqlException("Invalid time: " + time);
75-
}
76-
default:
77-
throw new SqlException("Unsupported conversion " + oid.name() + " -> Time");
62+
String time = new String(value, UTF_8);
63+
try {
64+
switch (oid) {
65+
case UNSPECIFIED: // fallthrough
66+
case TIME:
67+
return Time.valueOf(LocalTime.parse(time, ISO_LOCAL_TIME));
68+
case TIMETZ:
69+
return Time.valueOf(OffsetTime.parse(time, TIMEZ_FORMAT).toLocalTime());
70+
default:
71+
throw new SqlException("Unsupported conversion " + oid.name() + " -> Time");
72+
}
73+
} catch (DateTimeParseException e) {
74+
throw new SqlException("Invalid time: " + time);
7875
}
7976
}
8077

8178
static Timestamp toTimestamp(Oid oid, byte[] value) {
82-
switch (oid) {
83-
case UNSPECIFIED: // fallthrough
84-
case TIMESTAMP: // fallthrough
85-
case TIMESTAMPTZ:
86-
String time = new String(value, UTF_8);
87-
try {
88-
return new Timestamp(TIMESTAMP_FORMAT.get().parse(time).getTime());
89-
} catch (ParseException e) {
90-
throw new SqlException("Invalid time: " + time);
91-
}
92-
default:
93-
throw new SqlException("Unsupported conversion " + oid.name() + " -> Time");
79+
String time = new String(value, UTF_8);
80+
try {
81+
switch (oid) {
82+
case UNSPECIFIED: // fallthrough
83+
case TIMESTAMP:
84+
return Timestamp.valueOf(LocalDateTime.parse(time, TIMESTAMP_FORMAT));
85+
case TIMESTAMPTZ:
86+
return Timestamp.valueOf(OffsetDateTime.parse(time, TIMESTAMPZ_FORMAT).toLocalDateTime());
87+
default:
88+
throw new SqlException("Unsupported conversion " + oid.name() + " -> Time");
89+
}
90+
} catch (DateTimeParseException e) {
91+
throw new SqlException("Invalid time: " + time);
9492
}
9593
}
9694

9795
static byte[] fromTime(Time time) {
98-
return TIME_FORMAT.get().format(time).getBytes(UTF_8);
96+
return ISO_LOCAL_TIME.format(time.toLocalTime()).getBytes(UTF_8);
9997
}
10098

10199
static byte[] fromDate(Date date) {
102-
return DATE_FORMAT.get().format(date).getBytes(UTF_8);
100+
return ISO_LOCAL_DATE.format(date.toLocalDate()).getBytes(UTF_8);
103101
}
104102

105103
static byte[] fromTimestamp(Timestamp ts) {
106-
return TIMESTAMP_FORMAT.get().format(ts).getBytes(UTF_8);
107-
}
108-
109-
private static SimpleDateFormat zoned(SimpleDateFormat format) {
110-
format.setTimeZone(TimeZone.getTimeZone("UTC"));
111-
return format;
104+
return TIMESTAMP_FORMAT.format(ts.toLocalDateTime()).getBytes(UTF_8);
112105
}
113106
}

src/test/java/com/github/pgasync/impl/ArrayConversionsTest.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.math.BigDecimal;
77
import java.sql.Timestamp;
8+
import java.time.LocalDateTime;
89
import java.util.ArrayList;
910
import java.util.Collections;
1011
import java.util.List;
@@ -120,8 +121,8 @@ public void selectTimestamp() {
120121

121122
assertArrayEquals(
122123
new Timestamp[]{
123-
new Timestamp(926812800591L),
124-
new Timestamp(2941353001L),
124+
Timestamp.valueOf(LocalDateTime.parse("1999-05-16T00:00:00.591")),
125+
Timestamp.valueOf(LocalDateTime.parse("1970-02-04T01:02:33.01")),
125126
null
126127
},
127128
getRow().getArray("TIMESTAMPA", Timestamp[].class));

src/test/java/com/github/pgasync/impl/PreparedStatementTest.java

+14-15
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,22 @@
1414

1515
package com.github.pgasync.impl;
1616

17-
import com.github.pgasync.ResultSet;
1817
import org.junit.AfterClass;
1918
import org.junit.BeforeClass;
2019
import org.junit.ClassRule;
2120
import org.junit.Test;
2221

2322
import java.sql.Date;
2423
import java.sql.Time;
25-
import java.text.SimpleDateFormat;
26-
import java.util.Collections;
27-
import java.util.TimeZone;
24+
import java.sql.Timestamp;
25+
import java.time.LocalDate;
26+
import java.time.LocalDateTime;
27+
import java.time.LocalTime;
2828

2929
import static com.github.pgasync.impl.io.IO.bytes;
3030
import static java.util.Arrays.asList;
3131
import static java.util.Collections.singletonList;
32-
import static org.junit.Assert.assertArrayEquals;
33-
import static org.junit.Assert.assertEquals;
34-
import static org.junit.Assert.assertTrue;
32+
import static org.junit.Assert.*;
3533

3634
/**
3735
* Tests for parameter binding.
@@ -113,15 +111,21 @@ public void shouldBindClob() {
113111

114112
@Test
115113
public void shouldBindTime() throws Exception {
116-
Time time = new Time(dateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse("1970-01-01 16:47:59.897")
117-
.getTime());
114+
Time time = Time.valueOf(LocalTime.parse("16:47:59.897"));
118115
dbr.query("INSERT INTO PS_TEST(TIME) VALUES ($1)", asList(time));
119116
assertEquals(time, dbr.query("SELECT TIME FROM PS_TEST WHERE TIME = $1", asList(time)).row(0).getTime(0));
120117
}
121118

119+
@Test
120+
public void shouldBindTimestamp() throws Exception {
121+
Timestamp ts = Timestamp.valueOf(LocalDateTime.parse("2016-05-01T12:00:00"));
122+
dbr.query("INSERT INTO PS_TEST(TS) VALUES ($1)", singletonList(ts));
123+
assertEquals(ts, dbr.query("SELECT TS FROM PS_TEST WHERE TS = $1", asList(ts)).row(0).getTimestamp(0));
124+
}
125+
122126
@Test
123127
public void shouldBindDate() throws Exception {
124-
Date date = new Date(dateFormat("yyyy-MM-dd").parse("2014-01-19").getTime());
128+
Date date = Date.valueOf(LocalDate.parse("2014-01-19"));
125129
dbr.query("INSERT INTO PS_TEST(DATE) VALUES ($1)", asList(date));
126130
assertEquals(date, dbr.query("SELECT DATE FROM PS_TEST WHERE DATE = $1", asList(date)).row(0).getDate(0));
127131
}
@@ -141,9 +145,4 @@ public void shouldBindBoolean() throws Exception {
141145

142146
}
143147

144-
static SimpleDateFormat dateFormat(String pattern) {
145-
SimpleDateFormat format = new SimpleDateFormat(pattern);
146-
format.setTimeZone(TimeZone.getTimeZone("UTC"));
147-
return format;
148-
}
149148
}

src/test/java/com/github/pgasync/impl/TransactionTest.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
package com.github.pgasync.impl;
1616

17-
import com.github.pgasync.ConnectionPool;
1817
import com.github.pgasync.ResultSet;
1918
import org.junit.AfterClass;
2019
import org.junit.BeforeClass;
@@ -27,7 +26,6 @@
2726

2827
import static org.junit.Assert.assertEquals;
2928
import static org.junit.Assert.assertTrue;
30-
import static org.junit.Assert.fail;
3129

3230
/**
3331
* Tests for BEGIN/COMMIT/ROLLBACK.
@@ -37,7 +35,7 @@
3735
public class TransactionTest {
3836

3937
final Consumer<Throwable> err = Throwable::printStackTrace;
40-
final Consumer<ResultSet> fail = result -> { new AssertionError("Failure expected").printStackTrace(); };
38+
final Consumer<ResultSet> fail = result -> new AssertionError("Failure expected").printStackTrace();
4139

4240
@ClassRule
4341
public static DatabaseRule dbr = new DatabaseRule();

src/test/java/com/github/pgasync/impl/TypeConverterTest.java

+31-25
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@
2222
import java.sql.Date;
2323
import java.sql.Time;
2424
import java.sql.Timestamp;
25-
import java.util.Calendar;
26-
import java.util.TimeZone;
25+
import java.time.*;
2726
import java.util.UUID;
2827

2928
import static java.util.Arrays.asList;
@@ -158,32 +157,56 @@ public void shouldConvertNumericToDouble() {
158157

159158
@Test
160159
public void shouldConvertDateToDate() {
161-
assertEquals(new Date(millis(2014, 1, 31, 0, 0, 0, 0)),
160+
assertEquals(Date.valueOf(LocalDate.parse("2014-01-31")),
162161
dbr.query("select '2014-01-31'::DATE").row(0).getDate(0));
163162
}
164163

165164
@Test
166165
public void shouldConvertDateToDateWithName() {
167-
assertEquals(new Date(millis(2014, 2, 21, 0, 0, 0, 0)), dbr.query("select '2014-02-21'::DATE as D").row(0)
166+
assertEquals(Date.valueOf(LocalDate.parse("2014-02-21")), dbr.query("select '2014-02-21'::DATE as D").row(0)
168167
.getDate("D"));
169168
}
170169

171170
@Test
172171
public void shouldConvertTimeToTime() {
173-
assertEquals(new Time(millis(0, 0, 0, 10, 15, 31, 123)), dbr.query("select '10:15:31.123'::TIME").row(0)
172+
assertEquals(Time.valueOf(LocalTime.parse("10:15:31.123")), dbr.query("select '10:15:31.123'::TIME").row(0)
174173
.getTime(0));
175174
}
176175

177176
@Test
178177
public void shouldConvertZonedTimeToTime() {
179-
assertEquals(new Time(millis(0, 0, 0, 23, 59, 59, 999)), dbr.query("select '23:59:59.999Z'::TIMETZ as zoned")
178+
assertEquals(Time.valueOf(OffsetTime.parse("23:59:59.999Z").toLocalTime()), dbr.query("select '23:59:59.999Z'::TIMETZ as zoned")
180179
.row(0).getTime("zoned"));
181180
}
182181

183182
@Test
184183
public void shouldConvertTimestampToTimestamp() {
185-
assertEquals(new Timestamp(millis(2014, 2, 21, 23, 59, 59, 999)), dbr.query("select '2014-02-21 23:59:59.999'::TIMESTAMP as ts")
186-
.row(0).getTimestamp("ts"));
184+
assertEquals(Timestamp.valueOf(LocalDateTime.parse("2014-02-21T23:59:59.999")),
185+
dbr.query("select '2014-02-21 23:59:59.999'::TIMESTAMP as ts").row(0).getTimestamp("ts"));
186+
}
187+
188+
@Test
189+
public void shouldConvertTimestampWithShortMillisToTimestamp() {
190+
assertEquals(Timestamp.valueOf(LocalDateTime.parse("2014-02-21T23:59:59.990")),
191+
dbr.query("select '2014-02-21 23:59:59.99'::TIMESTAMP as ts").row(0).getTimestamp("ts"));
192+
}
193+
194+
@Test
195+
public void shouldConvertTimestampWithNoMillisToTimestamp() {
196+
assertEquals(Timestamp.valueOf(LocalDateTime.parse("2014-02-21T23:59:59")),
197+
dbr.query("select '2014-02-21 23:59:59'::TIMESTAMP as ts").row(0).getTimestamp("ts"));
198+
}
199+
200+
@Test
201+
public void shouldConvertZonedTimestampToTimestamp() {
202+
assertEquals(Timestamp.from(Instant.from(ZonedDateTime.parse("2014-02-21T23:59:59.999Z"))),
203+
dbr.query("select '2014-02-21 23:59:59.999Z'::TIMESTAMPTZ as ts").row(0).getTimestamp("ts"));
204+
}
205+
206+
@Test
207+
public void shouldConvertZonedTimestampWithNanosToTimestamp() {
208+
assertEquals(Timestamp.valueOf("2014-02-21 23:59:59.000999"),
209+
dbr.query("select '2014-02-21 23:59:59.000999'::TIMESTAMPTZ as ts").row(0).getTimestamp("ts"));
187210
}
188211

189212
@Test
@@ -212,21 +235,4 @@ public void shouldConvertUUID() {
212235
assertEquals(uuid, row.get("uuid"));
213236
}
214237

215-
static long millis(int year, int month, int day, int hour, int minute, int second, int millisecond) {
216-
Calendar cal = Calendar.getInstance();
217-
cal.clear();
218-
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
219-
if (year > 0) {
220-
cal.set(Calendar.YEAR, year);
221-
cal.set(Calendar.MONTH, month - 1);
222-
cal.set(Calendar.DAY_OF_MONTH, day);
223-
}
224-
if (hour > 0) {
225-
cal.set(Calendar.HOUR_OF_DAY, hour);
226-
cal.set(Calendar.MINUTE, minute);
227-
cal.set(Calendar.SECOND, second);
228-
cal.set(Calendar.MILLISECOND, millisecond);
229-
}
230-
return cal.getTimeInMillis();
231-
}
232238
}

0 commit comments

Comments
 (0)