2

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()
deHaar
  • 17,687
  • 10
  • 38
  • 51
RKB
  • 106
  • 10
  • https://stackoverflow.com/questions/35298214/unable-to-obtain-offsetdatetime-from-temporalaccessor – ADM Sep 23 '20 at 06:52
  • Does this answer your question? [Unable to obtain OffsetDateTime from TemporalAccessor](https://stackoverflow.com/questions/35298214/unable-to-obtain-offsetdatetime-from-temporalaccessor) – Ole V.V. Sep 23 '20 at 16:43

1 Answers1

3

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.

deHaar
  • 17,687
  • 10
  • 38
  • 51
  • Thanks for the detailed answer! The ZoneOffset makes much sense since it is a UTC. Though I used ThreeTenABP as it is backward compatible and java.time requires min API 26. – RKB Sep 23 '20 at 10:12
  • @RKB you're welcome. Have a look at the link in the last paragraph of my answer, `java.time` (at least a large subset of it) can now be used in lower API levels as well if you are using the required gradle plugin. Thanks for accepting and voting. – deHaar Sep 23 '20 at 10:14
  • 1
    Nice answer! Just some additional info: Since the goal is to calculate epoch millis, `val odt: OffsetDateTime = localDateTime.atZone(zoneId).toOffsetDateTime()` can be written as `val zdt: ZonedDateTime = localDateTime.atZone(ZoneOffset.UTC)`. After that, the millis could be calculated either as `zdt.toEpochSecond().1000` or `zdt.toInstant().toEpochMilli()`. In summary, converting to `ZonedDateTime` instead of `OffsetDateTime` would have made the code slightly cleaner. – Arvind Kumar Avinash Oct 01 '20 at 08:59