Skip to content

Commit 3d2ce49

Browse files
committed
✨ DatePatchMapper now supports new java LocalDate LocalDateTime formats
1 parent a3526aa commit 3d2ce49

2 files changed

Lines changed: 81 additions & 9 deletions

File tree

sormas-backend/src/main/java/de/symeda/sormas/backend/patch/mapping/impl/valuemapper/DatePatchMapper.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import java.text.ParseException;
44
import java.text.SimpleDateFormat;
5+
import java.time.LocalDate;
6+
import java.time.LocalDateTime;
7+
import java.time.ZoneId;
58
import java.util.Arrays;
69
import java.util.Date;
710
import java.util.List;
@@ -22,17 +25,14 @@ public class DatePatchMapper implements ValuePatchMapper {
2225

2326
private static final Logger logger = LoggerFactory.getLogger(DatePatchMapper.class);
2427

25-
private static final Set<Class<?>> SUPPORTED_TYPES = Set.of(Date.class);
28+
private static final Set<Class<?>> SUPPORTED_TYPES = Set.of(Date.class, LocalDate.class, LocalDateTime.class);
2629

2730
private static final List<String> DATE_FORMATS = Arrays.asList(
28-
"yyyy-MM-dd", // 2025-12-17
29-
"yyyy/MM/dd", // 2025/12/17
30-
"yyyy-MM-dd'T'HH:mm:ssZ", // 2025-12-17T14:30:00+0100
3131
"yyyy-MM-dd'T'HH:mm:ssXXX", // 2025-12-17T14:30:00+01:00
32+
"yyyy-MM-dd'T'HH:mm:ssZ", // 2025-12-17T14:30:00+0100
3233
"yyyy-MM-dd'T'HH:mm:ss", // 2025-12-17T14:30:00
3334
"yyyy-MM-dd'T'HH:mm", // 2025-12-17T14:30
34-
"yyyy/MM/dd'T'HH:mm:ss", // 2025/12/17T14:30:00
35-
"yyyy/MM/dd'T'HH:mm:ssXXX" // 2025/12/17T14:30:00+01:00
35+
"yyyy-MM-dd" // 2025-12-17
3636
);
3737

3838
@Override
@@ -50,12 +50,14 @@ public <T> ValueMappingResult<T> map(ValuePatchRequest<T> request) {
5050

5151
String str = value.toString().trim();
5252

53+
Class<?> targetType = request.getTargetType();
54+
5355
for (String format : DATE_FORMATS) {
5456
try {
5557
SimpleDateFormat sdf = new SimpleDateFormat(format);
5658
sdf.setLenient(false);
5759
Date parsed = sdf.parse(str);
58-
return ValueMappingResult.withData((T) parsed);
60+
return ValueMappingResult.withData((T) toTargetType(parsed, targetType));
5961
} catch (ParseException e) {
6062
// try next format
6163
}
@@ -65,4 +67,14 @@ public <T> ValueMappingResult<T> map(ValuePatchRequest<T> request) {
6567

6668
return ValueMappingResult.withCause(DataPatchFailureCause.INVALID_VALUE_TYPE);
6769
}
70+
71+
private Object toTargetType(Date parsed, Class<?> targetType) {
72+
if (targetType == LocalDate.class) {
73+
return parsed.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
74+
}
75+
if (targetType == LocalDateTime.class) {
76+
return parsed.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
77+
}
78+
return parsed;
79+
}
6880
}

sormas-backend/src/test/java/de/symeda/sormas/backend/patch/mapping/impl/valuemapper/DateMapperTest.java

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import static org.junit.jupiter.api.Assertions.assertEquals;
44

55
import java.text.SimpleDateFormat;
6+
import java.time.LocalDate;
7+
import java.time.LocalDateTime;
68
import java.util.Date;
79
import java.util.Set;
810

@@ -18,9 +20,9 @@ class DateMapperTest extends AbstractUnitTest {
1820
private DatePatchMapper victim;
1921

2022
@Test
21-
void getSupportedTypes_containsDateClass() {
23+
void getSupportedTypes_containsDateClasses() {
2224
// PREPARE
23-
Set<Class<?>> expected = Set.of(Date.class);
25+
Set<Class<?>> expected = Set.of(Date.class, LocalDate.class, LocalDateTime.class);
2426

2527
// EXECUTE
2628
Set<Class<?>> actual = victim.getSupportedTypes();
@@ -109,4 +111,62 @@ void map_randomString_throwsIllegalArgumentException() {
109111
// EXECUTE & CHECK
110112
assertEquals(DataPatchFailureCause.INVALID_VALUE_TYPE, victim.map("notADate", Date.class).getDataPatchFailureCause());
111113
}
114+
115+
// --- LocalDate ---
116+
117+
@Test
118+
void map_localDate_validDate() {
119+
// EXECUTE & CHECK
120+
assertEquals(LocalDate.of(2024, 6, 15), victim.map("2024-06-15", LocalDate.class).getData());
121+
}
122+
123+
@Test
124+
void map_localDate_fromDateTimeString_returnsDatePart() {
125+
// EXECUTE & CHECK
126+
assertEquals(LocalDate.of(2024, 6, 15), victim.map("2024-06-15T14:30:00", LocalDate.class).getData());
127+
}
128+
129+
@Test
130+
void map_localDate_invalidFormat() {
131+
// EXECUTE & CHECK
132+
assertEquals(DataPatchFailureCause.INVALID_VALUE_TYPE, victim.map("15/06/2024", LocalDate.class).getDataPatchFailureCause());
133+
}
134+
135+
@Test
136+
void map_localDate_invalidDay() {
137+
// EXECUTE & CHECK
138+
assertEquals(DataPatchFailureCause.INVALID_VALUE_TYPE, victim.map("2024-02-30", LocalDate.class).getDataPatchFailureCause());
139+
}
140+
141+
// --- LocalDateTime ---
142+
143+
@Test
144+
void map_localDateTime_validDateTime() {
145+
// EXECUTE & CHECK
146+
assertEquals(LocalDateTime.of(2024, 6, 15, 14, 30, 0), victim.map("2024-06-15T14:30:00", LocalDateTime.class).getData());
147+
}
148+
149+
@Test
150+
void map_localDateTime_validDateTimeWithoutSeconds() {
151+
// EXECUTE & CHECK
152+
assertEquals(LocalDateTime.of(2024, 6, 15, 14, 30, 0), victim.map("2024-06-15T14:30", LocalDateTime.class).getData());
153+
}
154+
155+
@Test
156+
void map_localDateTime_fromDateOnlyString_returnsMidnight() {
157+
// EXECUTE & CHECK
158+
assertEquals(LocalDateTime.of(2024, 6, 15, 0, 0, 0), victim.map("2024-06-15", LocalDateTime.class).getData());
159+
}
160+
161+
@Test
162+
void map_localDateTime_invalidFormat() {
163+
// EXECUTE & CHECK
164+
assertEquals(DataPatchFailureCause.INVALID_VALUE_TYPE, victim.map("15/06/2024", LocalDateTime.class).getDataPatchFailureCause());
165+
}
166+
167+
@Test
168+
void map_localDateTime_invalidDay() {
169+
// EXECUTE & CHECK
170+
assertEquals(DataPatchFailureCause.INVALID_VALUE_TYPE, victim.map("2024-02-30T10:00:00", LocalDateTime.class).getDataPatchFailureCause());
171+
}
112172
}

0 commit comments

Comments
 (0)