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 — это подкласс 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 основан на анализе двух дат, образующих диапазон, и выборе оптимального строкового представления с учётом локали. Класс сравнивает компоненты дат (год, месяц, день) и принимает решение о группировке: если год одинаковый — выводится один раз; если месяц одинаковый — выводится один раз; если дни различаются — выводится диапазон дней.
| Диапазон | ru_RU | en_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:45 | Jul 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 и timeStyle применяются к обоим концам диапазона одинаково. Нельзя задать разный стиль для начала и конца (например, .medium для начала и .short для конца). Если требуется разный стиль, необходимо форматировать каждую дату отдельным DateFormatter и склеивать результаты.
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 включает создание экземпляра, настройку стилей и вызов string(from:). Метод принимает DateInterval — структуру с start и end свойствами. DateInterval можно создать как из двух Date, так и инициализировать с Duration (iOS 16+). После форматирования результат готов к отображению в UI без дополнительной обработки.
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 — наиболее читаемый формат для дат без времени.
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 покрывает все эти варианты.
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 минут |
| ISO8601DateFormatter | ISO 8601 формат | 2024-07-21T14:30:00Z |
Когда использовать DateIntervalFormatter: для отображения диапазонов дат в UI — бронирование отелей, событий, фильтрация по датам, история операций. Для подсчёта продолжительности между датами используйте DateComponentsFormatter, а не DateIntervalFormatter — второй не предназначен для отображения длительности. DateFormatter следует использовать только для одиночных дат, а не для склеивания двух отформатированных дат через строковую конкатенацию — это приводит к потере локализации разделителя.
Edge case: DateIntervalFormatter не поддерживает отображение open-ended интервалов (без даты окончания). Если конец диапазона неизвестен (например, «с 21 июля»), используйте DateFormatter с предлогом «с» для русского языка или «from» для английского. Также DateIntervalFormatter не поддерживает интервалы с указанием времени для разных дней — время применяется к каждой дате отдельно, что может выглядеть избыточно для коротких интервалов.
Часто задаваемые вопросы
DateIntervalFormatter — класс Foundation для форматирования диапазонов дат в локализованные строки. Доступен с iOS 8, автоматически группирует повторяющиеся компоненты (месяц, год) и выбирает правильный разделитель для языка.
Сравнивает компоненты от года к дню. Если год совпадает — выводится один раз. Если месяц совпадает — выводится один раз. Различающиеся дни выводятся как диапазон. Это даёт «21–25 июля 2024» вместо «21 июля 2024 – 25 июля 2024».
Нет, разделитель определяется локалью и не настраивается через API. Для кастомного разделителя (например, «до» вместо «по») форматируйте каждую дату отдельным DateFormatter и склеивайте строки вручную.
DateIntervalFormatter форматирует диапазон дат (две даты) как единое целое с группировкой компонентов. DateFormatter форматирует одну дату. Для диапазонов всегда используйте DateIntervalFormatter — это даёт корректную локализацию разделителя.
Автоматически — DateIntervalFormatter определяет, что годы разные, и выводит полные даты для обоих концов: «25 декабря 2024 г. – 5 января 2025 г.». Никаких дополнительных настроек не требуется, достаточно установить dateStyle.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также