Skip to content

Commit 62a7fc2

Browse files
authored
Add more flexibility in LocalDateTime parsing (#40)
1 parent 37fad7d commit 62a7fc2

File tree

2 files changed

+110
-8
lines changed

2 files changed

+110
-8
lines changed

src/main/kotlin/javatimefun/localdatetime/extensions/LocalDateTimeParsingExtensions.kt

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import java.time.LocalTime
66
import java.time.format.DateTimeFormatter
77
import java.time.format.DateTimeParseException
88

9+
private const val flexibleIso8601Format = "yyyy-MM-dd'T'HH:mm:ss[.SSSSSS][.SSSS][.SSS][.SS][.S]['Z']"
10+
911
/**
1012
* Works off of String representations of date(time) and parses through the following attempts in order when
1113
* no format is present:
@@ -20,18 +22,19 @@ import java.time.format.DateTimeParseException
2022
* @return LocalDateTime? Null means couldn't parse, else parsed LocalDateTime.
2123
*/
2224
fun String.toLocalDateTime(format: String? = null): LocalDateTime? {
23-
val localDateTime = parseLocalDateTimeHelper(this, format)
24-
if (localDateTime != null) {
25-
return localDateTime
26-
}
25+
var localDateTime = parseLocalDateTimeOrNull(this, format)
26+
if (localDateTime != null) return localDateTime
27+
28+
localDateTime = parseLocalDateTimeOrNull(this, flexibleIso8601Format)
29+
if (localDateTime != null) return localDateTime
30+
2731
val localDate = this.toLocalDate(format)
28-
if (localDate != null) {
29-
return LocalDateTime.of(localDate, LocalTime.MIN)
30-
}
32+
if (localDate != null) return LocalDateTime.of(localDate, LocalTime.MIN)
33+
3134
return null
3235
}
3336

34-
private fun parseLocalDateTimeHelper(dateText: String, format: String?): LocalDateTime? =
37+
private fun parseLocalDateTimeOrNull(dateText: String, format: String?): LocalDateTime? =
3538
if (format.isNullOrEmpty())
3639
try {
3740
LocalDateTime.parse(dateText)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package localdatetime
2+
3+
import javatimefun.localdatetime.extensions.print
4+
import javatimefun.localdatetime.extensions.toLocalDateTime
5+
import org.junit.jupiter.api.Assertions.assertEquals
6+
import org.junit.jupiter.api.Test
7+
import java.lang.RuntimeException
8+
import java.time.LocalDateTime
9+
10+
class LocalDateTimeParsingExtensionsTest {
11+
12+
companion object {
13+
private const val ISO_8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'"
14+
private const val ISO_8601_S = "yyyy-MM-dd'T'HH:mm:ss.S'Z'"
15+
private const val ISO_8601_SS = "yyyy-MM-dd'T'HH:mm:ss.SS'Z'"
16+
private const val ISO_8601_SSS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
17+
private const val ISO_8601_SSSS = "yyyy-MM-dd'T'HH:mm:ss.SSSS'Z'"
18+
private const val ISO_8601_SSSSSS = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'"
19+
}
20+
21+
@Test
22+
fun `given date in ISO_8601, when parsed without format & converted to date, then should match when printed back to text`() {
23+
// given
24+
val dateInText = "2023-07-26T12:34:56Z"
25+
26+
// when
27+
val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse")
28+
29+
// then
30+
assertEquals(dateInText, dateParsed.print(ISO_8601))
31+
assertEquals("2023-07-26T12:34:56.000000Z", dateParsed.print(ISO_8601_SSSSSS))
32+
}
33+
34+
@Test
35+
fun `given date in ISO_8601_S, when parsed without format & converted to date, then should match when printed back to text`() {
36+
// given
37+
val dateInText = "2023-07-26T12:34:56.1Z"
38+
39+
// when
40+
val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse")
41+
42+
// then
43+
assertEquals(dateInText, dateParsed.print(ISO_8601_S))
44+
assertEquals("2023-07-26T12:34:56.100000Z", dateParsed.print(ISO_8601_SSSSSS))
45+
}
46+
47+
@Test
48+
fun `given date in ISO_8601_SS, when parsed without format & converted to date, then should match when printed back to text`() {
49+
// given
50+
val dateInText = "2023-07-26T12:34:56.12Z"
51+
52+
// when
53+
val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse")
54+
55+
// then
56+
assertEquals(dateInText, dateParsed.print(ISO_8601_SS))
57+
assertEquals("2023-07-26T12:34:56.120000Z", dateParsed.print(ISO_8601_SSSSSS))
58+
}
59+
60+
@Test
61+
fun `given date in ISO_8601_SSS, when parsed without format & converted to date, then should match when printed back to text`() {
62+
// given
63+
val dateInText = "2023-07-26T12:34:56.123Z"
64+
65+
// when
66+
val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse")
67+
68+
// then
69+
assertEquals(dateInText, dateParsed.print(ISO_8601_SSS))
70+
assertEquals("2023-07-26T12:34:56.123000Z", dateParsed.print(ISO_8601_SSSSSS))
71+
}
72+
73+
@Test
74+
fun `given date in ISO_8601_SSSS, when parsed without format & converted to date, then should match when printed back to text`() {
75+
// given
76+
val dateInText = "2023-07-26T12:34:56.1234Z"
77+
78+
// when
79+
val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse")
80+
81+
// then
82+
assertEquals(dateInText, dateParsed.print(ISO_8601_SSSS))
83+
assertEquals("2023-07-26T12:34:56.123400Z", dateParsed.print(ISO_8601_SSSSSS))
84+
}
85+
86+
@Test
87+
fun `given date in ISO_8601_SSSSSS, when parsed without format & converted to date, then should match when printed back to text`() {
88+
// given
89+
val dateInText = "2023-07-26T12:34:56.123456Z"
90+
91+
// when
92+
val dateParsed: LocalDateTime = dateInText.toLocalDateTime() ?: throw RuntimeException("Failed to parse")
93+
94+
// then
95+
assertEquals(dateInText, dateParsed.print(ISO_8601_SSSSSS))
96+
assertEquals("2023-07-26T12:34:56.123456Z", dateParsed.print(ISO_8601_SSSSSS))
97+
}
98+
99+
}

0 commit comments

Comments
 (0)