В мире есть два типа разработчиков: те, у кого глаза дергаются при упоминании часовых поясов, и те, кто все еще думает, что в сутках всегда 24 часа.
Время и часовые пояса, пожалуй, одна из самых обманчиво простых, но в то же время невероятно сложных вещей, с которыми вам придется иметь дело как разработчику. В качестве примера можно привести этот прекрасный урок истории из документации Apple по их NSCalendar
классу.
Григорианский календарь был впервые введен в 1582 году как замена юлианскому календарю. Согласно юлианскому календарю к февралю любого года добавляется високосный день с числом, кратным 4, что приводит к годовому расхождению в 11 минут или 1 день каждые 128 лет. Григорианский календарь пересмотрел правила расчета високосных дней, пропустив високосные дни для любого года с числом, кратным 100, если только этот год не делится на 400, в результате чего годовая разница составляет всего 26 секунд, или 1 день каждые 3323 года.
Для перехода от юлианского календаря к григорианскому календарю из григорианского календаря были исключены 10 дней (5–14 октября).
После введения григорианского календаря многие страны продолжали использовать юлианский календарь, при этом Турция была последней страной, принявшей григорианский календарь в 1926 году. В результате поэтапного принятия переходный период для стран на момент принятия был разные даты начала и другое количество пропущенных дней для учета дополнительных расхождений при расчетах високосных дней.
В этом посте мы рассмотрим:
- Введение в классы Apple
Date
иCalendar
и то, как они работают вместе для моделирования времени. - Как управлять датами и часовыми поясами с помощью
DateComponents
(вкратце оDateFormatter
). - Как и почему вы можете использовать эти классы.
- Некоторые проблемы, с которыми вы можете столкнуться.
Примечание. Классы Date
, Calendar
и DateComponents
- это классы Swift, которые соединяются с классами Objective-C NSDate
, NSCalendar
и NSDateComponents
соответственно. В этом посте будут использоваться классы Swift, однако на момент написания документация Objective-C была более подробной, поэтому ссылки на документацию Apple будут относиться к классам Objective-C.
Как Apple моделирует время
Фреймворк Apple использует два класса для работы с концепцией времени: Date
и Calendar
.
Date
Класс Date
назван ужасно - на самом деле он не соответствует «дате», например 8 августа 2014 года. Вместо этого он представляет момент времени, определяемый временным интервалом от абсолютной ссылки. точка. Вы можете думать об этом как о количестве секунд, прошедших с (или до) этой контрольной точки.
Date
не зависит от календарных систем или часовых поясов. Чтобы сопоставить момент времени с конкретным «datetime» (комбинация даты и времени, например, 28 января 2017 г., 14:45), нам нужно ввести календарную систему.
"Календарь"
Календарь помещает вехи на временную шкалу, определяя что-то вроде «1982 года» как интервал между двумя конкретными моментами времени. Дата имеет смысл только в контексте календаря.
Apple представляет календари с классом Calendar
. Calendar
экземпляры создаются с использованием предопределенных identifiers
, объявляющих использование календарной системы. Например, Calendar(identifier: .gregorian)
даст вам григорианский календарь.
Еще больше усложняет ситуацию то, что конкретная календарная система также может быть сдвинута вперед или назад во времени в зависимости от часового пояса.
Чтобы привязать календарную систему к месту, мы должны указать часовой пояс. Согласно документации Apple:
По умолчанию
NSCalendar
использует часовой пояс по умолчанию для приложения или процесса при создании объекта календаря. Если часовой пояс по умолчанию не был установлен иным образом, это часовой пояс, установленный в Системных настройках.
Другими словами, Calendar
объекты автоматически инициализируются часовым поясом по умолчанию, и этот часовой пояс по умолчанию, скорее всего, является часовым поясом в системных настройках пользователя.
Однако вы можете изменить это значение по умолчанию, явно установив свойство Calendar
.timeZone
самостоятельно. Apple представляет часовые пояса с помощью класса TimeZone
. Подобно Calendar
, они инициализируются идентификаторами.
Совет: вы можете использовать Calendar.current
, чтобы получить текущий календарь пользователя. Согласно документации Apple:
Возвращаемый календарь формируется из настроек для выбранного пользователем языкового стандарта системы, наложенных на любые пользовательские настройки, указанные пользователем в Системных настройках.
Совет 2: вы можете просмотреть список всех TimeZone
идентификаторов, перебирая TimeZone.knownTimeZoneIdentifiers
. Я не понимаю, почему этого где-то нет в документации.
Управление датой и временем
Управление Date
якобы так же просто, как прибавление или вычитание количества секунд (также известного как TimeInterval
) к одному моменту времени, чтобы получить другой момент времени.
Однако для наших целей этой возможности зачастую недостаточно. Например, мы могли бы захотеть перейти с первого дня одного месяца на первый день следующего месяца. В зависимости от месяца и года добавляемое количество секунд будет отличаться. Вот тут-то и пригодится Calendar
. Calendar
будет знать количество секунд, необходимое для перехода от одной календарной даты и времени к другой.
С Date
и Calendar
(установлен с TimeZone
) у нас есть инструменты, необходимые для управления датами относительно единиц календарного времени (например, месяцев или лет). Мы выполняем эти задачи, переводя не зависящие от календаря Date
объекты в зависящие от календаря DateComponents
объекты и обратно.
Компоненты даты
В отличие от Date
, DateComponents
представляет именно то, что можно было ожидать. Он инкапсулирует компоненты даты, такие как год, месяц, день, час, минута и секунда, относительно Calendar
.
Мы можем переводить между Date
и DateComponents
с помощью Calendar
. После того, как Date
был разбит на компоненты, мы можем манипулировать этими компонентами, прежде чем собирать их в новый Date
.
В приведенном выше примере (Код 2) мы начинаем с произвольного Date
(строка 2) и объекта григорианского Calendar
, установленного на Нью-Йорк TimeZone
(строки с 5 по 7). В строке 10 мы создаем DateComponents
, интерпретируя Date
в контексте Calendar
. Если мы проверим эти компоненты, мы увидим, что они описывают datetime 27 сентября 2018 г., 15:34:53. В строке 13 мы берем эти компоненты и снова превращаем их в Date
.
DateComponents
также может использоваться для представления интервала времени в календарных единицах. Например, вы можете узнать, сколько календарных дней находится между двумя точками времени.
В приведенном выше примере (Код 3) мы начинаем с двух Date
и Calendar
(строки со 2 по 4). Затем мы вычисляем разницу между этими двумя датами в календарных днях (то есть количество дней в нашем Calendar
между двумя нашими Date
s). Проверка differenceInCalendarUnits
показывает, что differenceInCalendarUnits.days
равно 1157, что означает, что в нашем calendar
промежутке между date1
и date2
1157 дней.
Боковое примечание: DateFormatter
В общем, DateComponents
позволяет выполнять вычисления и изменять Date
объекты. Если вы обнаружите, что у вас есть singleDate
, который никогда не меняется, и ваша цель - просто отобразить этот Date
по отношению к другому часовому поясу, тогда класс DateFormatter
может удовлетворить ваши потребности. DateFormatter
преобразует объект Date
в представление String
(и наоборот) и позволяет вам установить для него как Calendar
, так и TimeZone
(со свойствами .calendar
и .timeZone
соответственно). Вы можете использовать DateFormatter
, чтобы отображать свой Date
как TimeZone
по вашему желанию.
Примеры использования
Вот несколько способов использования Date
, Calendar
и DateComponents
.
14:00 сегодня в Калифорнии
Допустим, вы хотите создать Date
, соответствующий 14:00 сегодняшнего дня по калифорнийскому времени.
Какой сейчас день / время в Тайбэе?
Допустим, вы хотите знать, какая сейчас дата и время в Тайбэе, Тайвань.
Альтернативно
Обратите внимание, что .dateComponents(in:, from:)
игнорирует часовой пояс календаря, для которого вызывается метод, и вместо этого использует переданный часовой пояс. Он возвращает все компоненты.
Какой день / время сегодня 14:00 в Калифорнии по времени Тайбэя?
Просто объедините два приведенных выше примера.
В это же время на следующей неделе
Допустим, вы хотите создать Date
, представляющий «это время на следующей неделе» (т.е. через неделю).
Альтернативно
На этот раз в следующий понедельник
Допустим, вы хотите создать Date
, представляющий «в этот раз в следующий понедельник» (то есть в то же время, что и сейчас, но в следующий понедельник).
Обратите внимание, что понедельник обозначен цифрой 2. Согласно документации Apple:
Единицы дня недели - это числа от 1 до n, где n - количество дней в неделе. Например, в григорианском календаре n равно 7, а воскресенье представлено 1.
Обратный отсчет до нового года
Допустим, вы хотите знать, сколько месяцев, дней, часов, минут и секунд осталось до того, как мяч упадет на Таймс-сквер (Нью-Йорк).
И более!
Класс Calendar
имеет массу других полезных функций. Я рекомендую либо проверить документацию, либо просто ввести .
после экземпляра календаря и позволить автозаполнению показать вам все забавные вещи, которые вы можете сделать.
Исправление проблем
Перевод с Date на DateComponents и обратно приводит к другой дате
Если вы попытаетесь создать экземпляр Date
из неполного DateComponents
объекта, Apple попытается заполнить пробелы. Например, если ваш DateComponents
указывает только час и минуту, Apple по умолчанию установит месяц и день на 1 января.
Возможно, когда вы разложили свой Date
на DateComponents
, вы указали только определенные компоненты для возврата (например, .hour
и .minute
). Если в этих компонентах недостаточно информации, когда вы пытаетесь превратить эти DateComponents
обратно в Date
, Apple заполняет недостающую информацию своими значениями по умолчанию, что приводит к другому Date
.
При разложении Date
на DateComponents
попробуйте указать как минимум компоненты .year
, .month
, .day
, .hour
, .minute
, .second
и .nanosecond
. Это должно дать вам достаточно информации, чтобы перекомпоновать ваш оригинальный Date
.
Закрытие
В итоге:
Date
представляет собой абсолютный момент времени.Calendar
определяет вехи (например, год, месяц и день) на временной шкале. Он закреплен на временной шкале с помощьюTimeZone
.- Используя
Calendar
, вы можете разложитьDate
наDateComponents
и перекомпоноватьDateComponents
вDate
. DateComponents
позволяют вам манипулироватьDate
объектами, используя календарные единицы (например, месяцы, дни).
Надеюсь, теперь вы можете управлять датой и временем по своему усмотрению! В следующий раз, когда кто-то упомянет часовые пояса, ваши глаза могут немного меньше подергиваться.