Работа с датами и временем в Kotlin: полный гайд

Работа с датами и временем — вечная головная боль разработчика. Сколько раз вы путались в часовых поясах? Забывали про високосный год? Ломали приложение сменой летнего времени? Библиотека 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 без даты и зоны. Применяйте эти принципы в своих проектах — и неприятные сюрпризы с датами останутся в прошлом.