diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df57d3b..3cc21b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: contents: write @@ -23,20 +23,27 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6.0.1 - + + - name: timezone + uses: szenius/set-timezone@v2.0 + with: + timezoneLinux: "Europe/Berlin" + timezoneMacos: "Europe/Berlin" + timezoneWindows: "W. Europe Standard Time" + - name: Set up JDK 21 uses: actions/setup-java@v5.1.0 with: java-version: '21' distribution: 'temurin' cache: 'gradle' - + - name: Grant execute permission for gradlew run: chmod +x gradlew - + - name: Build with Gradle run: ./gradlew build - + - name: Upload test results uses: actions/upload-artifact@v5.0.0 if: always() diff --git a/build.gradle b/build.gradle index f5abae2..e88270a 100644 --- a/build.gradle +++ b/build.gradle @@ -95,4 +95,4 @@ tasks.register('specsValidation', Test) { group = 'verification' include '**/ConformanceTest.class' -} \ No newline at end of file +} diff --git a/src/main/java/dev/toonformat/jtoon/normalizer/JsonNormalizer.java b/src/main/java/dev/toonformat/jtoon/normalizer/JsonNormalizer.java index 9e238de..bdac3c9 100644 --- a/src/main/java/dev/toonformat/jtoon/normalizer/JsonNormalizer.java +++ b/src/main/java/dev/toonformat/jtoon/normalizer/JsonNormalizer.java @@ -25,6 +25,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.List; @@ -219,8 +220,16 @@ private static JsonNode tryNormalizeTemporal(Object value) { return formatTemporal(zonedDateTime, DateTimeFormatter.ISO_ZONED_DATE_TIME); } else if (value instanceof OffsetDateTime offsetDateTime) { return formatTemporal(offsetDateTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } else if (value instanceof Calendar calendar) { + return StringNode.valueOf(calendar.toInstant().toString()); } else if (value instanceof Instant instant) { return StringNode.valueOf(instant.toString()); + } else if (value instanceof java.sql.Timestamp timestamp) { + return formatTemporal(timestamp.toLocalDateTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } else if (value instanceof java.sql.Date date) { + return formatTemporal(date.toLocalDate(), DateTimeFormatter.ISO_LOCAL_DATE); + } else if (value instanceof java.sql.Time time) { + return formatTemporal(time.toLocalTime(), DateTimeFormatter.ISO_LOCAL_TIME); } else if (value instanceof Date date) { return StringNode.valueOf(LocalDate.ofInstant(date.toInstant(), ZoneId.systemDefault()).toString()); } else { diff --git a/src/test/java/dev/toonformat/jtoon/JToonTest.java b/src/test/java/dev/toonformat/jtoon/JToonTest.java index 9661dcc..36c8377 100644 --- a/src/test/java/dev/toonformat/jtoon/JToonTest.java +++ b/src/test/java/dev/toonformat/jtoon/JToonTest.java @@ -8,7 +8,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.math.BigInteger; +import java.sql.Date; import java.time.Instant; +import java.time.LocalDate; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -1237,6 +1239,26 @@ void encodesWithJsonPropertyOrderAnnotations() { encode); } + @Test + @DisplayName("encodes a POJO with SQLDate") + void encodesSQLDate() { + // Given + UserDTO userDTO = new UserDTO(123,"Bob", "Marley", new java.sql.Date(1766419274)); + + // When + String encode = encode(userDTO); + + // Then + assertEquals( + """ + id: 123 + firstName: Bob + lastName: Marley + lastLogin: "1970-01-21T10:40:19.274Z\"""", + encode); + } + + @Test @DisplayName("encodes POJO with annotation: JsonSerialize") void encodesWithJsonSerializeAnnotations() { diff --git a/src/test/java/dev/toonformat/jtoon/TestPojos.java b/src/test/java/dev/toonformat/jtoon/TestPojos.java index 481e4e2..814112a 100644 --- a/src/test/java/dev/toonformat/jtoon/TestPojos.java +++ b/src/test/java/dev/toonformat/jtoon/TestPojos.java @@ -4,15 +4,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.annotation.JsonRootName; import tools.jackson.core.JacksonException; import tools.jackson.core.JsonGenerator; import tools.jackson.databind.SerializationContext; import tools.jackson.databind.annotation.JsonSerialize; import tools.jackson.databind.ser.std.StdSerializer; -import java.io.IOException; -import java.text.SimpleDateFormat; import java.util.List; import java.util.Map; @@ -172,6 +169,8 @@ public record HotelInfoLlmRerankDTO(String no, String hotelAddressDistance) { } + public record UserDTO(Integer id, String firstName, String lastName, java.sql.Date lastLogin) {} + /** * Custom Serializer for HotelInfoLlmRerankDTO */ diff --git a/src/test/java/dev/toonformat/jtoon/normalizer/JsonNormalizerTest.java b/src/test/java/dev/toonformat/jtoon/normalizer/JsonNormalizerTest.java index 69f8c1c..36e1594 100644 --- a/src/test/java/dev/toonformat/jtoon/normalizer/JsonNormalizerTest.java +++ b/src/test/java/dev/toonformat/jtoon/normalizer/JsonNormalizerTest.java @@ -30,8 +30,10 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.Calendar; import java.util.Collection; import java.util.Date; +import java.util.GregorianCalendar; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -328,6 +330,34 @@ void testLocalDateTime() { assertEquals("2023-10-15T14:30:45", result.asString()); } + @Test + @DisplayName("should convert java.sql.Date to ISO formatted StringNode") + void testSQLDate() { + java.sql.Date dateTime = new java.sql.Date(1766419274); + JsonNode result = JsonNormalizer.normalize(dateTime); + assertTrue(result.isString()); + assertEquals("1970-01-21", result.asString()); + } + + @Test + @DisplayName("should convert java.sql.Time to ISO formatted StringNode") + void testSQLTime() { + java.sql.Time time = new java.sql.Time(1766419274); + JsonNode result = JsonNormalizer.normalize(time); + assertTrue(result.isString()); + assertEquals("11:40:19", result.asString()); + } + + @Test + @DisplayName("should convert java.sql.Timestamp to ISO formatted StringNode") + void testSQLTimeStamp() { + java.sql.Timestamp dateTime = new java.sql.Timestamp(1766419274); + JsonNode result = JsonNormalizer.normalize(dateTime); + assertTrue(result.isString()); + assertEquals("1970-01-21T11:40:19.274", result.asString()); + } + + @Test @DisplayName("should convert LocalDate to ISO formatted StringNode") void testLocalDate() { @@ -1148,6 +1178,36 @@ void givenInstant_whenTryNormalizeTemporal_thenIsoStringNode() throws Exception assertEquals("2025-11-26T14:45:36Z", ((JsonNode) result).asString()); } + @Test + @DisplayName("Given Calendar, When tryNormalizeTemporal is called, Then an ISO date StringNode is returned") + void givenCalendar_whenTryNormalizeTemporal_thenIsoStringNode() throws Exception { + // Given + Calendar input = Calendar.getInstance(); + input.set(2017, Calendar.FEBRUARY, 16, 20, 22, 28); + input.set(Calendar.MILLISECOND, 0); + + // When + Object result = invokePrivateStatic("tryNormalizeTemporal", new Class[]{Object.class}, input); + + // Then + assertInstanceOf(StringNode.class, result); + assertEquals("2017-02-16T19:22:28Z", ((JsonNode) result).asString()); + } + + @Test + @DisplayName("Given GregorianCalendar, When tryNormalizeTemporal is called, Then an ISO date StringNode is returned") + void givenGregorianCalendar_whenTryNormalizeTemporal_thenIsoStringNode() throws Exception { + // Given + GregorianCalendar input = new GregorianCalendar(2017, Calendar.FEBRUARY, 16, 20, 22, 28); + + // When + Object result = invokePrivateStatic("tryNormalizeTemporal", new Class[]{Object.class}, input); + + // Then + assertInstanceOf(StringNode.class, result); + assertEquals("2017-02-16T19:22:28Z", ((JsonNode) result).asString()); + } + @Test @DisplayName("Given Date, When tryNormalizeTemporal is called, Then an ISO date StringNode is returned") void givenDate_whenTryNormalizeTemporal_thenIsoStringNode() throws Exception {