Работа с датами и временем в Kotlin: продвинутые техники и примеры

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Продвинутая работа с датами и временем в Kotlin

В современной разработке на Kotlin грамотная обработка дат и времени — не просто удобство, а необходимость. На продвинутом уровне разработчику приходится иметь дело с часовыми поясами, периодами, длительностями и сложными операциями форматирования. В этой статье мы рассмотрим ключевые инструменты и приёмы, которые помогут вам писать надёжный и эффективный код.

1. Основные классы: LocalDate, LocalTime, LocalDateTime и Instant

Kotlin полностью наследует мощную библиотеку java.time (JSR-310), которая стала стандартом для работы с датами и временем в Java 8 и выше. Для Kotlin Multiplatform существует библиотека kotlinx-datetime, но в рамках JVM-проектов наиболее популярным остаётся java.time.

LocalDate, LocalTime, LocalDateTime

Эти классы представляют дату и время без привязки к часовому поясу. Они идеальны для хранения дней рождения, расписаний и других данных, где часовой пояс не важен.

import java.time.LocalDate
import java.time.LocalTime
import java.time.LocalDateTime

fun main() {
    val date = LocalDate.now()
    val time = LocalTime.of(14, 30, 0)
    val dateTime = LocalDateTime.of(date, time)
    
    println("Дата: $date")
    println("Время: $time")
    println("Дата и время: $dateTime")
    
    // Сложение и вычитание дней
    val tomorrow = date.plusDays(1)
    println("Завтра: $tomorrow")
}

Instant — метка времени для машин

Instant представляет собой точку на временной шкале в UTC. Он незаменим для логирования, обмена данными между серверами и работы с API.

import java.time.Instant

fun main() {
    val now = Instant.now()
    println("Текущий момент в UTC: $now")
    
    // Конвертация из миллисекунд
    val fromEpoch = Instant.ofEpochMilli(1_700_000_000_000)
    println("Из миллисекунд: $fromEpoch")
}

2. Часовые пояса: ZonedDateTime и OffsetDateTime

Когда приложение работает с пользователями из разных регионов, необходимо учитывать часовые пояса. Для этого существуют ZonedDateTime (с полной информацией о зоне) и OffsetDateTime (только смещение от UTC).

import java.time.ZonedDateTime
import java.time.ZoneId
import java.time.ZoneOffset

fun main() {
    // Текущее время в Нью-Йорке
    val newYork = ZonedDateTime.now(ZoneId.of("America/New_York"))
    println("Нью-Йорк: $newYork")
    
    // Преобразование из UTC
    val utcNow = ZonedDateTime.now(ZoneOffset.UTC)
    val moscowTime = utcNow.withZoneSameInstant(ZoneId.of("Europe/Moscow"))
    println("Москва: $moscowTime")
    
    // OffsetDateTime для простых смещений
    val offsetTime = java.time.OffsetDateTime.now(ZoneOffset.ofHours(3))
    println("+3 часа: $offsetTime")
}

Важно: Для пользовательского интерфейса всегда используйте ZonedDateTime, чтобы корректно обрабатывать переход на летнее/зимнее время. OffsetDateTime подходит для фиксированных смещений, например, при работе с логами.

3. Разница между датами: Period и Duration

Для вычисления разницы во времени Kotlin предоставляет два класса: Period (для дат, в днях/месяцах/годах) и Duration (для точных интервалов в секундах/наносекундах).

Period — работа с календарными единицами

import java.time.LocalDate
import java.time.Period

fun main() {
    val birthDate = LocalDate.of(1990, 5, 15)
    val today = LocalDate.now()
    val age = Period.between(birthDate, today)
    
    println("Возраст: ${age.years} лет, ${age.months} месяцев, ${age.days} дней")
    
    // Добавление периода к дате
    val nextMonth = today.plus(Period.ofMonths(1))
    println("Через месяц: $nextMonth")
}

Duration — точные интервалы

import java.time.Duration
import java.time.Instant

fun main() {
    val start = Instant.now()
    Thread.sleep(1500) // симуляция работы
    val end = Instant.now()
    
    val duration = Duration.between(start, end)
    println("Прошло: ${duration.seconds} секунд и ${duration.nano} наносекунд")
    
    // Сравнение длительностей
    val timeout = Duration.ofSeconds(2)
    if (duration.compareTo(timeout) < 0) {
        println("Операция выполнена быстрее таймаута")
    }
}

4. Форматирование и парсинг с помощью DateTimeFormatter

Библиотека java.time.format.DateTimeFormatter позволяет гибко настраивать вывод дат и читать строки из внешних источников. На продвинутом уровне часто требуется работа с локализованными форматами.

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import java.util.Locale

fun main() {
    val now = LocalDateTime.now()
    
    // Пользовательский формат
    val customFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
    println("Пользовательский: ${now.format(customFormatter)}")
    
    // Локализованный формат (для США)
    val usFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
        .withLocale(Locale.US)
    println("США: ${now.format(usFormatter)}")
    
    // Парсинг строки
    val dateString = "2024-12-25T10:30:00"
    val parsedDate = LocalDateTime.parse(dateString, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
    println("Распарсено: $parsedDate")
    
    // Локализованный парсинг (русский)
    val russianFormatter = DateTimeFormatter.ofPattern("d MMMM yyyy", Locale("ru"))
    val russianDate = LocalDate.parse("15 мая 2024", russianFormatter)
    println("Русская дата: $russianDate")
}

Совет: Если ваше приложение поддерживает несколько языков, используйте ofLocalizedDateTime() с разными Locale. Это избавит вас от ручного управления десятками шаблонов.

5. Продвинутые техники: работа с временными интервалами и таймзонами

На практике часто требуется не просто получить разницу, а проверить, пересекаются ли два временных интервала,

Блоги

Рекомендаций по книгам