Работа с датами и временем — вечная головная боль разработчика. Сколько раз вы путались в часовых поясах? Забывали про високосный год? Ломали приложение сменой летнего времени? Библиотека kotlinx-datetime от JetBrains создана, чтобы решить эти проблемы раз и навсегда. В этом руководстве я простым языком объясню, как подключить библиотеку, выбрать правильный тип для конкретной задачи и выполнять типичные операции: от получения текущего момента до конвертации между часовыми поясами.
Что такое kotlinx-datetime и зачем она нужна
kotlinx-datetime — это официальная мультиплатформенная библиотека от JetBrains для работы с датами и временем. Она написана на чистом Kotlin и одинаково работает на JVM, Android, iOS, JS и Native — в отличие от java.time, который доступен только на JVM.
Принципы библиотеки простые и строгие. Она основана на международном стандарте ISO 8601, проводит чёткую границу между физическим моментом времени (Instant) и местным гражданским временем (LocalDateTime), а также требует явно указывать часовой пояс при любых преобразованиях между ними. Такой подход помогает избежать ошибок, когда разработчик забывает учесть часовой пояс и получает непредсказуемый результат.
Добавление библиотеки в проект
Чтобы начать работу с kotlinx-datetime, откройте файл build.gradle.kts уровня модуля и добавьте одну строку в блок dependencies. На момент 2026 года актуальна версия 0.7.1.
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1")
}
Для Android-проектов с minSdk ниже 26 потребуется включить core library desugaring, потому что библиотека использует классы java.time под капотом.
Главные типы библиотеки
Чтобы не запутаться, запомните три главных типа и простое правило: моменты во времени храните в Instant, даты без привязки к часовому поясу — в LocalDate или LocalDateTime, а разницу между датами — в DateTimePeriod.
| Тип | Что хранит | Когда использовать |
|---|---|---|
| Instant | Момент на шкале UTC-SLS (одна точка во времени для всего мира) | Логи, время отправки сообщения, deadline заказа — всё, что уже случилось или случится в ближайшее время |
| LocalDate | Дата без времени и часового пояса (год, месяц, день) | День рождения, дата праздника — когда время суток не важно |
| LocalDateTime | Дата и время без часового пояса (год, месяц, день, час, минута, секунда) | Запланированная встреча в будущем, будильник — когда часовой пояс хранится отдельно |
| LocalTime | Время суток без даты и часового пояса | Ежедневное событие: «обед в 13:00», «полив в 08:00» |
| TimeZone | Набор правил для конвертации между Instant и LocalDateTime | Всегда, когда нужно показать время пользователю или сохранить будущее событие |
| DateTimePeriod | Разница между двумя моментами, разложенная на годы, месяцы, дни, часы | Расчёт возраста, оставшегося времени до события |
Как получить текущее время
Самый частый вопрос от новичков: «Как получить текущую дату и время?». Через Clock.System.now() вы получаете Instant — момент прямо сейчас. Затем, если нужно показать время пользователю, преобразуете его в LocalDateTime с помощью часового пояса.
import kotlinx.datetime.*
val now: Instant = Clock.System.now()
val timeZone = TimeZone.currentSystemDefault()
val localDateTime: LocalDateTime = now.toLocalDateTime(timeZone)
val localDate: LocalDate = now.toLocalDate(timeZone)
println(now) // 2026-05-10T10:45:30.123456789Z
println(localDateTime) // 2026-05-10T13:45:30.123456789
println(localDate) // 2026-05-10
Преобразование между часовыми поясами
Одна из главных возможностей библиотеки kotlinx-datetime — корректная работа с часовыми поясами. Когда сервер присылает время в UTC, а пользователь сидит в Санкт-Петербурге, нужно сдвинуть время на UTC+3. И наоборот: когда пользователь назначает встречу на завтра в 15:00 по Ташкенту, нужно сохранить этот момент в UTC, чтобы никто не запутался.
Для фиксированного смещения используйте FixedOffsetTimeZone. Он просто добавляет или вычитает часы и минуты — идеально для UTC, который никогда не меняется.
// из пункта A в пункт B
val instant = Clock.System.now()
val newYorkTime = instant.toLocalDateTime(TimeZone.of("America/New_York"))
// обратно в Instant
val localTime = LocalDateTime(2026, 5, 10, 15, 0, 0)
val timeZone = TimeZone.of("Asia/Tashkent")
val asInstant = localTime.toInstant(timeZone)
Важно не путать UtcOffset (просто смещение, например +05) и TimeZone (правила с летним/зимним временем). Смещение не знает о переходах на летнее время, поэтому для географических зон всегда используйте TimeZone.of("Europe/Moscow").
Форматирование и парсинг дат
Ещё один частый сценарий — отобразить дату в понятном пользователю формате или прочитать строку от сервера. В kotlinx-datetime для обмена данными всегда используют ISO-формат — его понимает метод parse() и выдаёт toString(). А для отображения пользователю можно задать собственный шаблон.
// парсинг ISO-строки (серверный формат)
val instant = Instant.parse("2026-05-10T10:45:30Z")
// парсинг LocalDate из строки
val date = LocalDate.parse("2026-05-10")
// собственный формат через DateTimeFormat
val format = DateTimeFormat.ofPattern("dd.MM.yyyy HH:mm")
val formatted = Clock.System.now()
.toLocalDateTime(TimeZone.currentSystemDefault())
.format(format)
println(formatted) // 10.05.2026 13:45
Практический пример: считаем возраст
Давайте закрепим изученное на реальном коде. Напишем функцию, которая принимает дату рождения и возвращает, сколько лет исполнилось человеку сегодня.
fun getAge(birthDate: LocalDate): Int {
val today = Clock.System.todayAt(TimeZone.currentSystemDefault())
val period = today.monthsUntil(birthDate)
return (period / 12).toInt()
}
val birthDate = LocalDate(1995, 8, 20)
val age = getAge(birthDate)
println("Возраст: $age лет")
Здесь Clock.System.todayAt() возвращает текущую дату по местному часовому поясу, а monthsUntil вычисляет разницу в месяцах — затем делим на 12 и получаем полные годы. Никаких ручных вычислений с миллисекундами и константами.
Советы начинающим
Работа с датами и временем в Kotlin может показаться сложной, но несколько простых правил помогут не наломать дров:
- Храните всегда в UTC. Для логов, сообщений, любых данных используйте
Instant. Конвертируйте в местное время только в момент показа пользователю. - Явно указывайте часовой пояс. Не полагайтесь на
currentSystemDefault()при сохранении данных — пользователь может находиться в поездке, а настройки телефона могут быть ошибочными. - Для будущих событий используйте LocalDateTime + TimeZone. Правила часовых поясов могут измениться, и встреча, запланированная на июль в виде Instant, может сместиться после обновления tzdata.
- Не делайте арифметику на LocalTime. Она запрещена в библиотеке не просто так — без даты и часового пояса результат может быть неверным. Сначала получите Instant, прибавьте период, потом снова выделите время.
Коротко о главном
Библиотека kotlinx-datetime — это современный и безопасный способ работы с датами и временем в Kotlin, доступный на всех платформах. Вы научились получать текущий момент через Clock.System, различать Instant и LocalDateTime, конвертировать время между часовыми поясами, форматировать и парсить даты. Запомните три главных правила: храните время в Instant, всегда указывайте часовой пояс при конвертации и не делайте арифметику над LocalTime без даты и зоны. Применяйте эти принципы в своих проектах — и неприятные сюрпризы с датами останутся в прошлом.