//
|
// 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 {
|
|
/// Return the oldest date in given list.
|
///
|
/// - Parameter list: list of dates
|
/// - Returns: a tuple with the index of the oldest date and its instance.
|
static func oldestIn(list: [Date]) -> Date? {
|
guard list.count > 0 else { return nil }
|
guard list.count > 1 else { return list.first! }
|
return list.min(by: {
|
return $0 < $1
|
})
|
}
|
|
/// Return the newest date in given list.
|
///
|
/// - Parameter list: list of dates
|
/// - Returns: a tuple with the index of the oldest date and its instance.
|
static func newestIn(list: [Date]) -> Date? {
|
guard list.count > 0 else { return nil }
|
guard list.count > 1 else { return list.first! }
|
return list.max(by: {
|
return $0 < $1
|
})
|
}
|
|
/// Enumerate dates between two intervals by adding specified time components defined by a function and return an array of dates.
|
/// `startDate` interval will be the first item of the resulting array.
|
/// The last item of the array is evaluated automatically and maybe not equal to `endDate`.
|
///
|
/// - Parameters:
|
/// - start: starting date
|
/// - endDate: ending date
|
/// - increment: increment function. It get the last generated date and require a valida `DateComponents` instance which define the increment
|
/// - Returns: array of dates
|
static func enumerateDates(from startDate: Date, to endDate: Date, increment: ((Date) -> (DateComponents))) -> [Date] {
|
var dates: [Date] = []
|
var currentDate = startDate
|
|
while currentDate <= endDate {
|
dates.append(currentDate)
|
currentDate = (currentDate + increment(currentDate))
|
}
|
return dates
|
}
|
|
/// Enumerate dates between two intervals by adding specified time components and return an array of dates.
|
/// `startDate` interval will be the first item of the resulting array.
|
/// The last item of the array is evaluated automatically and maybe not equal to `endDate`.
|
///
|
/// - Parameters:
|
/// - start: starting date
|
/// - endDate: ending date
|
/// - increment: components to add
|
/// - Returns: array of dates
|
static func enumerateDates(from startDate: Date, to endDate: Date, increment: DateComponents) -> [Date] {
|
return Date.enumerateDates(from: startDate, to: endDate, increment: { _ in
|
return increment
|
})
|
}
|
|
/// Round a given date time to the passed style (off|up|down).
|
///
|
/// - Parameter style: rounding mode.
|
/// - Returns: rounded date
|
func dateRoundedAt(at style: RoundDateMode) -> Date {
|
return inDefaultRegion().dateRoundedAt(style).date
|
}
|
|
/// Returns a new DateInRegion that is initialized at the start of a specified unit of time.
|
///
|
/// - Parameter unit: time unit value.
|
/// - Returns: instance at the beginning of the time unit; `self` if fails.
|
func dateAtStartOf(_ unit: Calendar.Component) -> Date {
|
return inDefaultRegion().dateAtStartOf(unit).date
|
}
|
|
/// Return a new DateInRegion that is initialized at the start of the specified components
|
/// executed in order.
|
///
|
/// - Parameter units: sequence of transformations as time unit components
|
/// - Returns: new date at the beginning of the passed components, intermediate results if fails.
|
func dateAtStartOf(_ units: [Calendar.Component]) -> Date {
|
return units.reduce(self) { (currentDate, currentUnit) -> Date in
|
return currentDate.dateAtStartOf(currentUnit)
|
}
|
}
|
|
/// Returns a new Moment that is initialized at the end of a specified unit of time.
|
///
|
/// - parameter unit: A TimeUnit value.
|
///
|
/// - returns: A new Moment instance.
|
func dateAtEndOf(_ unit: Calendar.Component) -> Date {
|
return inDefaultRegion().dateAtEndOf(unit).date
|
}
|
|
/// Return a new DateInRegion that is initialized at the end of the specified components
|
/// executed in order.
|
///
|
/// - Parameter units: sequence of transformations as time unit components
|
/// - Returns: new date at the end of the passed components, intermediate results if fails.
|
func dateAtEndOf(_ units: [Calendar.Component]) -> Date {
|
return units.reduce(self) { (currentDate, currentUnit) -> Date in
|
return currentDate.dateAtEndOf(currentUnit)
|
}
|
}
|
|
/// Create a new date by altering specified components of the receiver.
|
///
|
/// - Parameter components: components to alter with their new values.
|
/// - Returns: new altered `DateInRegion` instance
|
func dateBySet(_ components: [Calendar.Component: Int]) -> Date? {
|
return DateInRegion(self, region: SwiftDate.defaultRegion).dateBySet(components)?.date
|
}
|
|
/// Create a new date by altering specified time components.
|
///
|
/// - Parameters:
|
/// - hour: hour to set (`nil` to leave it unaltered)
|
/// - min: min to set (`nil` to leave it unaltered)
|
/// - secs: sec to set (`nil` to leave it unaltered)
|
/// - ms: milliseconds to set (`nil` to leave it unaltered)
|
/// - options: options for calculation
|
/// - Returns: new altered `DateInRegion` instance
|
func dateBySet(hour: Int?, min: Int?, secs: Int?, ms: Int? = nil, options: TimeCalculationOptions = TimeCalculationOptions()) -> Date? {
|
let srcDate = DateInRegion(self, region: SwiftDate.defaultRegion)
|
return srcDate.dateBySet(hour: hour, min: min, secs: secs, ms: ms, options: options)?.date
|
}
|
|
/// Creates a new instance by truncating the components
|
///
|
/// - Parameter components: components to truncate.
|
/// - Returns: new date with truncated components.
|
func dateTruncated(_ components: [Calendar.Component]) -> Date? {
|
return DateInRegion(self, region: SwiftDate.defaultRegion).dateTruncated(at: components)?.date
|
}
|
|
/// Creates a new instance by truncating the components starting from given components down the granurality.
|
///
|
/// - Parameter component: The component to be truncated from.
|
/// - Returns: new date with truncated components.
|
func dateTruncated(from component: Calendar.Component) -> Date? {
|
return DateInRegion(self, region: SwiftDate.defaultRegion).dateTruncated(from: component)?.date
|
}
|
|
/// Offset a date by n calendar components.
|
/// Note: This operation can be functionally chained.
|
///
|
/// - Parameters:
|
/// - count: value of the offset.
|
/// - component: component to offset.
|
/// - Returns: new altered date.
|
func dateByAdding(_ count: Int, _ component: Calendar.Component) -> DateInRegion {
|
return DateInRegion(self, region: SwiftDate.defaultRegion).dateByAdding(count, component)
|
}
|
|
/// Return related date starting from the receiver attributes.
|
///
|
/// - Parameter type: related date to obtain.
|
/// - Returns: instance of the related date.
|
func dateAt(_ type: DateRelatedType) -> Date {
|
return inDefaultRegion().dateAt(type).date
|
}
|
|
/// Create a new date at now and extract the related date using passed rule type.
|
///
|
/// - Parameter type: related date to obtain.
|
/// - Returns: instance of the related date.
|
static func nowAt(_ type: DateRelatedType) -> Date {
|
return Date().dateAt(type)
|
}
|
|
/// Return the dates for a specific weekday inside given month of specified year.
|
/// Ie. get me all the saturdays of Feb 2018.
|
/// NOTE: Values are returned in order.
|
///
|
/// - Parameters:
|
/// - weekday: weekday target.
|
/// - month: month target.
|
/// - year: year target.
|
/// - region: region target, omit to use `SwiftDate.defaultRegion`
|
/// - Returns: Ordered list of the dates for given weekday into given month.
|
static func datesForWeekday(_ weekday: WeekDay, inMonth month: Int, ofYear year: Int,
|
region: Region = SwiftDate.defaultRegion) -> [Date] {
|
let fromDate = DateInRegion(Date(year: year, month: month, day: 1, hour: 0, minute: 0), region: region)
|
let toDate = fromDate.dateAt(.endOfMonth)
|
return DateInRegion.datesForWeekday(weekday, from: fromDate, to: toDate, region: region).map { $0.date }
|
}
|
|
/// Return the dates for a specific weekday inside a specified date range.
|
/// NOTE: Values are returned in order.
|
///
|
/// - Parameters:
|
/// - weekday: weekday target.
|
/// - startDate: from date of the range.
|
/// - endDate: to date of the range.
|
/// - region: region target, omit to use `SwiftDate.defaultRegion`
|
/// - Returns: Ordered list of the dates for given weekday in passed range.
|
static func datesForWeekday(_ weekday: WeekDay, from startDate: Date, to endDate: Date,
|
region: Region = SwiftDate.defaultRegion) -> [Date] {
|
let fromDate = DateInRegion(startDate, region: region)
|
let toDate = DateInRegion(endDate, region: region)
|
return DateInRegion.datesForWeekday(weekday, from: fromDate, to: toDate, region: region).map { $0.date }
|
}
|
|
/// Returns the date at the given week number and week day preserving smaller components (hour, minute, seconds)
|
///
|
/// For example: to get the third friday of next month
|
/// let today = DateInRegion()
|
/// let result = today.dateAt(weekdayOrdinal: 3, weekday: .friday, monthNumber: today.month + 1)
|
///
|
/// - Parameters:
|
/// - weekdayOrdinal: the week number (by set position in a recurrence rule)
|
/// - weekday: WeekDay
|
/// - monthNumber: a number from 1 to 12 representing the month, optional parameter
|
/// - yearNumber: a number representing the year, optional parameter
|
/// - Returns: new date created with the given parameters
|
func dateAt(weekdayOrdinal: Int, weekday: WeekDay, monthNumber: Int? = nil,
|
yearNumber: Int? = nil) -> Date {
|
let date = DateInRegion(self, region: region)
|
return date.dateAt(weekdayOrdinal: weekdayOrdinal, weekday: weekday, monthNumber: monthNumber, yearNumber: yearNumber).date
|
}
|
|
/// Returns the next weekday preserving smaller components (hour, minute, seconds)
|
///
|
/// - Parameters:
|
/// - weekday: weekday to get.
|
/// - region: region target, omit to use `SwiftDate.defaultRegion`
|
/// - Returns: `Date`
|
func nextWeekday(_ weekday: WeekDay, region: Region = SwiftDate.defaultRegion) -> Date {
|
let date = DateInRegion(self, region: region)
|
return date.nextWeekday(weekday).date
|
}
|
|
}
|