add all api to sort by month
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
//
|
||||
// SwiftDate
|
||||
// Parse, validate, manipulate, and display dates, time and timezones in Swift
|
||||
//
|
||||
// Created by Daniele Margutti
|
||||
// - Web: https://www.danielemargutti.com
|
||||
// - Twitter: https://twitter.com/danielemargutti
|
||||
// - Mail: hello@danielemargutti.com
|
||||
//
|
||||
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - Date Components Extensions
|
||||
|
||||
public extension Calendar.Component {
|
||||
|
||||
/// Return a description of the calendar component in seconds.
|
||||
/// Note: Values for `era`,`weekday`,`weekdayOrdinal`, `yearForWeekOfYear`, `calendar`, `timezone` are `nil`.
|
||||
/// For `weekOfYear` it return the same value of `weekOfMonth`.
|
||||
var timeInterval: Double? {
|
||||
switch self {
|
||||
case .era: return nil
|
||||
case .year: return (Calendar.Component.day.timeInterval! * 365.0)
|
||||
case .month: return (Calendar.Component.minute.timeInterval! * 43800)
|
||||
case .day: return 86400
|
||||
case .hour: return 3600
|
||||
case .minute: return 60
|
||||
case .second: return 1
|
||||
case .quarter: return (Calendar.Component.day.timeInterval! * 91.25)
|
||||
case .weekOfMonth, .weekOfYear: return (Calendar.Component.day.timeInterval! * 7)
|
||||
case .nanosecond: return 1e-9
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the localized identifier of a calendar component
|
||||
///
|
||||
/// - parameter unit: unit
|
||||
/// - parameter value: value
|
||||
///
|
||||
/// - returns: return the plural or singular form of the time unit used to compose a valid identifier for search a localized
|
||||
/// string in resource bundle
|
||||
internal func localizedKey(forValue value: Int) -> String {
|
||||
let locKey = localizedKey
|
||||
let absValue = abs(value)
|
||||
switch absValue {
|
||||
case 0: // zero difference for this unit
|
||||
return "0\(locKey)"
|
||||
case 1: // one unit of difference
|
||||
return locKey
|
||||
default: // more than 1 unit of difference
|
||||
return "\(locKey)\(locKey)"
|
||||
}
|
||||
}
|
||||
|
||||
internal var localizedKey: String {
|
||||
switch self {
|
||||
case .year: return "y"
|
||||
case .month: return "m"
|
||||
case .weekOfYear: return "w"
|
||||
case .day: return "d"
|
||||
case .hour: return "h"
|
||||
case .minute: return "M"
|
||||
case .second: return "s"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public extension DateComponents {
|
||||
|
||||
/// Shortcut for 'all calendar components'.
|
||||
static var allComponentsSet: Set<Calendar.Component> {
|
||||
return [.era, .year, .month, .day, .hour, .minute,
|
||||
.second, .weekday, .weekdayOrdinal, .quarter,
|
||||
.weekOfMonth, .weekOfYear, .yearForWeekOfYear,
|
||||
.nanosecond, .calendar, .timeZone]
|
||||
}
|
||||
|
||||
internal static let allComponents: [Calendar.Component] = [.nanosecond, .second, .minute, .hour,
|
||||
.day, .month, .year, .yearForWeekOfYear,
|
||||
.weekOfYear, .weekday, .quarter, .weekdayOrdinal,
|
||||
.weekOfMonth]
|
||||
|
||||
/// This function return the absolute amount of seconds described by the components of the receiver.
|
||||
/// Note: evaluated value maybe not strictly exact because it ignore the context (calendar/date) of
|
||||
/// the date components. In details:
|
||||
/// - The following keys are ignored: `era`,`weekday`,`weekdayOrdinal`,
|
||||
/// `weekOfYear`, `yearForWeekOfYear`, `calendar`, `timezone
|
||||
///
|
||||
/// Some other values dependant from dates are fixed. This is a complete table:
|
||||
/// - `year` is 365.0 `days`
|
||||
/// - `month` is 30.4167 `days` (or 43800 minutes)
|
||||
/// - `quarter` is 91.25 `days`
|
||||
/// - `weekOfMonth` is 7 `days`
|
||||
/// - `day` is 86400 `seconds`
|
||||
/// - `hour` is 3600 `seconds`
|
||||
/// - `minute` is 60 `seconds`
|
||||
/// - `nanosecond` is 1e-9 `seconds`
|
||||
var timeInterval: TimeInterval {
|
||||
var totalAmount: TimeInterval = 0
|
||||
DateComponents.allComponents.forEach {
|
||||
if let multipler = $0.timeInterval, let value = value(for: $0), value != Int(NSDateComponentUndefined) {
|
||||
totalAmount += (TimeInterval(value) * multipler)
|
||||
}
|
||||
}
|
||||
return totalAmount
|
||||
}
|
||||
|
||||
/// Create a new `DateComponents` instance with builder pattern.
|
||||
///
|
||||
/// - Parameter builder: callback for builder
|
||||
/// - Returns: new instance
|
||||
static func create(_ builder: ((inout DateComponents) -> Void)) -> DateComponents {
|
||||
var components = DateComponents()
|
||||
builder(&components)
|
||||
return components
|
||||
}
|
||||
|
||||
/// Return the current date plus the receive's interval
|
||||
/// The default calendar used is the `SwiftDate.defaultRegion`'s calendar.
|
||||
var fromNow: Date {
|
||||
return SwiftDate.defaultRegion.calendar.date(byAdding: (self as DateComponents) as DateComponents, to: Date() as Date)!
|
||||
}
|
||||
|
||||
/// Returns the current date minus the receiver's interval
|
||||
/// The default calendar used is the `SwiftDate.defaultRegion`'s calendar.
|
||||
var ago: Date {
|
||||
return SwiftDate.defaultRegion.calendar.date(byAdding: -self as DateComponents, to: Date())!
|
||||
}
|
||||
|
||||
/// - returns: the date that will occur once the receiver's components pass after the provide date.
|
||||
func from(_ date: DateRepresentable) -> Date? {
|
||||
return date.calendar.date(byAdding: self, to: date.date)
|
||||
}
|
||||
|
||||
/// Return `true` if all interval components are zeroes
|
||||
var isZero: Bool {
|
||||
for component in DateComponents.allComponents {
|
||||
if let value = value(for: component), value != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/// Transform a `DateComponents` instance to a dictionary where key is the `Calendar.Component` and value is the
|
||||
/// value associated.
|
||||
///
|
||||
/// - returns: a new `[Calendar.Component : Int]` dict representing source `DateComponents` instance
|
||||
internal func toDict() -> [Calendar.Component: Int] {
|
||||
var list: [Calendar.Component: Int] = [:]
|
||||
DateComponents.allComponents.forEach { component in
|
||||
let value = self.value(for: component)
|
||||
if value != nil && value != Int(NSDateComponentUndefined) {
|
||||
list[component] = value!
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
/// Alter date components specified into passed dictionary.
|
||||
///
|
||||
/// - Parameter components: components dictionary with their values.
|
||||
internal mutating func alterComponents(_ components: [Calendar.Component: Int?]) {
|
||||
components.forEach {
|
||||
if let v = $0.value {
|
||||
setValue(v, for: $0.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds two NSDateComponents and returns their combined individual components.
|
||||
static func + (lhs: DateComponents, rhs: DateComponents) -> DateComponents {
|
||||
return combine(lhs, rhs: rhs, transform: +)
|
||||
}
|
||||
|
||||
/// Subtracts two NSDateComponents and returns the relative difference between them.
|
||||
static func - (lhs: DateComponents, rhs: DateComponents) -> DateComponents {
|
||||
return lhs + (-rhs)
|
||||
}
|
||||
|
||||
/// Applies the `transform` to the two `T` provided, defaulting either of them if it's
|
||||
/// `nil`
|
||||
internal static func bimap<T>(_ a: T?, _ b: T?, default: T, _ transform: (T, T) -> T) -> T? {
|
||||
if a == nil && b == nil { return nil }
|
||||
return transform(a ?? `default`, b ?? `default`)
|
||||
}
|
||||
|
||||
/// - returns: a new `NSDateComponents` that represents the negative of all values within the
|
||||
/// components that are not `NSDateComponentUndefined`.
|
||||
static prefix func - (rhs: DateComponents) -> DateComponents {
|
||||
var components = DateComponents()
|
||||
components.era = rhs.era.map(-)
|
||||
components.year = rhs.year.map(-)
|
||||
components.month = rhs.month.map(-)
|
||||
components.day = rhs.day.map(-)
|
||||
components.hour = rhs.hour.map(-)
|
||||
components.minute = rhs.minute.map(-)
|
||||
components.second = rhs.second.map(-)
|
||||
components.nanosecond = rhs.nanosecond.map(-)
|
||||
components.weekday = rhs.weekday.map(-)
|
||||
components.weekdayOrdinal = rhs.weekdayOrdinal.map(-)
|
||||
components.quarter = rhs.quarter.map(-)
|
||||
components.weekOfMonth = rhs.weekOfMonth.map(-)
|
||||
components.weekOfYear = rhs.weekOfYear.map(-)
|
||||
components.yearForWeekOfYear = rhs.yearForWeekOfYear.map(-)
|
||||
return components
|
||||
}
|
||||
|
||||
/// Combines two date components using the provided `transform` on all
|
||||
/// values within the components that are not `NSDateComponentUndefined`.
|
||||
private static func combine(_ lhs: DateComponents, rhs: DateComponents, transform: (Int, Int) -> Int) -> DateComponents {
|
||||
var components = DateComponents()
|
||||
components.era = bimap(lhs.era, rhs.era, default: 0, transform)
|
||||
components.year = bimap(lhs.year, rhs.year, default: 0, transform)
|
||||
components.month = bimap(lhs.month, rhs.month, default: 0, transform)
|
||||
components.day = bimap(lhs.day, rhs.day, default: 0, transform)
|
||||
components.hour = bimap(lhs.hour, rhs.hour, default: 0, transform)
|
||||
components.minute = bimap(lhs.minute, rhs.minute, default: 0, transform)
|
||||
components.second = bimap(lhs.second, rhs.second, default: 0, transform)
|
||||
components.nanosecond = bimap(lhs.nanosecond, rhs.nanosecond, default: 0, transform)
|
||||
components.weekday = bimap(lhs.weekday, rhs.weekday, default: 0, transform)
|
||||
components.weekdayOrdinal = bimap(lhs.weekdayOrdinal, rhs.weekdayOrdinal, default: 0, transform)
|
||||
components.quarter = bimap(lhs.quarter, rhs.quarter, default: 0, transform)
|
||||
components.weekOfMonth = bimap(lhs.weekOfMonth, rhs.weekOfMonth, default: 0, transform)
|
||||
components.weekOfYear = bimap(lhs.weekOfYear, rhs.weekOfYear, default: 0, transform)
|
||||
components.yearForWeekOfYear = bimap(lhs.yearForWeekOfYear, rhs.yearForWeekOfYear, default: 0, transform)
|
||||
return components
|
||||
}
|
||||
|
||||
/// Subscription support for `DateComponents` instances.
|
||||
/// ie. `cmps[.day] = 5`
|
||||
///
|
||||
/// Note: This does not take into account any built-in errors, `Int.max` returned instead of `nil`.
|
||||
///
|
||||
/// - Parameter component: component to get
|
||||
subscript(component: Calendar.Component) -> Int? {
|
||||
switch component {
|
||||
case .era: return era
|
||||
case .year: return year
|
||||
case .month: return month
|
||||
case .day: return day
|
||||
case .hour: return hour
|
||||
case .minute: return minute
|
||||
case .second: return second
|
||||
case .weekday: return weekday
|
||||
case .weekdayOrdinal: return weekdayOrdinal
|
||||
case .quarter: return quarter
|
||||
case .weekOfMonth: return weekOfMonth
|
||||
case .weekOfYear: return weekOfYear
|
||||
case .yearForWeekOfYear: return yearForWeekOfYear
|
||||
case .nanosecond: return nanosecond
|
||||
default: return nil // `calendar` and `timezone` are ignored in this context
|
||||
}
|
||||
}
|
||||
|
||||
/// Express a `DateComponents` instance in another time unit you choose.
|
||||
///
|
||||
/// - parameter component: time component
|
||||
/// - parameter calendar: context calendar to use
|
||||
///
|
||||
/// - returns: the value of interval expressed in selected `Calendar.Component`
|
||||
func `in`(_ component: Calendar.Component, of calendar: CalendarConvertible? = nil) -> Int? {
|
||||
let cal = (calendar?.toCalendar() ?? SwiftDate.defaultRegion.calendar)
|
||||
let dateFrom = Date()
|
||||
let dateTo = (dateFrom + self)
|
||||
let components: Set<Calendar.Component> = [component]
|
||||
let value = cal.dateComponents(components, from: dateFrom, to: dateTo).value(for: component)
|
||||
return value
|
||||
}
|
||||
|
||||
/// Express a `DateComponents` instance in a set of time units you choose.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - component: time component
|
||||
/// - calendar: context calendar to use
|
||||
/// - Returns: a dictionary of extract values.
|
||||
func `in`(_ components: Set<Calendar.Component>, of calendar: CalendarConvertible? = nil) -> [Calendar.Component: Int] {
|
||||
let cal = (calendar?.toCalendar() ?? SwiftDate.defaultRegion.calendar)
|
||||
let dateFrom = Date()
|
||||
let dateTo = (dateFrom + self)
|
||||
let extractedCmps = cal.dateComponents(components, from: dateFrom, to: dateTo)
|
||||
return extractedCmps.toDict()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//
|
||||
// SwiftDate
|
||||
// Parse, validate, manipulate, and display dates, time and timezones in Swift
|
||||
//
|
||||
// Created by Daniele Margutti
|
||||
// - Web: https://www.danielemargutti.com
|
||||
// - Twitter: https://twitter.com/danielemargutti
|
||||
// - Mail: hello@danielemargutti.com
|
||||
//
|
||||
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: Int Extension
|
||||
|
||||
/// This allows us to transform a literal number in a `DateComponents` and use it in math operations
|
||||
/// For example `5.days` will create a new `DateComponents` where `.day = 5`.
|
||||
|
||||
public extension Int {
|
||||
|
||||
/// Internal transformation function
|
||||
///
|
||||
/// - parameter type: component to use
|
||||
///
|
||||
/// - returns: return self value in form of `DateComponents` where given `Calendar.Component` has `self` as value
|
||||
internal func toDateComponents(type: Calendar.Component) -> DateComponents {
|
||||
var dateComponents = DateComponents()
|
||||
dateComponents.setValue(self, for: type)
|
||||
return dateComponents
|
||||
}
|
||||
|
||||
/// Create a `DateComponents` with `self` value set as nanoseconds
|
||||
var nanoseconds: DateComponents {
|
||||
return toDateComponents(type: .nanosecond)
|
||||
}
|
||||
|
||||
/// Create a `DateComponents` with `self` value set as seconds
|
||||
var seconds: DateComponents {
|
||||
return toDateComponents(type: .second)
|
||||
}
|
||||
|
||||
/// Create a `DateComponents` with `self` value set as minutes
|
||||
var minutes: DateComponents {
|
||||
return toDateComponents(type: .minute)
|
||||
}
|
||||
|
||||
/// Create a `DateComponents` with `self` value set as hours
|
||||
var hours: DateComponents {
|
||||
return toDateComponents(type: .hour)
|
||||
}
|
||||
|
||||
/// Create a `DateComponents` with `self` value set as days
|
||||
var days: DateComponents {
|
||||
return toDateComponents(type: .day)
|
||||
}
|
||||
|
||||
/// Create a `DateComponents` with `self` value set as weeks
|
||||
var weeks: DateComponents {
|
||||
return toDateComponents(type: .weekOfYear)
|
||||
}
|
||||
|
||||
/// Create a `DateComponents` with `self` value set as months
|
||||
var months: DateComponents {
|
||||
return toDateComponents(type: .month)
|
||||
}
|
||||
|
||||
/// Create a `DateComponents` with `self` value set as years
|
||||
var years: DateComponents {
|
||||
return toDateComponents(type: .year)
|
||||
}
|
||||
|
||||
/// Create a `DateComponents` with `self` value set as quarters
|
||||
var quarters: DateComponents {
|
||||
return toDateComponents(type: .quarter)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
//
|
||||
// SwiftDate
|
||||
// Parse, validate, manipulate, and display dates, time and timezones in Swift
|
||||
//
|
||||
// Created by Daniele Margutti
|
||||
// - Web: https://www.danielemargutti.com
|
||||
// - Twitter: https://twitter.com/danielemargutti
|
||||
// - Mail: hello@danielemargutti.com
|
||||
//
|
||||
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MARK: - DataParsable Protocol
|
||||
|
||||
public protocol DateParsable {
|
||||
|
||||
/// Convert a string to a `DateInRegion` instance by parsing it with given parser
|
||||
/// or using one of the built-in parser (if you know the format of the date you
|
||||
/// should consider explicitly pass it to avoid unecessary computations).
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - format: format of the date, `nil` to leave the library to found the best
|
||||
/// one via `SwiftDate.autoFormats`
|
||||
/// - region: region in which the date should be expressed in.
|
||||
/// Region's locale is used to format the date when using long readable unit names (like MMM
|
||||
/// for month).
|
||||
/// - Returns: date in region representation, `nil` if parse fails
|
||||
func toDate(_ format: String?, region: Region) -> DateInRegion?
|
||||
|
||||
/// Convert a string to a `DateInRegion` instance by parsing it with the ordered
|
||||
/// list of provided formats.
|
||||
/// If `formats` array is not provided it uses the `SwiftDate.autoFormats` array instead.
|
||||
/// Note: if you knwo the format of the date you should consider explicitly pass it to avoid
|
||||
/// unecessary computations.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - format: ordered formats to parse date (if you don't have a list of formats you can pass `SwiftDate.autoFormats`)
|
||||
/// - region: region in which the date should be expressed in.
|
||||
/// Region's locale is used to format the date when using long readable unit names (like MMM
|
||||
/// for month).
|
||||
/// - Returns: date in region representation, `nil` if parse fails
|
||||
func toDate(_ formats: [String], region: Region) -> DateInRegion?
|
||||
|
||||
/// Convert a string to a valid `DateInRegion` using passed style.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - style: parsing style.
|
||||
/// - region: region in which the date should be expressed in
|
||||
/// - Returns: date in region representation, `nil` if parse fails
|
||||
func toDate(style: StringToDateStyles, region: Region) -> DateInRegion?
|
||||
|
||||
/// Convert to date from a valid ISO8601 string
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - options: options of the parser
|
||||
/// - region: region in which the date should be expressed in (timzone is ignored and evaluated automatically)
|
||||
/// - Returns: date in region representation, `nil` if parse fails
|
||||
func toISODate(_ options: ISOParser.Options?, region: Region?) -> DateInRegion?
|
||||
|
||||
/// Convert to date from a valid DOTNET string
|
||||
///
|
||||
/// - region: region in which the date should be expressed in (timzone is ignored and evaluated automatically)
|
||||
/// - Returns: date in region representation, `nil` if parse fails
|
||||
func toDotNETDate(region: Region) -> DateInRegion?
|
||||
|
||||
/// Convert to a date from a valid RSS/ALT RSS string
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - alt: `true` if string represent an ALT RSS formatted date, `false` if a standard RSS formatted date.
|
||||
/// - region: region in which the date should be expressed in (timzone is ignored and evaluated automatically)
|
||||
/// - Returns: date in region representation, `nil` if parse fails
|
||||
func toRSSDate(alt: Bool, region: Region) -> DateInRegion?
|
||||
|
||||
/// Convert to a date from a valid SQL format string.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - region: region in which the date should be expressed in (timzone is ignored and evaluated automatically)
|
||||
/// - Returns: date in region representation, `nil` if parse fails
|
||||
func toSQLDate(region: Region) -> DateInRegion?
|
||||
|
||||
}
|
||||
|
||||
// MARK: - DataParsable Implementation for Strings
|
||||
|
||||
extension String: DateParsable {
|
||||
|
||||
public func toDate(_ format: String? = nil, region: Region = SwiftDate.defaultRegion) -> DateInRegion? {
|
||||
return DateInRegion(self, format: format, region: region)
|
||||
}
|
||||
|
||||
public func toDate(_ formats: [String], region: Region) -> DateInRegion? {
|
||||
return DateInRegion(self, formats: formats, region: region)
|
||||
}
|
||||
|
||||
public func toDate(style: StringToDateStyles, region: Region = SwiftDate.defaultRegion) -> DateInRegion? {
|
||||
return style.toDate(self, region: region)
|
||||
}
|
||||
|
||||
public func toISODate(_ options: ISOParser.Options? = nil, region: Region? = nil) -> DateInRegion? {
|
||||
return ISOParser.parse(self, region: region, options: options)
|
||||
}
|
||||
|
||||
public func toDotNETDate(region: Region = Region.ISO) -> DateInRegion? {
|
||||
return DOTNETParser.parse(self, region: region, options: nil)
|
||||
}
|
||||
|
||||
public func toRSSDate(alt: Bool, region: Region = Region.ISO) -> DateInRegion? {
|
||||
switch alt {
|
||||
case true: return StringToDateStyles.altRSS.toDate(self, region: region)
|
||||
case false: return StringToDateStyles.rss.toDate(self, region: region)
|
||||
}
|
||||
}
|
||||
|
||||
public func toSQLDate(region: Region = Region.ISO) -> DateInRegion? {
|
||||
return StringToDateStyles.sql.toDate(self, region: region)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// SwiftDate
|
||||
// Parse, validate, manipulate, and display dates, time and timezones in Swift
|
||||
//
|
||||
// Created by Daniele Margutti
|
||||
// - Web: https://www.danielemargutti.com
|
||||
// - Twitter: https://twitter.com/danielemargutti
|
||||
// - Mail: hello@danielemargutti.com
|
||||
//
|
||||
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public extension TimeInterval {
|
||||
|
||||
struct ComponentsFormatterOptions {
|
||||
|
||||
/// Fractional units may be used when a value cannot be exactly represented using the available units.
|
||||
/// For example, if minutes are not allowed, the value “1h 30m” could be formatted as “1.5h”.
|
||||
public var allowsFractionalUnits: Bool?
|
||||
|
||||
/// Specify the units that can be used in the output.
|
||||
public var allowedUnits: NSCalendar.Unit?
|
||||
|
||||
/// A Boolean value indicating whether to collapse the largest unit into smaller units when a certain threshold is met.
|
||||
public var collapsesLargestUnit: Bool?
|
||||
|
||||
/// The maximum number of time units to include in the output string.
|
||||
/// If 0 does not cause the elimination of any units.
|
||||
public var maximumUnitCount: Int?
|
||||
|
||||
/// The formatting style for units whose value is 0.
|
||||
public var zeroFormattingBehavior: DateComponentsFormatter.ZeroFormattingBehavior?
|
||||
|
||||
/// The preferred style for units.
|
||||
public var unitsStyle: DateComponentsFormatter.UnitsStyle?
|
||||
|
||||
/// Locale of the formatter
|
||||
public var locale: LocaleConvertible? {
|
||||
set { calendar.locale = newValue?.toLocale() }
|
||||
get { return calendar.locale }
|
||||
}
|
||||
|
||||
/// Calendar
|
||||
public var calendar = Calendar.autoupdatingCurrent
|
||||
|
||||
public func apply(toFormatter formatter: DateComponentsFormatter) {
|
||||
formatter.calendar = calendar
|
||||
|
||||
if let allowsFractionalUnits = self.allowsFractionalUnits {
|
||||
formatter.allowsFractionalUnits = allowsFractionalUnits
|
||||
}
|
||||
if let allowedUnits = self.allowedUnits {
|
||||
formatter.allowedUnits = allowedUnits
|
||||
}
|
||||
if let collapsesLargestUnit = self.collapsesLargestUnit {
|
||||
formatter.collapsesLargestUnit = collapsesLargestUnit
|
||||
}
|
||||
if let maximumUnitCount = self.maximumUnitCount {
|
||||
formatter.maximumUnitCount = maximumUnitCount
|
||||
}
|
||||
if let zeroFormattingBehavior = self.zeroFormattingBehavior {
|
||||
formatter.zeroFormattingBehavior = zeroFormattingBehavior
|
||||
}
|
||||
if let unitsStyle = self.unitsStyle {
|
||||
formatter.unitsStyle = unitsStyle
|
||||
}
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
/// Return the local thread shared formatter for date components
|
||||
private static func sharedFormatter() -> DateComponentsFormatter {
|
||||
let name = "SwiftDate_\(NSStringFromClass(DateComponentsFormatter.self))"
|
||||
return threadSharedObject(key: name, create: {
|
||||
let formatter = DateComponentsFormatter()
|
||||
formatter.includesApproximationPhrase = false
|
||||
formatter.includesTimeRemainingPhrase = false
|
||||
return formatter
|
||||
})
|
||||
}
|
||||
|
||||
//@available(*, deprecated: 5.0.13, obsoleted: 5.1, message: "Use toIntervalString function instead")
|
||||
func toString(options callback: ((inout ComponentsFormatterOptions) -> Void)? = nil) -> String {
|
||||
return self.toIntervalString(options: callback)
|
||||
}
|
||||
|
||||
/// Format a time interval in a string with desidered components with passed style.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - units: units to include in string.
|
||||
/// - style: style of the units, by default is `.abbreviated`
|
||||
/// - Returns: string representation
|
||||
func toIntervalString(options callback: ((inout ComponentsFormatterOptions) -> Void)? = nil) -> String {
|
||||
let formatter = DateComponentsFormatter()
|
||||
var options = ComponentsFormatterOptions()
|
||||
callback?(&options)
|
||||
options.apply(toFormatter: formatter)
|
||||
|
||||
let formattedValue = formatter.string(from: self)!
|
||||
if options.zeroFormattingBehavior?.contains(.pad) ?? false {
|
||||
// for some strange reason padding is not added at the very beginning positional item.
|
||||
// we'll add it manually if necessaru
|
||||
if let index = formattedValue.firstIndex(of: ":"), index.utf16Offset(in: formattedValue) < 2 {
|
||||
return "0\(formattedValue)"
|
||||
}
|
||||
}
|
||||
return formattedValue
|
||||
}
|
||||
|
||||
/// Format a time interval in a string with desidered components with passed style.
|
||||
///
|
||||
/// - Parameter options: options for formatting.
|
||||
/// - Returns: string representation
|
||||
func toString(options: ComponentsFormatterOptions) -> String {
|
||||
let formatter = TimeInterval.sharedFormatter()
|
||||
options.apply(toFormatter: formatter)
|
||||
return (formatter.string(from: self) ?? "")
|
||||
}
|
||||
|
||||
/// Return a string representation of the time interval in form of clock countdown (ie. 57:00:00)
|
||||
///
|
||||
/// - Parameter zero: behaviour with zero.
|
||||
/// - Returns: string representation
|
||||
func toClock(zero: DateComponentsFormatter.ZeroFormattingBehavior = [.pad, .dropLeading]) -> String {
|
||||
return toIntervalString(options: {
|
||||
$0.collapsesLargestUnit = true
|
||||
$0.maximumUnitCount = 0
|
||||
$0.unitsStyle = .positional
|
||||
$0.locale = Locales.englishUnitedStatesComputer
|
||||
$0.zeroFormattingBehavior = zero
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract requeste time units components from given interval.
|
||||
/// Reference date's calendar is used to make the extraction.
|
||||
///
|
||||
/// NOTE:
|
||||
/// Extraction is calendar/date based; if you specify a `refDate` calculation is made
|
||||
/// between the `refDate` and `refDate + interval`.
|
||||
/// If `refDate` is `nil` evaluation is made from `now()` and `now() + interval` in the context
|
||||
/// of the `SwiftDate.defaultRegion` set.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - units: units to extract
|
||||
/// - from: starting reference date, `nil` means `now()` in the context of the default region set.
|
||||
/// - Returns: dictionary with extracted components
|
||||
func toUnits(_ units: Set<Calendar.Component>, to refDate: DateInRegion? = nil) -> [Calendar.Component: Int] {
|
||||
let dateTo = (refDate ?? DateInRegion())
|
||||
let dateFrom = dateTo.addingTimeInterval(-self)
|
||||
let components = dateFrom.calendar.dateComponents(units, from: dateFrom.date, to: dateTo.date)
|
||||
return components.toDict()
|
||||
}
|
||||
|
||||
/// Express a time interval (expressed in seconds) in another time unit you choose.
|
||||
/// Reference date's calendar is used to make the extraction.
|
||||
///
|
||||
/// - parameter component: time unit in which you want to express the calendar component
|
||||
/// - parameter from: starting reference date, `nil` means `now()` in the context of the default region set.
|
||||
///
|
||||
/// - returns: the value of interval expressed in selected `Calendar.Component`
|
||||
func toUnit(_ component: Calendar.Component, to refDate: DateInRegion? = nil) -> Int? {
|
||||
return toUnits([component], to: refDate)[component]
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user