Мир слов и оправданий совершенен, и его невозможно подставить под удар. И поэтому мудрец избегает его. Не сходи в мир оправданий за отпущением грехов. Когда ты выходишь из области произносимого, ты не можешь быть ни в чём уверен. Усилия безумца — само общество, но только если они записаны. Мудрец может замещать один закон другим, пусть бессвязно, но утверждая, что у него есть метод. Это правда о речи, простирающаяся на все писания...
Мы уже знакомы немного с классами Date
или TimeInterval
. Попробуем разобраться с ними подробнее.
Класс дат отражает отдельную точку времени, независимую от конкретной временной зоны (или часового пояса) и календаря. По своей природе объект даты отражает собой интервал, прошедший от фиксированной даты. Date
является структурой, корреспондирующим же классом выступаем NSDate
.
Введём две важных даты:
1970
referenceDate
И то, и другое в 00:00:00 часов по UTC.Давайте поиграемся с конструкторами это системы:
let date1 = Date()
Как мы помним, это конструктор создаёт объект текущей даты.
Создадим объекты от обеих заданных дат:
let date2 = Date(timeIntervalSinceReferenceDate: 10000) // "1 Jan 2001 at 07:46"
let date3 = Date(timeIntervalSince1970: 10000) //"1 Jan 1970 at 07:46"
Также можно создать даты, считая от текущей даты и от определённой даты:
let date4 = Date(timeIntervalSinceNow: 1000000)
var date5 = Date(timeInterval: 100000, since: date3) //"2 Jan 1970 at 11:33"
Так же можно получить даты, соответствующие далёкому прошлому и будущему:
Date.distantFuture //"1 Jan 4001 at 05:00"
Date.distantPast //"30 Dec 1 at 04:02"
Как видите, этого класса хватит до 4001 года и если Вы отправитесь в прошлое, то до начала нашей эры.
Даты можно сравнивать:
date3 > date2 //false
date3 == date2 //false
date3 != date2 //true
date3 < date2 //true
date3 >= date2 //false
date3 <= date2 //true
Временные интервалы, как мы помним, выражаются в секундах
Вы можете получить интервал времени от изначальной даты, а также между ней и 1970-ым:
Date.timeIntervalSinceReferenceDate
Date.timeIntervalBetween1970AndReferenceDate //978307200
Также можно получить разницу между текущей датой и:
date2.timeIntervalSinceReferenceDate
date2.timeIntervalSince1970
date2.timeIntervalSinceNow
Вы можете складывать даты с временными интервалами, также доступны операторы составного присваивания:
date5 + 10000 //"2 Jan 1970 at 14:20"
date5 - 10000 //"2 Jan 1970 at 08:46"
date5 += 10000 //"2 Jan 1970 at 14:20"
date5 -= 10000 //"2 Jan 1970 at 11:33"
Даты можно сравнивать и с помощью функции compare(_:)
, которая вернёт объект ComparisonResult
. Сам по себе он не равен истине или лжи в Swift, однако с помощью rawValue
можно узнать результаты сравнения:
Как альтернативу можно использовать методы:
addTimeInterval(_:)
addingTimeInterval(_:)
date5.compare(date4).rawValue //-1
Вы можете создавать интервалы дат, которые отражают интервалы между датами. Вы можете использовать как обычные операторы ...
(и тому подобное), а также специальную структуру DateInterval
и корреспондирующий класс NSDateInterval
. Их можно сравнивать, находить пересечения и тому подобное.
Заметьте, что вывод с правой стороны отличается от использования свойства description
или функции print
;
date5.description //"1970-01-02 06:33:20 +0000"
print(date5) //"1970-01-02 06:33:20 +0000"
Каждая система обладает своей локализацией. Это важно для приложений, которые хотят выйти более, чем на одном языке или в одном регионе.
Для работы с локализацией используется тип Locale
(ссылочный тип - NSLocale
).
Чтобы получить текущую локализацию пользователя следует воспользоваться статическими свойствами типа current
и autoupdatingCurrent
:
import Foundation
Locale.current //en_US
let locale = Locale.autoupdatingCurrent //en_US
Их разница состоит в том, что вторая отслеживает изменение текущей локализации.
Если изменить объект, отслеживающий текущую локализацию, то он перестанет это делать.
Чтобы создать локализацию, нужно передать в неё идентификатор. Для российского региона, это ru_RU
:
Это имеет смысл для языков, используемых во многих странах и имеющих отличия в системах
let ruLocale = Locale(identifier: "ru_RU") //ru_RU
Вы можете получить все доступные коды для разных элементов, используя статические массивы типа:
//Все доступные идентификаторы
print(Locale.availableIdentifiers.count) //790
//Все доступные коды регионов
print(Locale.isoRegionCodes.count) //256
//Коды языков
print(Locale.isoLanguageCodes.count) ///594
//Коды валют
print(Locale.isoCurrencyCodes.count) //299
//Коды распространённых валют
print(Locale.commonISOCurrencyCodes.count) //161
Чтобы получить список языков, предпочитаемых пользователем, можно сделать так:
print(Locale.preferredLanguages) //["en"]
У каждого языка выделяют направление письменности и направление строк:
print(Locale.characterDirection(forLanguage: "ru"))
print(Locale.lineDirection(forLanguage: "ru"))
let arrayOfDirections: [Locale.LanguageDirection] = [
.leftToRight, .bottomToTop, .rightToLeft, .topToBottom
]
При этом возможны все направления. Для английского и русского языков такие символы пишутся слева направо, а строчки сверху вниз.
Стандартные свойства локализации:
//Идентификатор
print(ruLocale.identifier) //ru_RU
//Код региона
print(ruLocale.regionCode) //Optional(RU)
//Код яызка
print(ruLocale.languageCode) //Optional(ru)
При изучении дат мы узнали, что их объекты не зависят от календаря, а представляют собой точку во времени. Выделяют множество календарей, однако Европа и Америка чаще всего использует Григорианский календарь:
print(ruLocale.calendar) //gregorian
Вы можете получить канонические идентификаторы (то есть вида ru_RU
) из строки:
print(Locale.canonicalIdentifier(from: "ru_ru")) //ru_RU
print(Locale.canonicalLanguageIdentifier(from: "ru_ru")) //ru_RU
Методы смогут получить их даже из строк, не соответствующих нужным нам. Учтите, что обычный конструктор локализации, тоже сможет принять любую строку, что может привести потом к неожиданным последствиям.
Вы можете получить словарь с компонентами идентификатора
let components = Locale.components(fromIdentifier: "ru_RU")
for (key, value) in components {
print("\(key): \(value)")
}
//kCFLocaleLanguageCodeKey: ru
//kCFLocaleCountryCodeKey: RU
Обратите внимание на формат ключей - такой тип данных используется в чистом C и Objective-C - первая буква k
обозначает константу, а далее идёт блок наподобие перечисления CFLocale
, а далее название самой константы. Это связано с тем, что перечисления в C не умели принимать объекты, отличные от чисел, а потому требовалось вводить данные константы. Большинство высокоуровневых фреймворков в Swift умеют импортировать их в качестве кейсов перечислений, однако многие вещи остались в стиле Objective-C.
Из компонентов можно собрать идентификатор назад:
let idFromComponents = Locale.identifier(fromComponents: components) //ru_RU
В Windows используется другая система локализаций, а потому может быть полезным взаимно конвертировать их:
// AppleOS -> Windows
if let windowsLocaleCode = Locale.windowsLocaleCode(fromIdentifier: idFromComponents) {
print(windowsLocaleCode) //1049
//Windows -> AppleOS
print(Locale.identifier(fromWindowsLocaleCode: windowsLocaleCode)) //ru_RU
}
Можно получить значения основных кавычек (и главной альтернтивы им) для текущей локализации:
print(ruLocale.quotationBeginDelimiter) //"Optional("«")\n"
print(ruLocale.quotationEndDelimiter) //"Optional("»")\n"
print(ruLocale.alternateQuotationBeginDelimiter) //"Optional("„")\n"
print(ruLocale.alternateQuotationEndDelimiter) //"Optional("“")\n"
В странах так же отличаются символы для десятичного разделителя:
print(ruLocale.decimalSeparator) // ","
В России это запятая, тогда как в большинстве стран - точка.
Так же выделяют символ разделителя для разрядов в числах:
print(ruLocale.groupingSeparator) // " "
В России это пробел.
Помимо этого можно получить символ и название валюты:
print(ruLocale.currencySymbol) //"Optional("₽")\n"
let ruCurrencyCode = ruLocale.currencyCode
print(ruCurrencyCode) //Optional("RUB")
print(ruLocale.localizedString(forCurrencyCode: ruCurrencyCode!)) //"Optional("Российский рубль")\n"
Последним методом для некоторых типов данных Вы можете получить их локализованное описание.
Можно узнать, использует ли локализация метрическую систему измерений:
print(ruLocale.usesMetricSystem) //true
Можно получить набор базовых символов локализации для сравнения с:
print(ruLocale.exemplarCharacterSet)
Теперь посмотрим коды письменности:
let ruVariantCode = ruLocale.variantCode
print(ruVariantCode) //nil
print(ruLocale.localizedString(forLanguageCode: ruVariantCode ?? "")) //nil
Код вариантов позволяет узнать подвид языка. Для русского в международной системе его нет. Пример языка с вариантами - китайский, есть как китайский традиционный, так и упрощенный.
Также можно узнать код письменности:
let ruScriptCode = ruLocale.scriptCode
print(ruScriptCode) //nil
print(ruLocale.localizedString(forScriptCode: ruScriptCode ?? "")) //nil
В разных регионах меняется как порядок символов в алфавите, так и смысл сортировки. Соответствующее идентификаторы можно получить так:
let ruCollatorIdentifier = ruLocale.collatorIdentifier
print(ruCollatorIdentifier) //Optional("ru_RU")
print(ruLocale.localizedString(forCollatorIdentifier: ruCollatorIdentifier!)) //
let ruCollationIdentifier = ruLocale.collationIdentifier
print(ruCollationIdentifier) //nil
print(ruLocale.localizedString(forCollationIdentifier: ruCollationIdentifier ?? ""))
Узнать о разных тонкостях локализации можно на проекте ICU