Skip to content

Commit ace6d7c

Browse files
committed
Fix Nanosecond Precision Test
1 parent eced43f commit ace6d7c

10 files changed

Lines changed: 64 additions & 342 deletions

File tree

JSqlServerBulkInsert/src/main/java/de/bytefish/jsqlserverbulkinsert/converters/InstantConverter.java

Lines changed: 0 additions & 21 deletions
This file was deleted.

JSqlServerBulkInsert/src/main/java/de/bytefish/jsqlserverbulkinsert/converters/LocalDateTimeConverter.java

Lines changed: 0 additions & 26 deletions
This file was deleted.

JSqlServerBulkInsert/src/main/java/de/bytefish/jsqlserverbulkinsert/extensions/DateTimeExtensions.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import java.sql.Time;
1212
import java.sql.Timestamp;
1313
import java.sql.Types;
14+
import java.time.LocalDateTime;
15+
import java.time.ZoneOffset;
1416
import java.util.AbstractMap;
1517
import java.util.function.Function;
1618

@@ -44,12 +46,15 @@ public static <TEntity> void mapUTCNano(AbstractMapping<TEntity> mapping, String
4446
@Override
4547
public Object internalConvert(Long value) {
4648

47-
AbstractMap.SimpleImmutableEntry<Long, Integer> convertedDT = TimestampUtils.convertUtcNanoToEpochSecAndNano(value);
49+
// loses subsecond data:
50+
long seconds = value / 1000000000;
51+
int nanoseconds = (int) (value - (seconds * 1000000000));
4852

49-
Timestamp castedResult = new Timestamp(convertedDT.getKey() * 1000);
50-
castedResult.setNanos(convertedDT.getValue());
53+
// Round to 100 nanoseconds (precision that SQL server can handle):
54+
nanoseconds = (nanoseconds / 100) * 100;
5155

52-
return castedResult;
56+
// Must include this adjustment to counteract the timezone adjustment that the SQL Server JDBC driver makes:
57+
return LocalDateTime.ofEpochSecond(seconds, nanoseconds, ZoneOffset.UTC);
5358
}
5459
});
5560
}

