You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

639 lines
21 KiB

//
// Date+Comparators.swift
// DateToolsTests
//
// Created by Matthew York on 8/26/16.
// Copyright © 2016 Matthew York. All rights reserved.
//
import Foundation
/**
* Extends the Date class by adding methods for calculating the chunk
* of time between two dates and providing many variables and functions
* that compare the ordinality of two dates and the space between two dates
* for a given unit of time.
*/
public extension Date {
// MARK: - Comparisons
6 years ago
/**
* Given a date, returns a `TimeChunk` with components in their most natural form. Example:
*
* ```
* let formatter = DateFormatter()
* formatter.dateFormat = "yyyy MM dd HH:mm:ss.SSS"
* let birthday = formatter.date(from: "2015 11 24 14:50:12.000")!
* let age = birthday.chunkBetween(date: formatter.date(from: "2016 10 07 15:27:12.000")!)
* ```
*
6 years ago
* The age variable will have a chunk of time with year, month, day, hour, minute,
* and second components (note that we do not use weeks since they are not components
* of `Calendar`). So if you just wanted the age in years, you could then say: age.years.
*
6 years ago
* The chunk is calculated exactly as you'd say it in real life, always converting up
* when a lower unit equals 1 of the unit above it. The above example returns
* `TimeChunk(seconds: 0, minutes: 37, hours: 0, days: 13, weeks: 0, months: 10, years: 0)`.
*
6 years ago
* Passing a future date returns a TimeChunk with all positive components and passing
* a date in the past returns one with all negative components.
*
* - parameter date: The date of reference from the date called on
*
* - returns: A TimeChunk representing the time between the dates, in natural form
*/
func chunkBetween(date: Date) -> TimeChunk {
var compenentsBetween = Calendar.autoupdatingCurrent.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self, to: date)
return TimeChunk(seconds: compenentsBetween.second!, minutes: compenentsBetween.minute!, hours: compenentsBetween.hour!, days: compenentsBetween.day!, weeks: 0, months: compenentsBetween.month!, years: compenentsBetween.year!)
// TimeChunk(seconds: secondDelta, minutes: minuteDelta, hours: hourDelta, days: dayDelta, weeks: 0, months: monthDelta, years: yearDelta)
}
6 years ago
/**
* Returns a true if receiver is equal to provided comparison date, otherwise returns false
*
* - parameter date: Provided date for comparison
*
* - returns: Bool representing comparison result
*/
6 years ago
func equals(_ date: Date) -> Bool {
return compare(date) == .orderedSame
}
6 years ago
/**
* Returns a true if receiver is later than provided comparison date, otherwise
* returns false
*
* - parameter date: Provided date for comparison
*
* - returns: Bool representing comparison result
*/
6 years ago
func isLater(than date: Date) -> Bool {
return compare(date) == .orderedDescending
}
6 years ago
/**
* Returns a true if receiver is later than or equal to provided comparison date,
* otherwise returns false
*
* - parameter date: Provided date for comparison
*
* - returns: Bool representing comparison result
*/
func isLaterThanOrEqual(to date: Date) -> Bool {
6 years ago
return compare(date) == .orderedDescending || compare(date) == .orderedSame
}
6 years ago
/**
* Returns a true if receiver is earlier than provided comparison date, otherwise
* returns false
*
* - parameter date: Provided date for comparison
*
* - returns: Bool representing comparison result
*/
6 years ago
func isEarlier(than date: Date) -> Bool {
return compare(date) == .orderedAscending
}
6 years ago
/**
* Returns a true if receiver is earlier than or equal to the provided comparison date,
* otherwise returns false
*
* - parameter date: Provided date for comparison
*
* - returns: Bool representing comparison result
*/
func isEarlierThanOrEqual(to date: Date) -> Bool {
6 years ago
return compare(date) == .orderedAscending || compare(date) == .orderedSame
}
6 years ago
/**
* Returns whether two dates fall on the same day.
*
* - parameter date: Date to compare with sender
*
* - returns: True if both paramter dates fall on the same day, false otherwise
*/
6 years ago
func isSameDay(date: Date) -> Bool {
return Date.isSameDay(date: self, as: date)
}
6 years ago
/**
* Returns whether two dates fall on the same day.
*
* - parameter date: First date to compare
* - parameter compareDate: Second date to compare
*
* - returns: True if both paramter dates fall on the same day, false otherwise
*/
static func isSameDay(date: Date, as compareDate: Date) -> Bool {
let calendar = Calendar.autoupdatingCurrent
var components = calendar.dateComponents([.era, .year, .month, .day], from: date)
let dateOne = calendar.date(from: components)
6 years ago
components = calendar.dateComponents([.era, .year, .month, .day], from: compareDate)
let dateTwo = calendar.date(from: components)
6 years ago
return (dateOne?.equals(dateTwo!))!
}
6 years ago
// MARK: - Date Comparison
6 years ago
// MARK: Time From
6 years ago
/**
* Returns an Int representing the amount of time in years between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
* Uses the default Gregorian calendar
*
* - parameter date: The provided date for comparison
*
* - returns: The years between receiver and provided date
*/
func years(from date: Date) -> Int {
6 years ago
return years(from: date, calendar: nil)
}
6 years ago
/**
* Returns an Int representing the amount of time in months between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
* Uses the default Gregorian calendar
*
* - parameter date: The provided date for comparison
*
* - returns: The years between receiver and provided date
*/
func months(from date: Date) -> Int {
6 years ago
return months(from: date, calendar: nil)
}
6 years ago
/**
* Returns an Int representing the amount of time in weeks between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
* Uses the default Gregorian calendar
*
* - parameter date: The provided date for comparison
*
* - returns: The weeks between receiver and provided date
*/
func weeks(from date: Date) -> Int {
6 years ago
return weeks(from: date, calendar: nil)
}
6 years ago
/**
* Returns an Int representing the amount of time in days between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
* Uses the default Gregorian calendar
*
* - parameter date: The provided date for comparison
*
* - returns: The days between receiver and provided date
*/
func days(from date: Date) -> Int {
6 years ago
return days(from: date, calendar: nil)
}
6 years ago
/**
* Returns an Int representing the amount of time in hours between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
*
* - parameter date: The provided date for comparison
*
* - returns: The hours between receiver and provided date
*/
func hours(from date: Date) -> Int {
6 years ago
return Int(timeIntervalSince(date) / Constants.SecondsInHour)
}
6 years ago
/**
* Returns an Int representing the amount of time in minutes between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
*
* - parameter date: The provided date for comparison
*
* - returns: The minutes between receiver and provided date
*/
func minutes(from date: Date) -> Int {
6 years ago
return Int(timeIntervalSince(date) / Constants.SecondsInMinute)
}
6 years ago
/**
* Returns an Int representing the amount of time in seconds between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
*
* - parameter date: The provided date for comparison
*
* - returns: The seconds between receiver and provided date
*/
func seconds(from date: Date) -> Int {
return Int(timeIntervalSince(date))
}
6 years ago
// MARK: Time From With Calendar
6 years ago
/**
* Returns an Int representing the amount of time in years between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
*
* - parameter date: The provided date for comparison
* - parameter calendar: The calendar to be used in the calculation
*
* - returns: The years between receiver and provided date
*/
func years(from date: Date, calendar: Calendar?) -> Int {
var calendarCopy = calendar
6 years ago
if calendar == nil {
calendarCopy = Calendar.autoupdatingCurrent
}
6 years ago
let earliest = earlierDate(date)
6 years ago
let latest = (earliest == self) ? date : self
let multiplier = (earliest == self) ? -1 : 1
let components = calendarCopy!.dateComponents([.year], from: earliest, to: latest)
6 years ago
return multiplier * components.year!
}
6 years ago
/**
* Returns an Int representing the amount of time in months between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
*
* - parameter date: The provided date for comparison
* - parameter calendar: The calendar to be used in the calculation
*
* - returns: The months between receiver and provided date
*/
6 years ago
func months(from date: Date, calendar: Calendar?) -> Int {
var calendarCopy = calendar
6 years ago
if calendar == nil {
calendarCopy = Calendar.autoupdatingCurrent
}
6 years ago
let earliest = earlierDate(date)
6 years ago
let latest = (earliest == self) ? date : self
let multiplier = (earliest == self) ? -1 : 1
let components = calendarCopy!.dateComponents(Constants.AllCalendarUnitFlags, from: earliest, to: latest)
6 years ago
return multiplier * (components.month! + 12 * components.year!)
}
6 years ago
/**
* Returns an Int representing the amount of time in weeks between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
*
* - parameter date: The provided date for comparison
* - parameter calendar: The calendar to be used in the calculation
*
* - returns: The weeks between receiver and provided date
*/
6 years ago
func weeks(from date: Date, calendar: Calendar?) -> Int {
var calendarCopy = calendar
6 years ago
if calendar == nil {
calendarCopy = Calendar.autoupdatingCurrent
}
6 years ago
let earliest = earlierDate(date)
6 years ago
let latest = (earliest == self) ? date : self
let multiplier = (earliest == self) ? -1 : 1
let components = calendarCopy!.dateComponents([.weekOfYear], from: earliest, to: latest)
6 years ago
return multiplier * components.weekOfYear!
}
6 years ago
/**
* Returns an Int representing the amount of time in days between the receiver and
* the provided date.
*
* If the receiver is earlier than the provided date, the returned value will be negative.
*
* - parameter date: The provided date for comparison
* - parameter calendar: The calendar to be used in the calculation
*
* - returns: The days between receiver and provided date
*/
func days(from date: Date, calendar: Calendar?) -> Int {
var calendarCopy = calendar
6 years ago
if calendar == nil {
calendarCopy = Calendar.autoupdatingCurrent
}
6 years ago
let earliest = earlierDate(date)
let latest = (earliest == self) ? date : self
let multiplier = (earliest == self) ? -1 : 1
let components = calendarCopy!.dateComponents([.day], from: earliest, to: latest)
6 years ago
return multiplier * components.day!
}
6 years ago
// MARK: Time Until
6 years ago
/**
6 years ago
* The number of years until the receiver's date (0 if the receiver is the same or
* earlier than now).
*/
var yearsUntil: Int {
return yearsLater(than: Date())
}
6 years ago
/**
6 years ago
* The number of months until the receiver's date (0 if the receiver is the same or
* earlier than now).
*/
var monthsUntil: Int {
return monthsLater(than: Date())
}
6 years ago
/**
6 years ago
* The number of weeks until the receiver's date (0 if the receiver is the same or
* earlier than now).
*/
var weeksUntil: Int {
return weeksLater(than: Date())
}
6 years ago
/**
6 years ago
* The number of days until the receiver's date (0 if the receiver is the same or
* earlier than now).
*/
var daysUntil: Int {
return daysLater(than: Date())
}
6 years ago
/**
6 years ago
* The number of hours until the receiver's date (0 if the receiver is the same or
* earlier than now).
*/
6 years ago
var hoursUntil: Int {
return hoursLater(than: Date())
}
6 years ago
/**
6 years ago
* The number of minutes until the receiver's date (0 if the receiver is the same or
* earlier than now).
*/
6 years ago
var minutesUntil: Int {
return minutesLater(than: Date())
}
6 years ago
/**
6 years ago
* The number of seconds until the receiver's date (0 if the receiver is the same or
* earlier than now).
*/
6 years ago
var secondsUntil: Int {
return secondsLater(than: Date())
}
6 years ago
// MARK: Time Ago
6 years ago
/**
6 years ago
* The number of years the receiver's date is earlier than now (0 if the receiver is
* the same or earlier than now).
*/
var yearsAgo: Int {
return yearsEarlier(than: Date())
}
6 years ago
/**
6 years ago
* The number of months the receiver's date is earlier than now (0 if the receiver is
* the same or earlier than now).
*/
var monthsAgo: Int {
return monthsEarlier(than: Date())
}
6 years ago
/**
6 years ago
* The number of weeks the receiver's date is earlier than now (0 if the receiver is
* the same or earlier than now).
*/
var weeksAgo: Int {
return weeksEarlier(than: Date())
}
6 years ago
/**
6 years ago
* The number of days the receiver's date is earlier than now (0 if the receiver is
* the same or earlier than now).
*/
var daysAgo: Int {
return daysEarlier(than: Date())
}
6 years ago
/**
6 years ago
* The number of hours the receiver's date is earlier than now (0 if the receiver is
* the same or earlier than now).
*/
var hoursAgo: Int {
return hoursEarlier(than: Date())
}
6 years ago
/**
6 years ago
* The number of minutes the receiver's date is earlier than now (0 if the receiver is
* the same or earlier than now).
*/
var minutesAgo: Int {
return minutesEarlier(than: Date())
}
6 years ago
/**
6 years ago
* The number of seconds the receiver's date is earlier than now (0 if the receiver is
* the same or earlier than now).
*/
6 years ago
var secondsAgo: Int {
return secondsEarlier(than: Date())
}
6 years ago
// MARK: Earlier Than
6 years ago
/**
* Returns the number of years the receiver's date is earlier than the provided
* comparison date, 0 if the receiver's date is later than or equal to the provided comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of years
*/
func yearsEarlier(than date: Date) -> Int {
return abs(min(years(from: date), 0))
}
6 years ago
/**
* Returns the number of months the receiver's date is earlier than the provided
* comparison date, 0 if the receiver's date is later than or equal to the provided comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of months
*/
func monthsEarlier(than date: Date) -> Int {
6 years ago
return abs(min(months(from: date), 0))
}
6 years ago
/**
* Returns the number of weeks the receiver's date is earlier than the provided
* comparison date, 0 if the receiver's date is later than or equal to the provided comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of weeks
*/
func weeksEarlier(than date: Date) -> Int {
return abs(min(weeks(from: date), 0))
}
6 years ago
/**
* Returns the number of days the receiver's date is earlier than the provided
* comparison date, 0 if the receiver's date is later than or equal to the provided comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of days
*/
func daysEarlier(than date: Date) -> Int {
return abs(min(days(from: date), 0))
}
6 years ago
/**
* Returns the number of hours the receiver's date is earlier than the provided
* comparison date, 0 if the receiver's date is later than or equal to the provided comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of hours
*/
func hoursEarlier(than date: Date) -> Int {
return abs(min(hours(from: date), 0))
}
6 years ago
/**
* Returns the number of minutes the receiver's date is earlier than the provided
* comparison date, 0 if the receiver's date is later than or equal to the provided comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of minutes
*/
func minutesEarlier(than date: Date) -> Int {
return abs(min(minutes(from: date), 0))
}
6 years ago
/**
* Returns the number of seconds the receiver's date is earlier than the provided
* comparison date, 0 if the receiver's date is later than or equal to the provided comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of seconds
*/
func secondsEarlier(than date: Date) -> Int {
return abs(min(seconds(from: date), 0))
}
6 years ago
// MARK: Later Than
6 years ago
/**
* Returns the number of years the receiver's date is later than the provided
6 years ago
* comparison date, 0 if the receiver's date is earlier than or equal to the provided
* comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of years
*/
func yearsLater(than date: Date) -> Int {
return max(years(from: date), 0)
}
6 years ago
/**
* Returns the number of months the receiver's date is later than the provided
6 years ago
* comparison date, 0 if the receiver's date is earlier than or equal to the provided
* comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of months
*/
func monthsLater(than date: Date) -> Int {
return max(months(from: date), 0)
}
6 years ago
/**
* Returns the number of weeks the receiver's date is later than the provided
* comparison date, 0 if the receiver's date is earlier than or equal to the provided
* comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of weeks
*/
func weeksLater(than date: Date) -> Int {
return max(weeks(from: date), 0)
}
6 years ago
/**
* Returns the number of days the receiver's date is later than the provided
* comparison date, 0 if the receiver's date is earlier than or equal to the provided
* comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of days
*/
func daysLater(than date: Date) -> Int {
return max(days(from: date), 0)
}
6 years ago
/**
* Returns the number of hours the receiver's date is later than the provided
* comparison date, 0 if the receiver's date is earlier than or equal to the provided
* comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of hours
*/
func hoursLater(than date: Date) -> Int {
return max(hours(from: date), 0)
}
6 years ago
/**
* Returns the number of minutes the receiver's date is later than the provided
* comparison date, 0 if the receiver's date is earlier than or equal to the provided
* comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of minutes
*/
func minutesLater(than date: Date) -> Int {
return max(minutes(from: date), 0)
}
6 years ago
/**
* Returns the number of seconds the receiver's date is later than the provided
* comparison date, 0 if the receiver's date is earlier than or equal to the provided
* comparison date.
*
* - parameter date: Provided date for comparison
*
* - returns: The number of seconds
*/
func secondsLater(than date: Date) -> Int {
return max(seconds(from: date), 0)
}
}