ThreeTen-Backport error on Android - ZoneRulesException: No time-zone data files registered
Asked Answered
D

5

54

I'm using ThreeTen-Backport library for my Android project (because java.time is not yet implemented in android development).

When I write LocalDate today=LocalDate.now(); or LocalTime time=LocalTime.now(); I get the following exception:

Caused by: org.threeten.bp.zone.ZoneRulesException: 
  No time-zone data files registered   
      at org.threeten.bp.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:176)
      at org.threeten.bp.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:133)
      at org.threeten.bp.ZoneRegion.ofId(ZoneRegion.java:143)
      at org.threeten.bp.ZoneId.of(ZoneId.java:357)
      at org.threeten.bp.ZoneId.of(ZoneId.java:285)
      at org.threeten.bp.ZoneId.systemDefault(ZoneId.java:244)
      at org.threeten.bp.Clock.systemDefaultZone(Clock.java:137)
      at org.threeten.bp.LocalDate.now(LocalDate.java:165)

The same line of code works well in another java project I have, which uses the native java.time library.

I searched for a possible solution but couldn't find anything useful: one solution suggested I need to use another jar that includes the time-zone rules and other suggested that there might be two or more ThreeTenBP-libraries inside the classpath.
Those cases don't match my case.

Inside the build.gradle file, at the dependencies section, I've tried few configurations:

  • At first, I used - compile 'com.jakewharton.threetenabp:threetenabp:1.0.3'
  • Then, I tried - compile 'org.threeten:threetenbp:1.0.3'
  • After that, I tried - compile 'org.threeten:threetenbp:1.3.1'
  • Currently, I use compile 'org.threeten:threetenbp:1.3.2'

I don't know what is wrong with that line of code and how to fix it.
The LocalDate.now() and LocalTime.now() methods should work without specifying a time zone.

Disclose answered 9/7, 2016 at 12:29 Comment(0)
E
117

For Android project you should use

implementation 'com.jakewharton.threetenabp:threetenabp:1.0.3'

Make sure you call AndroidThreeTen.init(this); before using the classes from the library. This will read the time zones data (included in the library). You can initialize the library in your Application class in the onCreate method just like it is recommended in the README.

Ephah answered 9/7, 2016 at 12:38 Comment(6)
Thank you, my project didn't have an application class before, so I wasn't able to use the AndroidThreeTen.init(this); code. I thought to add this in the activity's onCreate method but I saw it wasn't right. Now I've created a new class which extends the application class and put that line in the onCreate method. Now everything works just great!Disclose
Calling init in onCreate violates strictmode and can even delay app startup. If you call init asynchronously, you have to make sure not to use it until it completes, which can be difficult for JUnit testing.Biogen
@ErikB it does violate strict mode because it reads the time zones file from assets so it delays startup time. For unit tests you should use the original library threeten.org/threetenbp because this one has Android dependencies as explained here github.com/JakeWharton/ThreeTenABP/issues/14Ephah
How to initialize this library in a Unit test?Nureyev
You don't. You just add the original one for unit tests (testImplementation) and you are good to goEphah
This https://mcmap.net/q/25105/-androidthreeten-not-working-in-unit-test-without-robolectric was useful to make it work in unit tests 👍🏻Bucko
M
3

Instead of initialization of the library, you can try this:

LocalDateEx.kt

object LocalDateEx {
    /**an alternative of LocalDate.now(), as it requires initialization using AndroidThreeTen.init(context), which takes a bit time (loads a file)*/
    @JvmStatic
    fun getNow(): LocalDate = Calendar.getInstance().toLocalDate()
}

fun Calendar.toLocalDate(): LocalDate = LocalDate.of(get(Calendar.YEAR), get(Calendar.MONTH) + 1, get(Calendar.DAY_OF_MONTH))

LocalTimeEx.kt

object LocalTimeEx {
    /**an alternative of LocalDateTime.now(), as it requires initialization using AndroidThreeTen.init(context), which takes a bit time (loads a file)*/
    @JvmStatic
    fun getNow(): LocalTime = Calendar.getInstance().toLocalTime()
}

fun Calendar.toLocalTime(): LocalTime = LocalTime.of(get(Calendar.HOUR_OF_DAY), get(Calendar.MINUTE), get(Calendar.SECOND), get(Calendar.MILLISECOND) * 1000000)

LocalDateTimeEx.kt

object LocalDateTimeEx {
    /**an alternative of LocalDateTime.now(), as it requires initialization using AndroidThreeTen.init(context), which takes a bit time (loads a file)*/
    @JvmStatic
    fun getNow(): LocalDateTime = Calendar.getInstance().toLocalDateTime()
}

private fun Calendar.toLocalDateTime(): LocalDateTime = LocalDateTime.of(get(Calendar.YEAR), get(Calendar.MONTH) + 1, get(Calendar.DAY_OF_MONTH), get(Calendar.HOUR_OF_DAY), get(Calendar.MINUTE), get(Calendar.SECOND),
        get(Calendar.MILLISECOND) * 1000000)

Usage:

   val today=LocalDateEx.getNow()
   val today2=LocalTimeEx.getNow()
   val today3=LocalDateTimeEx.getNow()
Malvinamalvino answered 22/3, 2018 at 12:38 Comment(1)
this is very useful for compose previews or testsTrudytrue
S
2

For me, it wasn't working on signed release build with proguard enabled. Check your proguard rules if they contain

-keep class org.threeten.bp.zone.*

unless, add it. It helped for me.

Saladin answered 28/2, 2020 at 9:42 Comment(0)
I
2

When I was using API 25 java.time did not work. Using Jake Wharton's threetenadp did. So here's a slight update to use it on Studio 2020.3.1

In the module build.gradle

dependencies {
 implementation 'com.jakewharton.threetenabp:threetenabp:1.3.1'
}

import com.jakewharton.threetenabp.AndroidThreeTen

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    AndroidThreeTen.init(application);

When the IDE asks to import, be sure to choose com.jakewharton.threetenabp.AndroidThreeTen and not java.time

LocalTime.now().toSecondOfDay() is what I needed.

Islamize answered 4/11, 2021 at 1:10 Comment(0)
O
1

AGP 4.0 now support Java 8 Desugar API, see here: https://developer.android.com/studio/write/java8-support

So you can use java.time. without using a backported version

NOTES:

There's still an issue if you trying to support API below 21 (solved by minSdk 21): https://github.com/google/bundletool/issues/182#issuecomment-753269941

Desugaring will also increase builds time as stated in the official docs

To support these language APIs, the plugin compiles a separate DEX file that contains an implementation of the missing APIs and includes it in your app. The desugaring process rewrites your app’s code to instead use this library at runtime.

Outwardly answered 11/1, 2021 at 11:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.