Skip to content

Instantly share code, notes, and snippets.

@OndraZizka
Last active October 30, 2021 09:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OndraZizka/5fd56479ed2f6175703eb8a2e1bb1088 to your computer and use it in GitHub Desktop.
Save OndraZizka/5fd56479ed2f6175703eb8a2e1bb1088 to your computer and use it in GitHub Desktop.
Parse various date-time formats: Year only to full LocalDateTime, or relative from now (to the past). Kotlin.
package ch.zizka.time
import java.time.Duration
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException
import java.time.temporal.ChronoField.DAY_OF_MONTH
import java.time.temporal.ChronoField.HOUR_OF_DAY
import java.time.temporal.ChronoField.MINUTE_OF_HOUR
import java.time.temporal.ChronoField.MONTH_OF_YEAR
import java.time.temporal.ChronoField.SECOND_OF_MINUTE
import java.time.temporal.ChronoField.YEAR
import java.time.temporal.ChronoUnit
import java.time.temporal.UnsupportedTemporalTypeException
object FlexibleTemporalInputParser {
val RELAXED_FORMATTER = DateTimeFormatter.ofPattern("yyyy[-MM[-dd[' 'HH:mm[:ss[.SSS]]]]]")
fun parseTemporalInput(input: String): LocalDateTime? {
when (input.lowercase()) {
"now" -> return LocalDateTime.now()
"midnight", "today" -> return LocalDateTime.now().truncatedTo(ChronoUnit.DAYS)
}
try {
val parsed = RELAXED_FORMATTER.parse(input)
var result = LocalDateTime.MAX.withNano(0)
for (field in listOf(YEAR, MONTH_OF_YEAR, DAY_OF_MONTH, HOUR_OF_DAY, MINUTE_OF_HOUR, SECOND_OF_MINUTE)) {
try {
result = result.with(field, parsed.getLong(field))
} catch (ex: UnsupportedTemporalTypeException) {
result = result.with(field, if (field.isDateBased) 1 else 0)
}
}
return result
}
catch (parseEx: DateTimeParseException) {
try {
val inputToIso8601 = "P" + input.uppercase().replace("-","").replace(" ", "").replace("D", "DT").removeSuffix("T")
// Expected format: "PnDTnHnMn.nS"
val duration = Duration.parse(inputToIso8601)
val base =
if (inputToIso8601.contains("D"))
LocalDateTime.now().truncatedTo(ChronoUnit.DAYS)
else
LocalDateTime.now()
return base.minus(duration)
}
catch (ex: DateTimeParseException) {
return null
}
}
return null
}
}
package ch.zizka.time
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit
class FlexibleTemporalInputParserTest {
@Test fun testDateTime() {
val dateTime = FlexibleTemporalInputParser.parseTemporalInput("1983-02-17 16:32:19")
assertThat(dateTime).isEqualTo(LocalDateTime.parse("1983-02-17T16:32:19"))
}
@Test fun testDateTime_Minutes() {
val dateTime = FlexibleTemporalInputParser.parseTemporalInput("1983-02-17 16:32")
assertThat(dateTime).isEqualTo(LocalDateTime.parse("1983-02-17T16:32:00"))
}
@Test fun testDate() {
val dateTime = FlexibleTemporalInputParser.parseTemporalInput("1983-02-17")
assertThat(dateTime).isEqualTo(LocalDateTime.parse("1983-02-17T00:00:00"))
}
@Test fun testDateRelative_1d_minus() {
val dateTime = FlexibleTemporalInputParser.parseTemporalInput("-1d")
assertThat(dateTime).isEqualTo(LocalDateTime.now().truncatedTo(ChronoUnit.DAYS).minusDays(1))
}
@Test fun testDateRelative_1d() {
val dateTime = FlexibleTemporalInputParser.parseTemporalInput("1d")
assertThat(dateTime).isEqualTo(LocalDateTime.now().truncatedTo(ChronoUnit.DAYS).minusDays(1))
}
@Test fun testDateRelative_0d() {
val dateTime = FlexibleTemporalInputParser.parseTemporalInput("0d")
assertThat(dateTime).isEqualTo(LocalDateTime.now().truncatedTo(ChronoUnit.DAYS))
}
@Test fun testDateRelative_now() {
val dateTime = FlexibleTemporalInputParser.parseTemporalInput("now")
assertThat(dateTime?.truncatedTo(ChronoUnit.SECONDS)).isEqualTo(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS))
}
@Test fun testDateRelative_midnight() {
val dateTime = FlexibleTemporalInputParser.parseTemporalInput("midnight")
assertThat(dateTime).isEqualTo(LocalDateTime.now().truncatedTo(ChronoUnit.DAYS))
}
}
package ch.zizka.utils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/**
* Creates a logger for the containing class.
* Usage:
* companion object {
* private val log = logger()
* }
*/
fun Any.logger(): Logger {
val clazz = if (this::class.isCompanion) this::class.java.enclosingClass else this::class.java
return LoggerFactory.getLogger(clazz)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment