DateIntervalFormatter — основы, форматирование интервалов дат

Автор: IT Sectr Опубликовано: 2026-07-13 Время чтения: 11 мин

DateIntervalFormatter — это класс Foundation в iOS и macOS, предназначенный для форматирования диапазонов дат в локализованные строки: «21–25 июля 2024 г.», «с 21 по 25 июля 2024 г.» или «Jul 21–25, 2024». По данным Apple Developer Documentation, 2024, DateIntervalFormatter автоматически определяет, какие компоненты даты повторять (если диапазон в пределах одного месяца — месяц выносится один раз), и локализует разделители и порядок слов. В отличие от ручного склеивания двух отформатированных дат, этот класс учитывает грамматические правила языка: для русского — предлог «по», для английского — тире без предлога. Класс доступен начиная с iOS 8 и macOS 10.10.

Главное

  • DateIntervalFormatter — класс Foundation для форматирования диапазонов дат (iOS 8+)
  • Автоматическая группировка — повторяющиеся компоненты (месяц, год) выводятся один раз
  • Локализованные разделители — учёт грамматических правил: «по» (русский), тире (английский)
  • Три стиля даты — .none, .short, .medium, .long, .full для каждого конца диапазона
  • Четыре стиля времени — те же уровни детализации для временной части диапазона

Что такое DateIntervalFormatter?

DateIntervalFormatter — это подкласс Formatter в Foundation, который принимает объект DateInterval (диапазон между двумя датами) и возвращает локализованную строку с описанием этого диапазона. Например, для диапазона с 21 по 25 июля 2024 года результат будет «21–25 июля 2024 г.» для ru_RU и «Jul 21–25, 2024» для en_US. Класс умно группирует повторяющиеся элементы: если обе даты в одном месяце, название месяца выводится один раз.

Основная ценность DateIntervalFormatter — автоматическая обработка грамматики диапазонов. При ручном форматировании разработчику пришлось бы сравнивать месяц/год начала и окончания, выбирать разделитель (тире, «по», «to», «bis») и следить за склонением. DateIntervalFormatter делает это автоматически на основе данных CLDR (Common Locale Data Repository), что уменьшает количество багов в интернациональных приложениях, особенно для языков со сложной грамматикой.

По данным Unicode CLDR v44 (2024), правила форматирования интервалов дат сильно различаются между языками. Немецкий использует предлог «bis» (21.–25. Juli 2024), французский — «au» (du 21 au 25 juillet 2024), японский — через дефис (2024 Jul 21-25). DateIntervalFormatter покрывает все эти вариации без дополнительного кода на стороне разработчика.

Как работает DateIntervalFormatter в Foundation?

Принцип работы DateIntervalFormatter основан на анализе двух дат, образующих диапазон, и выборе оптимального строкового представления с учётом локали. Класс сравнивает компоненты дат (год, месяц, день) и принимает решение о группировке: если год одинаковый — выводится один раз; если месяц одинаковый — выводится один раз; если дни различаются — выводится диапазон дней.

Диапазонru_RUen_US
Один день21 июля 2024 г.Jul 21, 2024
Внутри месяца21–25 июля 2024 г.Jul 21–25, 2024
Разные месяцы21 июля – 5 августа 2024 г.Jul 21 – Aug 5, 2024
Разные годы25 декабря 2024 г. – 5 января 2025 г.Dec 25, 2024 – Jan 5, 2025
С временем21 июля 2024 г., 14:30–16:45Jul 21, 2024, 2:30–4:45 PM

Логика группировки реализована на основе шаблонов CLDR. Форматтер проверяет equality каждого компонента даты (Calendar.Component: year, month, day) в порядке от старшего к младшему. Как только компонент различается, все младшие компоненты выводятся для обеих дат. Например, для 21–25 июля: год совпадает, месяц совпадает, день различается → месяц выводится один раз, дни — для каждой даты.

Важный нюанс: DateIntervalFormatter не поддерживает настройку разделителя через API. Разделитель определяется локалью и не может быть переопределён. Если требуется нестандартный разделитель (например, «до» вместо «по»), придётся форматировать даты вручную через DateFormatter и склеивать строки с кастомным разделителем.

Стили форматирования интервалов

DateIntervalFormatter предоставляет два набора стилей: dateStyle и timeStyle — для каждого конца диапазона. Эти стили работают аналогично DateFormatter: .none скрывает компонент, .short даёт короткий формат (21.07.2024), .medium — средний (21 июл. 2024 г.), .long — полный (21 июля 2024 г.), .full — максимально подробный (воскресенье, 21 июля 2024 г.). Комбинируя dateStyle и timeStyle, можно получить разные уровни детализации.

Примеры комбинаций стилей

  • dateStyle = .medium, timeStyle = .none — только дата: «21–25 июля 2024 г.»
  • dateStyle = .short, timeStyle = .short — дата и время: «21.07.2024, 14:30 – 25.07.2024, 16:45»
  • dateStyle = .none, timeStyle = .short — только время: «14:30–16:45» (если даты одинаковые)
  • dateStyle = .long, timeStyle = .none — длинная дата: «21–25 июля 2024 года»
  • dateStyle = .full, timeStyle = .short — полный формат: «воскресенье, 21 июля 2024 г. – четверг, 25 июля 2024 г.»

Важно: dateStyle и timeStyle применяются к обоим концам диапазона одинаково. Нельзя задать разный стиль для начала и конца (например, .medium для начала и .short для конца). Если требуется разный стиль, необходимо форматировать каждую дату отдельным DateFormatter и склеивать результаты.

swift
import Foundation

let formatter = DateIntervalFormatter()

// Different style combinations
let startDate = Date()
let endDate = Date().addingTimeInterval(345600) // +4 days
let interval = DateInterval(start: startDate, end: endDate)

// Date only, medium format
formatter.dateStyle = .medium
formatter.timeStyle = .none
print("Date only: \(formatter.string(from: interval))")

// Short format with time
formatter.dateStyle = .short
formatter.timeStyle = .short
print("Short with time: \(formatter.string(from: interval))")

// Full format
formatter.dateStyle = .full
formatter.timeStyle = .none
print("Full date: \(formatter.string(from: interval))")

DateIntervalFormatter в Swift: примеры

Базовое использование DateIntervalFormatter включает создание экземпляра, настройку стилей и вызов string(from:). Метод принимает DateInterval — структуру с start и end свойствами. DateInterval можно создать как из двух Date, так и инициализировать с Duration (iOS 16+). После форматирования результат готов к отображению в UI без дополнительной обработки.

swift
import Foundation

let calendar = Calendar.current
let now = Date()

// Specific interval examples
let examples: [(title: String, start: Date, end: Date)] = [
    (
        "2-hour event",
        now,
        now.addingTimeInterval(7200)
    ),
    (
        "Week-long vacation",
        now,
        now.addingTimeInterval(604800)
    ),
    (
        "Cross-year period",
        calendar.date(from: DateComponents(
            year: 2024, month: 12, day: 25
        ))!,
        calendar.date(from: DateComponents(
            year: 2025, month: 1, day: 5
        ))!
    )
]

let intervalFormatter = DateIntervalFormatter()
intervalFormatter.dateStyle = .medium
intervalFormatter.timeStyle = .short

for (title, start, end) in examples {
    let interval = DateInterval(start: start, end: end)
    let result = intervalFormatter.string(from: interval)
    print("\(title): \(result)")
}

// Format via Calendar (iOS 16+)
if let nextWeek = calendar.date(
    byAdding: .day, value: 7, to: now
) {
    let weekInterval = DateInterval(start: now, end: nextWeek)
    print("Next 7 days: \(intervalFormatter.string(from: weekInterval))")
}

DateIntervalFormatter и Calendar Booking: в приложениях бронирования (отели, билеты) интервалы дат — ключевой элемент UI. DateIntervalFormatter корректно обрабатывает межгодовые интервалы (например, с 28 декабря по 3 января), группируя месяц и год для каждой даты отдельно. Для таких сценариев рекомендуется использовать dateStyle = .medium и timeStyle = .none — наиболее читаемый формат для дат без времени.

swift
import Foundation

let formatter = DateIntervalFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none

// Booking examples
struct Booking {
    let checkIn: Date
    let checkOut: Date

    var formattedInterval: String {
        let interval = DateInterval(start: checkIn, end: checkOut)
        return formatter.string(from: interval)
    }
}

let booking = Booking(
    checkIn: calendar.date(from: DateComponents(
        year: 2024, month: 7, day: 21
    ))!,
    checkOut: calendar.date(from: DateComponents(
        year: 2024, month: 7, day: 25
    ))!
)
print("Booking: \(booking.formattedInterval)")

// Use with different locales
let enFormatter = DateIntervalFormatter()
enFormatter.dateStyle = .medium
enFormatter.timeStyle = .none
enFormatter.locale = Locale(identifier: "en_US")
print("English: \(enFormatter.string(from: interval))")

DateInterval vs DateIntervalFormatter: DateInterval — структура, представляющая диапазон дат (начало и конец). DateIntervalFormatter — форматтер, преобразующий эту структуру в строку. DateInterval также поддерживает проверку contains(Date) и intersection(with:), что полезно для логики бронирования (проверка пересечения дат).

Локализация интервалов дат

DateIntervalFormatter локализует вывод через свойство locale. По умолчанию используется Locale.current — локаль устройства. Для отображения интервалов на другом языке (например, контент на английском при русском интерфейсе) установите locale явно. В отличие от DateFormatter, DateIntervalFormatter не требует установки Locale для серверных данных — он всегда использует свою внутреннюю логику группировки на основе CLDR.

Языковые различия в форматировании интервалов дат значительно сложнее, чем для одиночных дат. Английский использует простое тире: Jul 21–25. Русский — предлог «по»: с 21 по 25 июля. Французский — предлог «au»: du 21 au 25 juillet. Немецкий — точка после числа и тире: 21.–25. Juli. Японский — через дефис без предлогов: Jul 21-25. DateIntervalFormatter покрывает все эти варианты.

