androiddatetimekotlinthreetenabp

ThreeTenABP DateTime parser giving exception for yyyy-MM-ddTHH:mm:ss formate


I need to convert a dateTime String to millis and I am using ThreeTenABP for this, but the OffSetDateTime.parse is unable to parse the dateTime String which is for ex. "2020-08-14T20:05:00" and giving the following exception.

Caused by: org.threeten.bp.format.DateTimeParseException:  
Text '2020-09-22T20:35:00' could not be parsed:  
Unable to obtain OffsetDateTime from TemporalAccessor:  
DateTimeBuilder[, ISO, null, 2020-09-22, 20:35], type org.threeten.bp.format.DateTimeBuilder

I have already searched through similar questions but could not find the exact solution.

Below is the code that I am using in Kotlin.

val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss",
                                                                Locale.ROOT)
val givenDateString = event?.eventDateTime
val timeInMillis = OffsetDateTime.parse(givenDateString, formatter)
                                    .toInstant()
                                    .toEpochMilli()

Solution

  • The problem is the missing offset in the String that you are trying to parse to an OffsetDateTime. An OffsetDateTime cannot be created without a ZoneOffset but no ZoneOffset can be derived from this String (one could just guess it's UTC, but guessing is not suitable in such a situation).

    You can parse the String to a LocalDateTime (a representation of a date and a time of day without a zone or an offset) and then add / attach the desired offset. You don't even need a custom DateTimeFormatter because your String is of ISO format and can be parsed using the default built-in formatter:

    fun main() {
        // example String
        val givenDateString = "2020-09-22T20:35:00"
        // determine the zone id of the device (you can alternatively set a fix one here)
        val localZoneId: ZoneId = ZoneId.systemDefault()
        // parse the String to a LocalDateTime
        val localDateTime = LocalDateTime.parse(givenDateString)
        // then create a ZonedDateTime by adding the zone id and convert it to an OffsetDateTime
        val odt: OffsetDateTime = localDateTime.atZone(zoneId).toOffsetDateTime()
        // get the time in epoch milliseconds
        val timeInMillis = odt.toInstant().toEpochMilli()
        // and print it
        println("$odt ==> $timeInMillis")
    }
    

    this example code produces the following output (pay attention to the trailing Z in the datetime representation, that's an offset of +00:00 hours, the UTC time zone, I wrote this code in the Kotlin Playground and it seems to have UTC time zone ;-) ):

    2020-09-22T20:35Z ==> 1600806900000
    

    Please note that I tried this with java.time and not with the ThreeTen ABP, which is obsolete to use for many (lower) Android versions now, since there's Android API Desugaring. However, this shouldn't make a difference because your example code threw exactly the same exception when I tried it first, which means ThreeTen is not to blame for this.