// // 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 Date { // MARK: - Comparing Close /// Decides whether a Date is "close by" another one passed in parameter, /// where "Being close" is measured using a precision argument /// which is initialized a 300 seconds, or 5 minutes. /// /// - Parameters: /// - refDate: reference date compare against to. /// - precision: The precision of the comparison (default is 5 minutes, or 300 seconds). /// - Returns: A boolean; true if close by, false otherwise. func compareCloseTo(_ refDate: Date, precision: TimeInterval = 300) -> Bool { return (abs(timeIntervalSince(refDate)) < precision) } // MARK: - Extendend Compare /// Compare the date with the rule specified in the `compareType` parameter. /// /// - Parameter compareType: comparison type. /// - Returns: `true` if comparison succeded, `false` otherwise func compare(_ compareType: DateComparisonType) -> Bool { return inDefaultRegion().compare(compareType) } /// Returns a ComparisonResult value that indicates the ordering of two given dates based on /// their components down to a given unit granularity. /// /// - parameter date: date to compare. /// - parameter granularity: The smallest unit that must, along with all larger units be less for the given dates /// - returns: `ComparisonResult` func compare(toDate refDate: Date, granularity: Calendar.Component) -> ComparisonResult { return inDefaultRegion().compare(toDate: refDate.inDefaultRegion(), granularity: granularity) } /// Compares whether the receiver is before/before equal `date` based on their components down to a given unit granularity. /// /// - Parameters: /// - refDate: reference date /// - orEqual: `true` to also check for equality /// - granularity: smallest unit that must, along with all larger units, be less for the given dates /// - Returns: Boolean func isBeforeDate(_ refDate: Date, orEqual: Bool = false, granularity: Calendar.Component) -> Bool { return inDefaultRegion().isBeforeDate(refDate.inDefaultRegion(), orEqual: orEqual, granularity: granularity) } /// Compares whether the receiver is after `date` based on their components down to a given unit granularity. /// /// - Parameters: /// - refDate: reference date /// - orEqual: `true` to also check for equality /// - granularity: Smallest unit that must, along with all larger units, be greater for the given dates. /// - Returns: Boolean func isAfterDate(_ refDate: Date, orEqual: Bool = false, granularity: Calendar.Component) -> Bool { return inDefaultRegion().isAfterDate(refDate.inDefaultRegion(), orEqual: orEqual, granularity: granularity) } /// Return true if receiver date is contained in the range specified by two dates. /// /// - Parameters: /// - startDate: range upper bound date /// - endDate: range lower bound date /// - orEqual: `true` to also check for equality on date and date2 /// - granularity: smallest unit that must, along with all larger units, be greater for the given dates. /// - Returns: Boolean func isInRange(date startDate: Date, and endDate: Date, orEqual: Bool = false, granularity: Calendar.Component = .nanosecond) -> Bool { return self.inDefaultRegion().isInRange(date: startDate.inDefaultRegion(), and: endDate.inDefaultRegion(), orEqual: orEqual, granularity: granularity) } /// Compares equality of two given dates based on their components down to a given unit /// granularity. /// /// - parameter date: date to compare /// - parameter granularity: The smallest unit that must, along with all larger units, be equal for the given /// dates to be considered the same. /// /// - returns: `true` if the dates are the same down to the given granularity, otherwise `false` func isInside(date: Date, granularity: Calendar.Component) -> Bool { return (compare(toDate: date, granularity: granularity) == .orderedSame) } // MARK: - Date Earlier/Later /// Return the earlier of two dates, between self and a given date. /// /// - Parameter date: The date to compare to self /// - Returns: The date that is earlier func earlierDate(_ date: Date) -> Date { return timeIntervalSince(date) <= 0 ? self : date } /// Return the later of two dates, between self and a given date. /// /// - Parameter date: The date to compare to self /// - Returns: The date that is later func laterDate(_ date: Date) -> Date { return timeIntervalSince(date) >= 0 ? self : date } } extension Date { /// Returns the difference in the calendar component given (like day, month or year) /// with respect to the other date as a positive integer public func difference(in component: Calendar.Component, from other: Date) -> Int? { let (max, min) = orderDate(with: other) let result = calendar.dateComponents([component], from: min, to: max) return getValue(of: component, from: result) } /// Returns the differences in the calendar components given (like day, month and year) /// with respect to the other date as dictionary with the calendar component as the key /// and the diffrence as a positive integer as the value public func differences(in components: Set, from other: Date) -> [Calendar.Component: Int] { let (max, min) = orderDate(with: other) let differenceInDates = calendar.dateComponents(components, from: min, to: max) var result = [Calendar.Component: Int]() for component in components { if let value = getValue(of: component, from: differenceInDates) { result[component] = value } } return result } private func getValue(of component: Calendar.Component, from dateComponents: DateComponents) -> Int? { switch component { case .era: return dateComponents.era case .year: return dateComponents.year case .month: return dateComponents.month case .day: return dateComponents.day case .hour: return dateComponents.hour case .minute: return dateComponents.minute case .second: return dateComponents.second case .weekday: return dateComponents.weekday case .weekdayOrdinal: return dateComponents.weekdayOrdinal case .quarter: return dateComponents.quarter case .weekOfMonth: return dateComponents.weekOfMonth case .weekOfYear: return dateComponents.weekOfYear case .yearForWeekOfYear: return dateComponents.yearForWeekOfYear case .nanosecond: return dateComponents.nanosecond case .calendar, .timeZone: return nil @unknown default: assert(false, "unknown date component") } return nil } private func orderDate(with other: Date) -> (Date, Date) { let first = self.timeIntervalSince1970 let second = other.timeIntervalSince1970 if first >= second { return (self, other) } return (other, self) } }