ThreeTenABP
Android Development
Date and Time API
Java
Mobile App Development

How to use ThreeTenABP in Android Project

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

ThreeTenABP brings the modern Java time API to Android apps that need consistent date-time handling on older devices. It wraps ThreeTenBP and provides Android-friendly initialization and timezone data loading. Using it correctly avoids a large class of timezone and daylight-saving bugs.

Add Dependency and Initialize at App Startup

Initialization should happen once in your Application class before date-time operations run. If you skip this step, runtime exceptions are common when loading zone rules.

kotlin
1// build.gradle (module)
2dependencies {
3    implementation("com.jakewharton.threetenabp:threetenabp:1.4.7")
4}
kotlin
1import android.app.Application
2import com.jakewharton.threetenabp.AndroidThreeTen
3
4class App : Application() {
5    override fun onCreate() {
6        super.onCreate()
7        AndroidThreeTen.init(this)
8    }
9}

Then register the Application class in AndroidManifest.xml so initialization runs for all app processes.

Use org.threeten.bp Types in App Code

After initialization, you can use LocalDate, LocalDateTime, ZonedDateTime, and DateTimeFormatter with consistent behavior. Prefer explicit zones for server communication and persistence.

kotlin
1import org.threeten.bp.Instant
2import org.threeten.bp.ZoneId
3import org.threeten.bp.ZonedDateTime
4import org.threeten.bp.format.DateTimeFormatter
5
6fun main() {
7    val zone = ZoneId.of("America/Toronto")
8    val now = ZonedDateTime.ofInstant(Instant.now(), zone)
9    val fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z")
10    println(now.format(fmt))
11}

For API payloads, store timestamps in UTC and convert to user timezone only at presentation boundaries. This prevents cross-region consistency issues.

Parsing and Formatting User Input Safely

Date parsing should always specify a formatter and expected locale. Avoid relying on device defaults for parse logic in business workflows.

kotlin
1import org.threeten.bp.LocalDate
2import org.threeten.bp.format.DateTimeFormatter
3
4fun parseBirthday(input: String): LocalDate {
5    val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
6    return LocalDate.parse(input, formatter)
7}
8
9println(parseBirthday("1998-04-12"))

If you need interoperability with legacy java.util.Date, convert at boundaries and keep modern types internally.

Migration Notes for Newer API Levels

On newer Android API levels with java.time support, projects often move away from ThreeTenABP. If you are in transition, isolate time operations in one utility layer so migration to native java.time is straightforward.

Keep tests around timezone conversion, month-end boundaries, and daylight-saving transitions. These tests should stay unchanged regardless of library migration.

Architecture Pattern for Time Handling

A practical architecture is to keep all time conversions inside one utility layer. UI code requests formatted strings from this layer, while repositories store and retrieve UTC timestamps only. This separation prevents accidental timezone conversions in random fragments and activities. For recurring jobs such as reminders, compute next trigger times using ZonedDateTime in user timezone and persist the equivalent UTC instant. On app restart, reconstruct user-facing schedule text from stored instants and current zone rules. This process handles daylight-saving transitions correctly because zone rules are applied consistently. Add unit tests for boundary moments such as spring-forward and fall-back dates. Teams that centralize time logic usually avoid the class of bugs where reminders appear one hour early or late after timezone changes.

kotlin
1import org.threeten.bp.Instant
2import org.threeten.bp.ZoneId
3import org.threeten.bp.ZonedDateTime
4
5fun toUserLocal(utcEpochMillis: Long, zoneId: String): ZonedDateTime {
6    return ZonedDateTime.ofInstant(Instant.ofEpochMilli(utcEpochMillis), ZoneId.of(zoneId))
7}
8
9fun toUtcMillis(local: ZonedDateTime): Long = local.toInstant().toEpochMilli()

Verification Checklist

Add instrumentation tests that parse, format, and convert timestamps around timezone boundaries. Include at least one case where local midnight maps to previous UTC date. This confirms your storage and display contracts remain stable across locales and daylight-saving transitions.

Common Pitfalls

  • Forgetting AndroidThreeTen.init, which causes zone-data failures.
  • Mixing legacy Date logic across the app without a clear boundary.
  • Parsing user input with implicit locale or format assumptions.
  • Storing local timezone timestamps in backend systems.

Add one additional test that serializes to UTC and back to local time for every supported user timezone in your app.

Summary

  • Add ThreeTenABP dependency and initialize it once at app startup.
  • Use org.threeten.bp classes for consistent date-time handling.
  • Parse and format with explicit patterns.
  • Keep UTC for storage and convert only for display.
  • Isolate time logic to simplify future migration.

Course illustration
Course illustration

All Rights Reserved.