Search code examples
kotlintimezone-offsetmoshithreetenbp

How to parse time stamp and time zone offset simultaneously with Moshi?


A JSON-API-response contains the following properties:

created_at_timestamp: 1565979486,
timezone: "+01:00",

I am using Moshi and ThreeTenBp to parse the time stamps and prepared the following custom adapters:

class ZonedDateTimeAdapter {

    @FromJson
    fun fromJson(jsonValue: Long?) = jsonValue?.let {
        try {
            ZonedDateTime.ofInstant(Instant.ofEpochSecond(jsonValue), ZoneOffset.UTC) // <---
        } catch (e: DateTimeParseException) {
            println(e.message)
            null
        }
    }

}

As you can see the zone offset is hardcoded here.

class ZonedDateTimeJsonAdapter : JsonAdapter<ZonedDateTime>() {

    private val delegate = ZonedDateTimeAdapter()

    override fun fromJson(reader: JsonReader): ZonedDateTime? {
        val jsonValue = reader.nextLong()
        return delegate.fromJson(jsonValue)
    }

}

...

class ZoneOffsetAdapter {

    @FromJson
    fun fromJson(jsonValue: String?) = jsonValue?.let {
        try {
            ZoneOffset.of(jsonValue)
        } catch (e: DateTimeException) {
            println(e.message)
            null
        }
    }

}

...

class ZoneOffsetJsonAdapter : JsonAdapter<ZoneOffset>() {

    private val delegate = ZoneOffsetAdapter()

    override fun fromJson(reader: JsonReader): ZoneOffset? {
        val jsonValue = reader.nextString()
        return delegate.fromJson(jsonValue)
    }

}

The adapters are registered with Moshi as follows:

Moshi.Builder()
    .add(ZoneOffset::class.java, ZoneOffsetJsonAdapter())
    .add(ZonedDateTime::class.java, ZonedDateTimeJsonAdapter())
    .build()

Parsing the individual fields (created_at_timestamp, timezone) works fine. I want however get rid of the hardcoded zone offset. How can I configure Moshi to fall back on the timezone property when parsing the created_at_timestamp property.

Related


Solution

  • For the created_at_timestamp field you should use a type that doesn't have a timezone. This is usually Instant. It identifies a moment in time independent of which timezone it is being interpreted in.

    Then in your enclosing type you can define a getter method to combine the instant and zone into one value. The ZonedDateTime.ofInstant method can do this.