add all api to sort by month

This commit is contained in:
Michael Simard
2020-06-14 21:46:44 -05:00
parent 6b9e9a4c35
commit 842abb0895
162 changed files with 34480 additions and 71 deletions

View File

@@ -0,0 +1,328 @@
//
// 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 protocol TimePeriodProtocol {
/// The start date for a TimePeriod representing the starting boundary of the time period
var start: DateInRegion? { get set }
/// The end date for a TimePeriod representing the ending boundary of the time period
var end: DateInRegion? { get set }
}
public extension TimePeriodProtocol {
/// Return `true` if time period has both start and end dates
var hasFiniteRange: Bool {
guard start != nil && end != nil else { return false }
return true
}
/// Return `true` if period has a start date
var hasStart: Bool {
return (start != nil)
}
/// Return `true` if period has a end date
var hasEnd: Bool {
return (end != nil)
}
/// Check if receiver is equal to given period (both start/end groups are equals)
///
/// - Parameter period: period to compare against to.
/// - Returns: true if are equals
func equals(_ period: TimePeriodProtocol) -> Bool {
return (start == period.start && end == period.end)
}
/// If the given `TimePeriod`'s beginning is before `beginning` and
/// if the given 'TimePeriod`'s end is after `end`.
///
/// - Parameter period: The time period to compare to self
/// - Returns: True if self is inside of the given `TimePeriod`
func isInside(_ period: TimePeriodProtocol) -> Bool {
guard hasFiniteRange, period.hasFiniteRange else { return false }
return (period.start! <= start! && period.end! >= end!)
}
/// If the given Date is after `beginning` and before `end`.
///
/// - Parameters:
/// - date: The time period to compare to self
/// - interval: Whether the edge of the date is included in the calculation
/// - Returns: True if the given `TimePeriod` is inside of self
func contains(date: DateInRegion, interval: IntervalType = .closed) -> Bool {
guard hasFiniteRange else { return false }
switch interval {
case .closed: return (start! <= date && end! >= date)
case .open: return (start! < date && end! > date)
}
}
/// If the given `TimePeriod`'s beginning is after `beginning` and
/// if the given 'TimePeriod`'s after is after `end`.
///
/// - Parameter period: The time period to compare to self
/// - Returns: True if the given `TimePeriod` is inside of self
func contains(_ period: TimePeriodProtocol) -> Bool {
guard hasFiniteRange, period.hasFiniteRange else { return false }
if period.start! < start! && period.end! > start! {
return true // Outside -> Inside
} else if period.start! >= start! && period.end! <= end! {
return true // Enclosing
} else if period.start! < end! && period.end! > end! {
return true // Inside -> Out
}
return false
}
/// If self and the given `TimePeriod` share any sub-`TimePeriod`.
///
/// - Parameter period: The time period to compare to self
/// - Returns: True if there is a period of time that is shared by both `TimePeriod`s
func overlaps(with period: TimePeriodProtocol) -> Bool {
if period.start! < start! && period.end! > start! {
return true // Outside -> Inside
} else if period.start! >= start! && period.end! <= end! {
return true // Enclosing
} else if period.start! < end! && period.end! > end! {
return true // Inside -> Out
}
return false
}
/// If self and the given `TimePeriod` overlap or the period's edges touch.
///
/// - Parameter period: The time period to compare to self
/// - Returns: True if there is a period of time or moment that is shared by both `TimePeriod`s
func intersects(with period: TimePeriodProtocol) -> Bool {
let relation = self.relation(to: period)
return (relation != .after && relation != .before)
}
/// If self is before the given `TimePeriod` chronologically. (A gap must exist between the two).
///
/// - Parameter period: The time period to compare to self
/// - Returns: True if self is after the given `TimePeriod`
func isBefore(_ period: TimePeriodProtocol) -> Bool {
return (relation(to: period) == .before)
}
/// If self is after the given `TimePeriod` chronologically. (A gap must exist between the two).
///
/// - Parameter period: The time period to compare to self
/// - Returns: True if self is after the given `TimePeriod`
func isAfter(_ period: TimePeriodProtocol) -> Bool {
return (relation(to: period) == .after)
}
/// The period of time between self and the given `TimePeriod` not contained by either.
///
/// - Parameter period: The time period to compare to self
/// - Returns: The gap between the periods. Zero if there is no gap.
func hasGap(between period: TimePeriodProtocol) -> Bool {
return (isBefore(period) || isAfter(period))
}
/// The period of time between self and the given `TimePeriod` not contained by either.
///
/// - Parameter period: The time period to compare to self
/// - Returns: The gap between the periods. Zero if there is no gap.
func gap(between period: TimePeriodProtocol) -> TimeInterval {
guard hasFiniteRange, period.hasFiniteRange else { return TimeInterval.greatestFiniteMagnitude }
if end! < period.start! {
return abs(end!.timeIntervalSince(period.start!))
} else if period.end! < start! {
return abs(end!.timeIntervalSince(start!))
}
return 0
}
/// In place, shift the `TimePeriod` by a `TimeInterval`
///
/// - Parameter timeInterval: The time interval to shift the period by
mutating func shift(by timeInterval: TimeInterval) {
start?.addTimeInterval(timeInterval)
end?.addTimeInterval(timeInterval)
}
/// In place, lengthen the `TimePeriod`, anchored at the beginning, end or center
///
/// - Parameters:
/// - timeInterval: The time interval to lengthen the period by
/// - anchor: The anchor point from which to make the change
mutating func lengthen(by timeInterval: TimeInterval, at anchor: TimePeriodAnchor) {
switch anchor {
case .beginning:
end?.addTimeInterval(timeInterval)
case .end:
start?.addTimeInterval(timeInterval)
case .center:
start = start?.addingTimeInterval(-timeInterval / 2.0)
end = end?.addingTimeInterval(timeInterval / 2.0)
}
}
/// In place, shorten the `TimePeriod`, anchored at the beginning, end or center
///
/// - Parameters:
/// - timeInterval: The time interval to shorten the period by
/// - anchor: The anchor point from which to make the change
mutating func shorten(by timeInterval: TimeInterval, at anchor: TimePeriodAnchor) {
switch anchor {
case .beginning:
end?.addTimeInterval(-timeInterval)
case .end:
start?.addTimeInterval(timeInterval)
case .center:
start?.addTimeInterval(timeInterval / 2.0)
end?.addTimeInterval(-timeInterval / 2.0)
}
}
/// The relationship of the self `TimePeriod` to the given `TimePeriod`.
/// Relations are stored in Enums.swift. Formal defnitions available in the provided
/// links:
/// [GitHub](https://github.com/MatthewYork/DateTools#relationships),
/// [CodeProject](http://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET)
///
/// - Parameter period: The time period to compare to self
/// - Returns: The relationship between self and the given time period
func relation(to period: TimePeriodProtocol) -> TimePeriodRelation {
//Make sure that all start and end points exist for comparison
guard hasFiniteRange, period.hasFiniteRange else { return .none }
//Make sure time periods are of positive durations
guard start! < end! && period.start! < period.end! else { return .none }
//Make comparisons
if period.start! < start! {
return .after
} else if period.end! == start! {
return .startTouching
} else if period.start! < start! && period.end! < end! {
return .startInside
} else if period.start! == start! && period.end! > end! {
return .insideStartTouching
} else if period.start! == start! && period.end! < end! {
return .enclosingStartTouching
} else if period.start! > start! && period.end! < end! {
return .enclosing
} else if period.start! > start! && period.end! == end! {
return .enclosingEndTouching
} else if period.start == start! && period.end! == end! {
return .exactMatch
} else if period.start! < start! && period.end! > end! {
return .inside
} else if period.start! < start! && period.end! == end! {
return .insideEndTouching
} else if period.start! < end! && period.end! > end! {
return .endInside
} else if period.start! == end! && period.end! > end! {
return .endTouching
} else if period.start! > end! {
return .before
}
return .none
}
/// Return `true` if period is zero-seconds long or less than specified precision.
///
/// - Parameter precision: precision in seconds; by default is 0.
/// - Returns: true if start/end has the same value or less than specified precision
func isMoment(precision: TimeInterval = 0) -> Bool {
guard hasFiniteRange else { return false }
return (abs(start!.date.timeIntervalSince1970 - end!.date.timeIntervalSince1970) <= precision)
}
/// Returns the duration of the receiver expressed with given time unit.
/// If time period has not a finite range it returns `nil`.
///
/// - Parameter unit: unit of the duration
/// - Returns: duration, `nil` if period has not a finite range
func durationIn(_ units: Set<Calendar.Component>) -> DateComponents? {
guard hasFiniteRange else { return nil }
return start!.calendar.dateComponents(units, from: start!.date, to: end!.date)
}
/// Returns the duration of the receiver expressed with given time unit.
/// If time period has not a finite range it returns `nil`.
///
/// - Parameter unit: unit of the duration
/// - Returns: duration, `nil` if period has not a finite range
func durationIn(_ unit: Calendar.Component) -> Int? {
guard hasFiniteRange else { return nil }
return start!.calendar.dateComponents([unit], from: start!.date, to: end!.date).value(for: unit)
}
/// The duration of the `TimePeriod` in years.
/// Returns the `Int.max` if beginning or end are `nil`.
var years: Int {
guard let b = start, let e = end else { return Int.max }
return b.toUnit(.year, to: e)
}
/// The duration of the `TimePeriod` in months.
/// Returns the `Int.max` if beginning or end are `nil`.
var months: Int {
guard let b = start, let e = end else { return Int.max }
return b.toUnit(.month, to: e)
}
/// The duration of the `TimePeriod` in weeks.
/// Returns the `Int.max` if beginning or end are `nil`.
var weeks: Int {
guard let b = start, let e = end else { return Int.max }
return b.toUnit(.weekOfMonth, to: e)
}
/// The duration of the `TimePeriod` in days.
/// Returns the `Int.max` if beginning or end are `nil`.
var days: Int {
guard let b = start, let e = end else { return Int.max }
return b.toUnit(.day, to: e)
}
/// The duration of the `TimePeriod` in hours.
/// Returns the `Int.max` if beginning or end are `nil`.
var hours: Int {
guard let b = start, let e = end else { return Int.max }
return b.toUnit(.hour, to: e)
}
/// The duration of the `TimePeriod` in years.
/// Returns the `Int.max` if beginning or end are `nil`.
var minutes: Int {
guard let b = start, let e = end else { return Int.max }
return b.toUnit(.minute, to: e)
}
/// The duration of the `TimePeriod` in seconds.
/// Returns the `Int.max` if beginning or end are `nil`.
var seconds: Int {
guard let b = start, let e = end else { return Int.max }
return b.toUnit(.second, to: e)
}
/// The length of time between the beginning and end dates of the
/// `TimePeriod` as a `TimeInterval`.
/// If intervals are not nil returns `Double.greatestFiniteMagnitude`
var duration: TimeInterval {
guard let b = start, let e = end else {
return TimeInterval(Double.greatestFiniteMagnitude)
}
return abs(b.date.timeIntervalSince(e.date))
}
}