|
|
|
//
|
|
|
|
// Date+Manipulations.swift
|
|
|
|
// DateToolsTests
|
|
|
|
//
|
|
|
|
// Created by Grayson Webster on 9/28/16.
|
|
|
|
// Copyright © 2016 Matthew York. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extends the Date class by adding manipulation methods for transforming dates
|
|
|
|
*/
|
|
|
|
public extension Date {
|
|
|
|
// MARK: - StartOf
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a date set to the start of a given component.
|
|
|
|
*
|
|
|
|
* - parameter component: The date component (second, minute, hour, day, month, or year)
|
|
|
|
*
|
|
|
|
* - returns: A date retaining the value of the given component and all larger components,
|
|
|
|
* with all smaller components set to their minimum
|
|
|
|
*/
|
|
|
|
func start(of component: Component) -> Date {
|
|
|
|
var newDate = self
|
|
|
|
if component == .second {
|
|
|
|
newDate.second(second)
|
|
|
|
} else if component == .minute {
|
|
|
|
newDate.second(0)
|
|
|
|
} else if component == .hour {
|
|
|
|
newDate.second(0)
|
|
|
|
newDate.minute(0)
|
|
|
|
} else if component == .day {
|
|
|
|
newDate.second(0)
|
|
|
|
newDate.minute(0)
|
|
|
|
newDate.hour(0)
|
|
|
|
} else if component == .month {
|
|
|
|
newDate.second(0)
|
|
|
|
newDate.minute(0)
|
|
|
|
newDate.hour(0)
|
|
|
|
newDate.day(1)
|
|
|
|
} else if component == .year {
|
|
|
|
newDate.second(0)
|
|
|
|
newDate.minute(0)
|
|
|
|
newDate.hour(0)
|
|
|
|
newDate.day(1)
|
|
|
|
newDate.month(1)
|
|
|
|
}
|
|
|
|
return newDate
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a date set to the end of a given component.
|
|
|
|
*
|
|
|
|
* - parameter component: The date component (second, minute, hour, day, month, or year)
|
|
|
|
*
|
|
|
|
* - returns: A date retaining the value of the given component and all larger components,
|
|
|
|
* with all smaller components set to their maximum
|
|
|
|
*/
|
|
|
|
func end(of component: Component) -> Date {
|
|
|
|
var newDate = self
|
|
|
|
if component == .second {
|
|
|
|
newDate.second(newDate.second + 1)
|
|
|
|
newDate -= 0.001
|
|
|
|
} else if component == .minute {
|
|
|
|
newDate.second(60)
|
|
|
|
newDate -= 0.001
|
|
|
|
} else if component == .hour {
|
|
|
|
newDate.second(60)
|
|
|
|
newDate -= 0.001
|
|
|
|
newDate.minute(59)
|
|
|
|
} else if component == .day {
|
|
|
|
newDate.second(60)
|
|
|
|
newDate -= 0.001
|
|
|
|
newDate.minute(59)
|
|
|
|
newDate.hour(23)
|
|
|
|
} else if component == .month {
|
|
|
|
newDate.second(60)
|
|
|
|
newDate -= 0.001
|
|
|
|
newDate.minute(59)
|
|
|
|
newDate.hour(23)
|
|
|
|
newDate.day(daysInMonth(date: newDate))
|
|
|
|
} else if component == .year {
|
|
|
|
newDate.second(60)
|
|
|
|
newDate -= 0.001
|
|
|
|
newDate.minute(59)
|
|
|
|
newDate.hour(23)
|
|
|
|
newDate.month(12)
|
|
|
|
newDate.day(31)
|
|
|
|
}
|
|
|
|
|
|
|
|
return newDate
|
|
|
|
}
|
|
|
|
|
|
|
|
func daysInMonth(date: Date) -> Int {
|
|
|
|
let month = date.month
|
|
|
|
if month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 {
|
|
|
|
// 31 day month
|
|
|
|
return 31
|
|
|
|
} else if month == 2, date.isInLeapYear {
|
|
|
|
// February with leap year
|
|
|
|
return 29
|
|
|
|
} else if month == 2, !date.isInLeapYear {
|
|
|
|
// February without leap year
|
|
|
|
return 28
|
|
|
|
} else {
|
|
|
|
// 30 day month
|
|
|
|
return 30
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Addition / Subtractions
|
|
|
|
|
|
|
|
/**
|
|
|
|
* # Add (TimeChunk to Date)
|
|
|
|
* Increase a date by the value of a given `TimeChunk`.
|
|
|
|
*
|
|
|
|
* - parameter chunk: The amount to increase the date by (ex. 2.days, 4.years, etc.)
|
|
|
|
*
|
|
|
|
* - returns: A date with components increased by the values of the
|
|
|
|
* corresponding `TimeChunk` variables
|
|
|
|
*/
|
|
|
|
func add(_ chunk: TimeChunk) -> Date {
|
|
|
|
let calendar = Calendar.autoupdatingCurrent
|
|
|
|
var components = DateComponents()
|
|
|
|
components.year = chunk.years
|
|
|
|
components.month = chunk.months
|
|
|
|
components.day = chunk.days + (chunk.weeks * 7)
|
|
|
|
components.hour = chunk.hours
|
|
|
|
components.minute = chunk.minutes
|
|
|
|
components.second = chunk.seconds
|
|
|
|
return calendar.date(byAdding: components, to: self)!
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* # Subtract (TimeChunk from Date)
|
|
|
|
* Decrease a date by the value of a given `TimeChunk`.
|
|
|
|
*
|
|
|
|
* - parameter chunk: The amount to decrease the date by (ex. 2.days, 4.years, etc.)
|
|
|
|
*
|
|
|
|
* - returns: A date with components decreased by the values of the
|
|
|
|
* corresponding `TimeChunk` variables
|
|
|
|
*/
|
|
|
|
func subtract(_ chunk: TimeChunk) -> Date {
|
|
|
|
let calendar = Calendar.autoupdatingCurrent
|
|
|
|
var components = DateComponents()
|
|
|
|
components.year = -chunk.years
|
|
|
|
components.month = -chunk.months
|
|
|
|
components.day = -(chunk.days + (chunk.weeks * 7))
|
|
|
|
components.hour = -chunk.hours
|
|
|
|
components.minute = -chunk.minutes
|
|
|
|
components.second = -chunk.seconds
|
|
|
|
return calendar.date(byAdding: components, to: self)!
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Operator Overloads
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Operator overload for adding a `TimeChunk` to a date.
|
|
|
|
*/
|
|
|
|
static func + (leftAddend: Date, rightAddend: TimeChunk) -> Date {
|
|
|
|
return leftAddend.add(rightAddend)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Operator overload for subtracting a `TimeChunk` from a date.
|
|
|
|
*/
|
|
|
|
static func - (minuend: Date, subtrahend: TimeChunk) -> Date {
|
|
|
|
return minuend.subtract(subtrahend)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Operator overload for adding a `TimeInterval` to a date.
|
|
|
|
*/
|
|
|
|
static func + (leftAddend: Date, rightAddend: Int) -> Date {
|
|
|
|
return leftAddend.addingTimeInterval(TimeInterval(rightAddend))
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Operator overload for subtracting a `TimeInterval` from a date.
|
|
|
|
*/
|
|
|
|
static func - (minuend: Date, subtrahend: Int) -> Date {
|
|
|
|
return minuend.addingTimeInterval(-TimeInterval(subtrahend))
|
|
|
|
}
|
|
|
|
}
|