JSqlServerBulkInsert/src/main/java/de/bytefish/jsqlserverbulkinsert/mapping/AbstractMapping.java

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,17 @@ protected <TProperty> void mapNull(String columnName, Function<TEntity, TProper
9191

9292
protected void mapTinyInt(String columnName, Function<TEntity, Byte> propertyGetter)
9393
{
94-
mapProperty(columnName, Types.TINYINT, propertyGetter, new IdentityConverter());
94+
mapProperty(columnName, Types.TINYINT, propertyGetter, new IdentityConverter<>());
9595
}
9696

9797
protected void mapSmallInt(String columnName, Function<TEntity, Short> propertyGetter)
9898
{
99-
mapProperty(columnName, Types.SMALLINT, propertyGetter, new IdentityConverter());
99+
mapProperty(columnName, Types.SMALLINT, propertyGetter, new IdentityConverter<>());
100100
}
101101

102102
protected void mapSmallInt(String columnName, boolean isAutoIncrement)
103103
{
104-
mapProperty(columnName, Types.SMALLINT, 0, 0, isAutoIncrement, (entity) -> null, new IdentityConverter());
104+
mapProperty(columnName, Types.SMALLINT, 0, 0, isAutoIncrement, (entity) -> null, new IdentityConverter<>());
105105
}
106106

107107
protected void mapInteger(String columnName, Function<TEntity, Integer> propertyGetter)
@@ -111,24 +111,24 @@ protected void mapInteger(String columnName, Function<TEntity, Integer> property
111111

112112
protected void mapInteger(String columnName, boolean isAutoIncrement)
113113
{
114-
mapProperty(columnName, Types.INTEGER, 0, 0, isAutoIncrement, (entity) -> null, new IdentityConverter());
114+
mapProperty(columnName, Types.INTEGER, 0, 0, isAutoIncrement, (entity) -> null, new IdentityConverter<>());
115115
}
116116

117117
protected void mapIntegerPrimitive(String columnName, ToIntFunction<TEntity> propertyGetter)
118118
{
119-
mapProperty(columnName, Types.INTEGER, (entity) -> propertyGetter.applyAsInt(entity), new IdentityConverter());
119+
mapProperty(columnName, Types.INTEGER, propertyGetter::applyAsInt, new IdentityConverter<>());
120120
}
121121

122122
protected void mapLong(String columnName, Function<TEntity, Long> propertyGetter) {
123-
mapProperty(columnName, Types.BIGINT, propertyGetter, new IdentityConverter());
123+
mapProperty(columnName, Types.BIGINT, propertyGetter, new IdentityConverter<>());
124124
}
125125

126126
protected void mapLong(String columnName, boolean isAutoIncrement) {
127127
mapProperty(columnName, Types.BIGINT, 0, 0, isAutoIncrement, (entity) -> null, new IdentityConverter());
128128
}
129129

130130
protected void mapLongPrimitive(String columnName, ToLongFunction<TEntity> propertyGetter) {
131-
mapProperty(columnName, Types.BIGINT, (entity) -> propertyGetter.applyAsLong(entity), new IdentityConverter());
131+
mapProperty(columnName, Types.BIGINT, (entity) -> propertyGetter.applyAsLong(entity), new IdentityConverter<>());
132132
}
133133

134134
protected void mapNumeric(String columnName, int precision, int scale, Function<TEntity, BigDecimal> propertyGetter) {
@@ -143,60 +143,55 @@ protected void mapDecimal(String columnName, int precision, int scale, Function<
143143
}
144144

145145
protected void mapReal(String columnName, Function<TEntity, Float> propertyGetter) {
146-
mapProperty(columnName, Types.REAL, propertyGetter, new IdentityConverter());
146+
mapProperty(columnName, Types.REAL, propertyGetter, new IdentityConverter<>());
147147
}
148148

149149
protected void mapRealPrimitive(String columnName, ToFloatFunction<TEntity> propertyGetter) {
150-
mapProperty(columnName, Types.REAL, (entity) -> propertyGetter.applyAsFloat(entity), new IdentityConverter());
150+
mapProperty(columnName, Types.REAL, propertyGetter::applyAsFloat, new IdentityConverter<>());
151151
}
152152

153153
protected void mapBigInt(String columnName, Function<TEntity, BigInteger> propertyGetter) {
154154
mapProperty(columnName, Types.BIGINT, propertyGetter, new BigIntegerConverter());
155155
}
156156

157157
protected void mapDouble(String columnName, Function<TEntity, Double> propertyGetter) {
158-
mapProperty(columnName, Types.DOUBLE, propertyGetter, new IdentityConverter());
158+
mapProperty(columnName, Types.DOUBLE, propertyGetter, new IdentityConverter<>());
159159
}
160160

161161
protected void mapDoublePrimitive(String columnName, ToDoubleFunction<TEntity> propertyGetter) {
162-
mapProperty(columnName, Types.DOUBLE, (entity) -> propertyGetter.applyAsDouble(entity), new IdentityConverter());
162+
mapProperty(columnName, Types.DOUBLE, propertyGetter::applyAsDouble, new IdentityConverter<>());
163163
}
164164

165165
// endregion
166166

167167
// region Time Functions
168168

169169
protected void mapDate(String columnName, Function<TEntity, LocalDate> propertyGetter) {
170-
mapProperty(columnName, Types.DATE, propertyGetter, new IdentityConverter());
171-
}
172-
173-
protected void mapInstant(String columnName, Function<TEntity, Instant> propertyGetter) {
174-
175-
mapProperty(columnName, Types.TIMESTAMP, propertyGetter, new InstantConverter());
170+
mapProperty(columnName, Types.DATE, propertyGetter, new IdentityConverter<>());
176171
}
177172

178173
protected void mapDateTime(String columnName, Function<TEntity, Timestamp> propertyGetter) {
179-
mapProperty(columnName, Types.TIMESTAMP, propertyGetter, new IdentityConverter());
174+
mapProperty(columnName, Types.TIMESTAMP, propertyGetter, new IdentityConverter<>());
180175
}
181176

182177
protected void mapLocalDateTime(String columnName, Function<TEntity, LocalDateTime> propertyGetter) {
183-
mapProperty(columnName, Types.TIMESTAMP, propertyGetter, new LocalDateTimeConverter());
178+
mapProperty(columnName, Types.TIMESTAMP, propertyGetter, new IdentityConverter<>());
184179
}
185180

186181
protected void mapTimeWithTimeZone(String columnName, Function<TEntity, OffsetTime> propertyGetter) {
187-
mapProperty(columnName, SqlServerTypes.TimeWithTimeZone, propertyGetter, new IdentityConverter());
182+
mapProperty(columnName, SqlServerTypes.TimeWithTimeZone, propertyGetter, new IdentityConverter<>());
188183
}
189184

190185
protected void mapDateTimeWithTimeZone(String columnName, Function<TEntity, OffsetDateTime> propertyGetter) {
191-
mapProperty(columnName, SqlServerTypes.DateTimeWithTimeZone, propertyGetter, new IdentityConverter());
186+
mapProperty(columnName, SqlServerTypes.DateTimeWithTimeZone, propertyGetter, new IdentityConverter<>());
192187
}
193188

194189
// endregion
195190

196191
// region Binary
197192

198193
protected void mapVarBinary(String columnName, int maxLength, Function<TEntity, byte[]> propertyGetter) {
199-
mapProperty(columnName, Types.VARBINARY, maxLength, 0, false, propertyGetter, new IdentityConverter());
194+
mapProperty(columnName, Types.VARBINARY, maxLength, 0, false, propertyGetter, new IdentityConverter<>());
200195
}
201196

202197
// endregion

JSqlServerBulkInsert/src/test/java/de/bytefish/jsqlserverbulkinsert/test/mapping/InstantTest.java

Lines changed: 0 additions & 115 deletions
This file was deleted.

JSqlServerBulkInsert/src/test/java/de/bytefish/jsqlserverbulkinsert/test/mapping/LocalDateMappingTest.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616
import java.sql.SQLException;
1717
import java.sql.Statement;
1818
import java.time.LocalDate;
19+
import java.time.LocalDateTime;
20+
import java.time.ZoneOffset;
1921
import java.util.Arrays;
22+
import java.util.Calendar;
2023
import java.util.List;
24+
import java.util.TimeZone;
2125

2226
public class LocalDateMappingTest extends TransactionalTestBase {
2327

@@ -65,17 +69,17 @@ public void bulkInsertPersonDataTest() throws SQLException {
6569
while (rs.next()) {
6670

6771
// Get the Date we have written:
68-
Date date = rs.getDate("LocalDate");
72+
LocalDate date = rs.getTimestamp("LocalDate", Calendar.getInstance(TimeZone.getTimeZone("UTC")))
73+
.toInstant()
74+
.atZone(ZoneOffset.UTC)
75+
.toLocalDate();
6976

7077
// We should have a date:
7178
Assert.assertNotNull(date);
7279

73-
// Get the LocalDate:
74-
LocalDate resultDate = date.toLocalDate();
75-
76-
Assert.assertEquals(localDate.getYear(), resultDate.getYear());
77-
Assert.assertEquals(localDate.getMonthValue(), resultDate.getMonthValue());
78-
Assert.assertEquals(localDate.getDayOfMonth(), resultDate.getDayOfMonth());
80+
Assert.assertEquals(localDate.getYear(), date.getYear());
81+
Assert.assertEquals(localDate.getMonthValue(), date.getMonthValue());
82+
Assert.assertEquals(localDate.getDayOfMonth(), date.getDayOfMonth());
7983
}
8084
}
8185

0 commit comments

Comments
 (0)