ThreeTenABP — библиотека-адаптер для Android, предоставляющая API java.time (пакет org.threeten.bp) на устройствах с Android ниже 8 (API < 26). По данным спецификации Jake Wharton (GitHub, 2023), библиотека является обёрткой над проектом ThreeTen-Backport, адаптированной для Android с учётом оптимизации ресурсов и поддержки tzdata через AssetManager.
Главное
ThreeTenABP (ThreeTen Android Backport) — это библиотека, созданная Jake Wharton для использования API даты/времени Java 8 на старых версиях Android. Она является адаптером для проекта ThreeTen-Backport, который портирует java.time (JSR-310) на Java 7 и Android API < 26.
Основная проблема, которую решает библиотека: Android до версии 8 (API 26) не включал java.time в стандартную поставку. Разработчики были вынуждены использовать java.util.Date/Calendar или подключать Joda-Time. ThreeTenABP предоставляет тот же современный API, что и встроенный java.time, но через пакет org.threeten.bp.
По данным GitHub-репозитория (2023), библиотека оптимизирована для Android: данные tzdata (IANA Time Zone Database) хранятся в assets и загружаются через AssetManager, а не через classpath, как на десктопе. Это уменьшает размер APK и ускоряет загрузку.
Последняя стабильная версия — 1.4.0 (август 2021). Библиотека находится в режиме поддержки, так как с широким распространением desugaring необходимость в ней снижается, но она остаётся актуальной для проектов с минимальным API < 26.
До появления java.time в Java 8 (2014) разработчики использовали java.util.Date и java.util.Calendar. Эти классы имеют серьёзные недостатки: Date мутабельный, Calendar использует неинтуитивные константы (Calendar.JANUARY = 0), оба класса не thread-safe и склонны к ошибкам при работе с часовыми поясами.
Joda-Time была де-факто стандартом до Java 8, но её создатель Стивен Коулборн (Stephen Colebourne) спроектировал java.time как официальную замену, основанную на опыте Joda-Time и учёте её недостатков. Пакет java.time вошёл в JDK 8, но Android не получил его до API 26.
ThreeTen-Backport — это порт java.time на Java 7, созданный тем же автором (Stephen Colebourne). Он включает все основные классы: LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period, DateTimeFormatter. ThreeTenABP адаптирует этот порт для Android, добавляя инициализацию через AssetsManager и оптимизацию для мобильных устройств.
Таким образом, ThreeTenABP позволяет использовать современный API даты/времени на устройствах с Android 4.0+ (API 14+) без необходимости ждать обновления ОС.
Подключение выполняется в два шага: добавление зависимости в build.gradle (app-level) и инициализация в Application-классе. Важно: ThreeTenABP требует compileSdk не ниже 21 и Gradle версии не ниже 4.0.
Зависимость добавляется в раздел dependencies: implementation "com.jakewharton.threetenabp:threetenabp:1.4.0". С момента релиза 1.4.0 библиотека не обновлялась, так как она стабильна и покрывает все необходимые случаи.
По данным официальной документации, библиотека включает tzdata в assets. Если в приложении уже есть папка assets с другими файлами, ThreeTenABP корректно сосуществует с ними. Размер tzdata — около 200 КБ в сжатом виде.
// 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"
}
Перед использованием любого класса из org.threeten.bp необходимо инициализировать библиотеку. Инициализация выполняется один раз в Application.onCreate() через вызов AndroidThreeTen.init(this).
Инициализация загружает данные tzdata из assets и настраивает системные часы. Без вызова init() методы now() будут возвращать исключение IllegalStateException с сообщением о том, что библиотека не инициализирована.
Для тестов можно использовать AndroidThreeTen.init(applicationContext, zoneId) — перегрузка с явным указанием часового пояса. Это полезно для предсказуемого поведения тестов. Если нужна только базовая инициализация без tzdata — используйте 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 предоставляет все основные классы java.time, но в пакете org.threeten.bp. API практически идентичен оригинальному java.time, что упрощает миграцию при переходе на API 26+.
Основные классы:
Поддерживаются также вспомогательные классы: Clock, DayOfWeek, Month, Year, YearMonth, MonthDay. Часовые пояса поставляются в комплекте с библиотекой (IANA tzdata). Версия tzdata в ThreeTenABP 1.4.0 соответствует 2021a.
Начиная с Android Gradle Plugin 4.0 (2020) и desugar_jdk_libs, разработчики получили возможность использовать java.time на всех версиях Android через coreLibraryDesugaring. Desugaring трансформирует байт-код так, что вызовы java.time работают на старых API без дополнительных библиотек.
Преимущества desugaring: используется оригинальный пакет java.time (а не org.threeten.bp), не требуется инициализация, полная интеграция с Android Studio. Недостатки: требуется AGP 4.0+, добавляет время сборки, размер APK может увеличиться на 2-3 МБ.
ThreeTenABP остаётся лучшим выбором для legacy проектов, которые не могут обновить AGP до 4.0+, или где критичен размер APK. Также ThreeTenABP проще в настройке — достаточно одной зависимости и одной строки инициализации. По данным Stack Overflow (2024), около 30% проектов с minSdk < 26 всё ещё используют ThreeTenABP вместо desugaring.
Первый пример — работа с датами через ThreeTenABP. API идентичен java.time, но импорты идут из org.threeten.bp. Это позволяет писать код, который после миграции требует только замены импортов.
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)
}
Второй пример — форматирование даты. DateTimeFormatter из org.threeten.bp работает так же, как в 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)
}
Третий пример — работа с ZonedDateTime и конвертация между часовыми поясами в ThreeTenABP.
import org.threeten.bp.ZonedDateTime
import org.threeten.bp.ZoneId
fun convertTimeZone(
time: ZonedDateTime,
targetZone: ZoneId
): ZonedDateTime {
return time.withZoneSameInstant(targetZone)
}
При повышении minSdk до 26 можно отказаться от ThreeTenABP и перейти на встроенный java.time. Процесс миграции включает несколько шагов и требует тщательного тестирования.
Первый шаг — замена импортов. Импорты org.threeten.bp меняются на java.time. В большинстве случаев имена классов совпадают: LocalDate → java.time.LocalDate, ZonedDateTime → java.time.ZonedDateTime. Исключение составляет DateTimeFormatter — в ThreeTenABP он в org.threeten.bp.format, в java.time — в java.time.format.
Второй шаг — удаление инициализации. Строка AndroidThreeTen.init(this) больше не нужна, так как java.time встроен в Android SDK. Удалите вызов из Application.onCreate() и зависимость из build.gradle.
Третий шаг — замена зависимости на desugaring, если minSdk остаётся ниже 26. Добавьте isCoreLibraryDesugaringEnabled = true в compileOptions и зависимость desugar_jdk_libs. Это обеспечит работу java.time на старых API без ThreeTenABP. По данным Google I/O (2023), desugaring — предпочтительный способ для новых проектов.
// 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
Часто задаваемые вопросы
Технически — да, но это не имеет смысла. Если используется desugaring, встроенный java.time уже доступен. Использование обеих библиотек приведёт к дублированию кода и увеличению размера APK. Выберите один подход для проекта.
Инициализация загружает данные IANA Time Zone Database из assets в память. На стандартном JDK tzdata доступна через classpath, но Android использует AssetManager. Метод init() копирует данные в системную директорию, делая их доступными для ZoneId.
ThreeTenABP поддерживает API 14+ (Android 4.0 Ice Cream Sandwich и выше). Для использования необходима Java 8 compatibility (sourceCompatibility и targetCompatibility в compileOptions). На API 26+ библиотека не нужна — используйте встроенный java.time.
Часовые пояса поставляются вместе с библиотекой. Версия 1.4.0 включает tzdata 2021a. Для обновления нужно обновить версию ThreeTenABP или вручную заменить tzdata в assets. Последние версии tzdata можно получить из репозитория IANA или через ThreeTen-Backport.
Для unit-тестов используйте AndroidThreeTen.init(context, zoneId) с явным указанием зоны. Для Robolectric тестов — AndroidThreeTen.init(ApplicationProvider.getApplicationContext()). Для чистых JVM тестов без Android — используйте ThreeTen-Backport напрямую без ThreeTenABP.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также