diff --git a/src/main/java/org/kiwiproject/jdbc/KiwiJdbc.java b/src/main/java/org/kiwiproject/jdbc/KiwiJdbc.java index 36e35c31..f947e0af 100644 --- a/src/main/java/org/kiwiproject/jdbc/KiwiJdbc.java +++ b/src/main/java/org/kiwiproject/jdbc/KiwiJdbc.java @@ -4,6 +4,7 @@ import static org.kiwiproject.base.KiwiPreconditions.checkArgumentNotNull; import lombok.experimental.UtilityClass; +import org.checkerframework.checker.nullness.qual.Nullable; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -11,6 +12,7 @@ import java.sql.Timestamp; import java.sql.Types; import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; @@ -118,6 +120,30 @@ public static LocalDateTime localDateTimeFromTimestamp(Timestamp timestamp) { return isNull(timestamp) ? null : timestamp.toLocalDateTime(); } + /** + * Returns a {@link LocalDate} from the specified {@link java.sql.Date} column in the given {@link ResultSet}. + * + * @param rs the ResultSet + * @param columnName the date column name + * @return the converted LocalDate or {@code null} if the column was {@code NULL} + * @throws SQLException if there is problem getting the date + */ + @Nullable + public static LocalDate localDateFromDateOrNull(ResultSet rs, String columnName) throws SQLException { + return localDateFromDateOrNull(rs.getDate(columnName)); + } + + /** + * Returns a {@link LocalDateTime} from the given {@link java.sql.Date}. + * + * @param date the date to convert + * @return the converted LocalDate or {@code null} if the date is {@code null} + */ + @Nullable + public static LocalDate localDateFromDateOrNull(java.sql.@Nullable Date date) { + return isNull(date) ? null : date.toLocalDate(); + } + /** * Returns a {@link ZonedDateTime} in UTC from the specified column in the {@link ResultSet}. * diff --git a/src/test/java/org/kiwiproject/jdbc/KiwiJdbcTest.java b/src/test/java/org/kiwiproject/jdbc/KiwiJdbcTest.java index 6b14a0f2..4e03ca8b 100644 --- a/src/test/java/org/kiwiproject/jdbc/KiwiJdbcTest.java +++ b/src/test/java/org/kiwiproject/jdbc/KiwiJdbcTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -19,7 +20,9 @@ import java.sql.Timestamp; import java.sql.Types; import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.Month; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -194,6 +197,49 @@ void shouldReturnNull_WhenResultSet_ReturnsNullTimestamp() throws SQLException { } } + @Nested + class LocalDateFromDateOrNull { + + @Test + void shouldConvertFromDate() { + var originalDate = LocalDate.of(2023, Month.APRIL, 1); + var date = java.sql.Date.valueOf(originalDate); + + assertThat(KiwiJdbc.localDateFromDateOrNull(date)).isEqualTo(originalDate); + } + + @Test + void shouldReturnNull_WhenGivenNullDate() { + assertThat(KiwiJdbc.localDateFromDateOrNull(null)).isNull(); + } + + @Test + void shouldConvertFromResultSet() throws SQLException { + var originalDate = LocalDate.of(2023, Month.SEPTEMBER, 8); + var date = java.sql.Date.valueOf(originalDate); + + var resultSet = newMockResultSet(); + when(resultSet.getDate(anyString())).thenReturn(date); + + assertThat(KiwiJdbc.localDateFromDateOrNull(resultSet, "date_of_birth")) + .isEqualTo(originalDate); + + verify(resultSet).getDate("date_of_birth"); + verifyNoMoreInteractions(resultSet); + } + + @Test + void shouldReturnNull_WhenResultSet_ReturnsNullDate() throws SQLException { + var resultSet = newMockResultSet(); + when(resultSet.getDate(anyString())).thenReturn(null); + + assertThat(KiwiJdbc.localDateFromDateOrNull(resultSet, "expiration_date")).isNull(); + + verify(resultSet).getDate("expiration_date"); + verifyNoMoreInteractions(resultSet); + } + } + @Nested class UtcZonedDateTimeFromTimestamp { @@ -638,4 +684,4 @@ private static ResultSet newMockResultSet() { private PreparedStatement newMockPreparedStatement() { return mock(PreparedStatement.class); } -} \ No newline at end of file +}