swift
import Foundation

let formatter = DateIntervalFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none

let start = Date()
let end = Date().addingTimeInterval(259200) // +3 days
let interval = DateInterval(start: start, end: end)

// Demonstrate different locales
let localeIds = ["ru_RU", "en_US", "de_DE", "fr_FR", "ja_JP", "zh_CN"]
for localeId in localeIds {
    formatter.locale = Locale(identifier: localeId)
    print("\(localeId): \(formatter.string(from: interval))")
}

// Localization for time intervals
formatter.dateStyle = .none
formatter.timeStyle = .short

let todayStart = Date()
let todayEnd = Date().addingTimeInterval(3600)
let timeInterval = DateInterval(start: todayStart, end: todayEnd)

for localeId in ["ru_RU", "en_US", "de_DE"] {
    formatter.locale = Locale(identifier: localeId)
    print("\(localeId) time: \(formatter.string(from: timeInterval))")
}

Особенности локализации: для русского языка DateIntervalFormatter использует предлоги «с» и «по» для длинных форматов даты. Для коротких (short) — только тире: 21.07–25.07.2024. Для полного формата (full) — с указанием дня недели. Выбор формата зависит от контекста: для списка бронирований используйте .medium, для детального просмотра — .long или .full.

Сравнение с другими форматтерами дат

DateIntervalFormatter занимает нишу форматирования диапазонов дат, которую не покрывают другие классы Foundation. DateFormatter форматирует одиночные даты, RelativeDateTimeFormatter — относительные даты (назад/через), DateComponentsFormatter — продолжительность (2 часа 30 минут). Каждый из этих классов решает свою задачу, и их нельзя взаимозаменять.

КлассНазначениеПример вывода
DateIntervalFormatterДиапазон дат21–25 июля 2024 г.
DateFormatterОдиночная дата21 июля 2024 г.
RelativeDateTimeFormatterОтносительное время3 дня назад
DateComponentsFormatterПродолжительность2 часа 30 минут
ISO8601DateFormatterISO 8601 формат2024-07-21T14:30:00Z

Когда использовать DateIntervalFormatter: для отображения диапазонов дат в UI — бронирование отелей, событий, фильтрация по датам, история операций. Для подсчёта продолжительности между датами используйте DateComponentsFormatter, а не DateIntervalFormatter — второй не предназначен для отображения длительности. DateFormatter следует использовать только для одиночных дат, а не для склеивания двух отформатированных дат через строковую конкатенацию — это приводит к потере локализации разделителя.

Edge case: DateIntervalFormatter не поддерживает отображение open-ended интервалов (без даты окончания). Если конец диапазона неизвестен (например, «с 21 июля»), используйте DateFormatter с предлогом «с» для русского языка или «from» для английского. Также DateIntervalFormatter не поддерживает интервалы с указанием времени для разных дней — время применяется к каждой дате отдельно, что может выглядеть избыточно для коротких интервалов.

Часто задаваемые вопросы

Что такое DateIntervalFormatter?

DateIntervalFormatter — класс Foundation для форматирования диапазонов дат в локализованные строки. Доступен с iOS 8, автоматически группирует повторяющиеся компоненты (месяц, год) и выбирает правильный разделитель для языка.

Как DateIntervalFormatter группирует даты?

Сравнивает компоненты от года к дню. Если год совпадает — выводится один раз. Если месяц совпадает — выводится один раз. Различающиеся дни выводятся как диапазон. Это даёт «21–25 июля 2024» вместо «21 июля 2024 – 25 июля 2024».

Можно ли изменить разделитель диапазона?

Нет, разделитель определяется локалью и не настраивается через API. Для кастомного разделителя (например, «до» вместо «по») форматируйте каждую дату отдельным DateFormatter и склеивайте строки вручную.

Чем DateIntervalFormatter отличается от DateFormatter?

DateIntervalFormatter форматирует диапазон дат (две даты) как единое целое с группировкой компонентов. DateFormatter форматирует одну дату. Для диапазонов всегда используйте DateIntervalFormatter — это даёт корректную локализацию разделителя.

Как отформатировать интервал с разными годами?

Автоматически — DateIntervalFormatter определяет, что годы разные, и выводит полные даты для обоих концов: «25 декабря 2024 г. – 5 января 2025 г.». Никаких дополнительных настроек не требуется, достаточно установить dateStyle.

Итоги

  • DateIntervalFormatter — специализированный класс для форматирования диапазонов дат (iOS 8+)
  • Автоматическая группировка — повторяющиеся компоненты (год, месяц) выводятся один раз для всего диапазона
  • Локализованные разделители — «по» (русский), тире (английский), «bis» (немецкий) — автоматически
  • Стили даты и времени — комбинации .none, .short, .medium, .long, .full для каждого конца диапазона
  • Не подходит для длительности — для подсчёта продолжительности используйте DateComponentsFormatter
  • Не поддерживает кастомные разделители — для нестандартных случаев требуется ручное форматирование
  • Интеграция с DateInterval — структура DateInterval предоставляет проверку contains и intersection

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также