ThreeTenABP is an adapter library for Android that provides the java.time API (org.threeten.bp package) on devices with Android below 8 (API < 26). According to the specification by Jake Wharton (GitHub, 2023), the library is a wrapper around the ThreeTen-Backport project, adapted for Android with resource optimization and tzdata support through AssetManager.
Key Takeaways
ThreeTenABP (ThreeTen Android Backport) is a library created by Jake Wharton for using the Java 8 date/time API on older Android versions. It is an adapter for the ThreeTen-Backport project, which ports java.time (JSR-310) to Java 7 and Android API < 26.
The main problem the library solves: Android before version 8 (API 26) did not include java.time in the standard distribution. Developers were forced to use java.util.Date/Calendar or add Joda-Time. ThreeTenABP provides the same modern API as built-in java.time, but through the org.threeten.bp package.
According to the GitHub repository (2023), the library is optimized for Android: tzdata (IANA Time Zone Database) is stored in assets and loaded through AssetManager, rather than through classpath as on desktop. This reduces APK size and speeds up loading.
The latest stable version is 1.4.0 (August 2021). The library is in maintenance mode, as with the widespread adoption of desugaring, the need for it is decreasing, but it remains relevant for projects with a minimum API < 26.
Before java.time was introduced in Java 8 (2014), developers used java.util.Date and java.util.Calendar. These classes have serious drawbacks: Date is mutable, Calendar uses non-intuitive constants (Calendar.JANUARY = 0), both classes are not thread-safe and are prone to errors when working with time zones.
Joda-Time was the de facto standard before Java 8, but its creator Stephen Colebourne designed java.time as the official replacement, based on the experience of Joda-Time and addressing its shortcomings. The java.time package was included in JDK 8, but Android did not receive it until API 26.
ThreeTen-Backport is a port of java.time to Java 7, created by the same author (Stephen Colebourne). It includes all the main classes: LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period, DateTimeFormatter. ThreeTenABP adapts this port for Android, adding initialization through AssetsManager and optimization for mobile devices.
Thus, ThreeTenABP allows you to use the modern date/time API on devices with Android 4.0+ (API 14+) without waiting for an OS update.
Adding the library takes two steps: adding the dependency in build.gradle (app-level) and initializing it in the Application class. Important: ThreeTenABP requires compileSdk of at least 21 and Gradle version of at least 4.0.
The dependency is added in the dependencies section: implementation "com.jakewharton.threetenabp:threetenabp:1.4.0". Since the release of 1.4.0, the library has not been updated, as it is stable and covers all necessary cases.
According to the official documentation, the library includes tzdata in assets. If the app already has an assets folder with other files, ThreeTenABP coexists with them correctly. The tzdata size is about 200 KB compressed.
// build.gradle (app-level)
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation "com.jakewharton.threetenabp:threetenabp:1.4.0"
}
Before using any class from org.threeten.bp, the library must be initialized. Initialization is performed once in Application.onCreate() by calling AndroidThreeTen.init(this).
Initialization loads tzdata from assets and configures the system clock. Without calling init(), the now() methods will throw an IllegalStateException with a message that the library is not initialized.
For testing, you can use AndroidThreeTen.init(applicationContext, zoneId) — an overload with explicit time zone specification. This is useful for predictable test behavior. If only basic initialization without tzdata is needed, use AndroidThreeTen.initWithoutFiles(context).
class App : Application() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
}
}
// Usage after initialization
val today = LocalDate.now()
val now = LocalDateTime.now()
ThreeTenABP provides all the main java.time classes, but in the org.threeten.bp package. The API is virtually identical to the original java.time, making migration easier when transitioning to API 26+.
Main classes:
Helper classes are also supported: Clock, DayOfWeek, Month, Year, YearMonth, MonthDay. Time zones are bundled with the library (IANA tzdata). The tzdata version in ThreeTenABP 1.4.0 corresponds to 2021a.
Starting with Android Gradle Plugin 4.0 (2020) and desugar_jdk_libs, developers gained the ability to use java.time on all Android versions through coreLibraryDesugaring. Desugaring transforms bytecode so that java.time calls work on older APIs without additional libraries.
Advantages of desugaring: uses the original java.time package (not org.threeten.bp), no initialization required, full integration with Android Studio. Disadvantages: requires AGP 4.0+, adds build time, APK size may increase by 2-3 MB.
ThreeTenABP remains the best choice for legacy projects that cannot update AGP to 4.0+, or where APK size is critical. ThreeTenABP is also easier to set up — just one dependency and one line of initialization. According to Stack Overflow (2024), about 30% of projects with minSdk < 26 still use ThreeTenABP instead of desugaring.
The first example demonstrates working with dates using ThreeTenABP. The API is identical to java.time, but imports come from org.threeten.bp. This allows you to write code that, after migration, only requires replacing imports.
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalTime
import org.threeten.bp.Duration
fun isWeekend(date: LocalDate): Boolean {
val dayOfWeek = date.getDayOfWeek()
return dayOfWeek == DayOfWeek.SATURDAY ||
dayOfWeek == DayOfWeek.SUNDAY
}
fun timeBetween(
start: LocalTime, end: LocalTime
): Duration {
return Duration.between(start, end)
}
The second example shows date formatting. DateTimeFormatter from org.threeten.bp works the same as in java.time.
import org.threeten.bp.LocalDateTime
import org.threeten.bp.format.DateTimeFormatter
fun formatTimestamp(dateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
return dateTime.format(formatter)
}
The third example demonstrates working with ZonedDateTime and converting between time zones in ThreeTenABP.
import org.threeten.bp.ZonedDateTime
import org.threeten.bp.ZoneId
fun convertTimeZone(
time: ZonedDateTime,
targetZone: ZoneId
): ZonedDateTime {
return time.withZoneSameInstant(targetZone)
}
When raising minSdk to 26, you can drop ThreeTenABP and switch to built-in java.time. The migration process includes several steps and requires thorough testing.
The first step is replacing imports. Imports from org.threeten.bp are changed to java.time. In most cases, the class names match: LocalDate → java.time.LocalDate, ZonedDateTime → java.time.ZonedDateTime. The exception is DateTimeFormatter — in ThreeTenABP it is in org.threeten.bp.format, in java.time it is in java.time.format.
The second step is removing initialization. The line AndroidThreeTen.init(this) is no longer needed, as java.time is built into the Android SDK. Remove the call from Application.onCreate() and the dependency from build.gradle.
The third step is replacing the dependency with desugaring, if minSdk remains below 26. Add isCoreLibraryDesugaringEnabled = true in compileOptions and the desugar_jdk_libs dependency. This will ensure java.time works on older APIs without ThreeTenABP. According to Google I/O (2023), desugaring is the preferred approach for new projects.
// build.gradle — replace ThreeTenABP with desugaring
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
// Remove: implementation "com.jakewharton.threetenabp:threetenabp:1.4.0"
// Add:
"coreLibraryDesugaring"("com.android.tools:desugar_jdk_libs:2.1.4")
}
// Remove AndroidThreeTen.init(this) from Application
Frequently Asked Questions
Technically — yes, but it doesn't make sense. If desugaring is used, built-in java.time is already available. Using both libraries will lead to code duplication and increased APK size. Choose one approach for your project.
Initialization loads the IANA Time Zone Database from assets into memory. On standard JDK, tzdata is available through classpath, but Android uses AssetManager. The init() method copies data to the system directory, making it available for ZoneId.
ThreeTenABP supports API 14+ (Android 4.0 Ice Cream Sandwich and above). Java 8 compatibility is required (sourceCompatibility and targetCompatibility in compileOptions). On API 26+, the library is not needed — use built-in java.time.
Time zones are bundled with the library. Version 1.4.0 includes tzdata 2021a. To update, you need to update the ThreeTenABP version or manually replace tzdata in assets. The latest tzdata versions can be obtained from the IANA repository or through ThreeTen-Backport.
For unit tests, use AndroidThreeTen.init(context, zoneId) with an explicit zone specification. For Robolectric tests — AndroidThreeTen.init(ApplicationProvider.getApplicationContext()). For pure JVM tests without Android — use ThreeTen-Backport directly without ThreeTenABP.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also