Search code examples
kotlinjava-timethreetenbp

TzdbZoneRulesProvider not working in java.time


I'm migrating an old code base away from the threeten backport library to use java.time instead. There is a test method that no longer works, but I don't know how to fix it.

Here is the class before the changes. This compiles correctly:

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import org.junit.Before
import org.junit.Rule
import org.threeten.bp.ZoneId
import org.threeten.bp.zone.TzdbZoneRulesProvider
import org.threeten.bp.zone.ZoneRulesProvider

open class BaseAvailabilityTest {

    @get:Rule
    val rule = InstantTaskExecutorRule()

    @Before
    open fun setUp() {
        if (ZoneRulesProvider.getAvailableZoneIds().isEmpty()) {
            val stream = this.javaClass.classLoader?.getResourceAsStream("TZDB.dat")
            stream.use(::TzdbZoneRulesProvider).apply {
                ZoneRulesProvider.registerProvider(this)
            }
        }
        zoneId = ZoneId.of("America/Los_Angeles")
    }
}

But when I switch the threeten imports over to java.time like this:

import java.time.ZoneId
import java.time.zone.TzdbZoneRulesProvider
import java.time.zone.ZoneRulesProvider

then Android Studio shows an error:

Type mismatch
Required: (TypeVariable(T))->TypeVariable(R)
Found: KFunction0<TzdbZoneRulesProvider>

I can't find anything that addresses this issue. Can anyone help me understand what is going on here?


Solution

  • You don't need to register the tzdb provider explicitly. That is the default provider for the JVM, as documented in the JavaDocs.

    The Java virtual machine has a default provider that provides zone rules for the time-zones defined by IANA Time Zone Database (TZDB). If the system property java.time.zone.DefaultZoneRulesProvider is defined then it is taken to be the fully-qualified name of a concrete ZoneRulesProvider class to be loaded as the default provider, using the system class loader. If this system property is not defined, a system-default provider will be loaded to serve as the default provider.

    In fact, TzdbZoneRulesProvider is a package-private class in java.time.zone, so you cannot access it anyway.

    This essentially means that you can remove this if entirely:

    if (ZoneRulesProvider.getAvailableZoneIds().isEmpty()) {
        val stream = this.javaClass.classLoader?.getResourceAsStream("TZDB.dat")
        stream.use(::TzdbZoneRulesProvider).apply {
            ZoneRulesProvider.registerProvider(this)
        }
    }
    

    If your tests depend on a particular version of tzdb (like in very rare situations such as this post), you can use the timezone updater tool to change the tzdb version in your JDK. An example is described here.