import UIKit

// Overloading + and - so that we can add and subtract DateComponents

// ------------------------------------------------------------------

func +(_ lhs: DateComponents, _ rhs: DateComponents) -> DateComponents {

return combineComponents(lhs, rhs)

}

func -(_ lhs: DateComponents, _ rhs: DateComponents) -> DateComponents {

return combineComponents(lhs, rhs, multiplier: -1)

}

func combineComponents(_ lhs: DateComponents,

_ rhs: DateComponents,

multiplier: Int = 1)

-> DateComponents {

var result = DateComponents()

result.second = (lhs.second ?? 0) + (rhs.second ?? 0) * multiplier

result.minute = (lhs.minute ?? 0) + (rhs.minute ?? 0) * multiplier

result.hour = (lhs.hour ?? 0) + (rhs.hour ?? 0) * multiplier

result.day = (lhs.day ?? 0) + (rhs.day ?? 0) * multiplier

result.weekOfYear = (lhs.weekOfYear ?? 0) + (rhs.weekOfYear ?? 0) * multiplier

result.month = (lhs.month ?? 0) + (rhs.month ?? 0) * multiplier

result.year = (lhs.year ?? 0) + (rhs.year ?? 0) * multiplier

return result

}

// Let's define a couple of durations of time

var oneDayFiveHoursTenMinutes = DateComponents()

oneDayFiveHoursTenMinutes.day = 1

oneDayFiveHoursTenMinutes.hour = 5

oneDayFiveHoursTenMinutes.minute = 10

var threeDaysTenHoursThirtyMinutes = DateComponents()

threeDaysTenHoursThirtyMinutes.day = 3

threeDaysTenHoursThirtyMinutes.hour = 10

threeDaysTenHoursThirtyMinutes.minute = 30

// Now let's add and subtract them

let additionResult = oneDayFiveHoursTenMinutes + threeDaysTenHoursThirtyMinutes

additionResult.day // 4

additionResult.hour // 15

additionResult.minute // 40

let subtractionResult = threeDaysTenHoursThirtyMinutes - oneDayFiveHoursTenMinutes

subtractionResult.day // 2

subtractionResult.hour // 5

subtractionResult.minute // 20

// Overloading - so that we can negate DateComponents

// --------------------------------------------------

// We'll need to overload unary - so we can negate components

prefix func -(components: DateComponents) -> DateComponents {

var result = DateComponents()

if components.second != nil { result.second = -components.second! }

if components.minute != nil { result.minute = -components.minute! }

if components.hour != nil { result.hour = -components.hour! }

if components.day != nil { result.day = -components.day! }

if components.weekOfYear != nil { result.weekOfYear = -components.weekOfYear! }

if components.month != nil { result.month = -components.month! }

if components.year != nil { result.year = -components.year! }

return result

}

let negativeTime = -oneDayFiveHoursTenMinutes

negativeTime.day // -1

negativeTime.hour // -5

negativeTime.minute // -10

// Overloading + and - so that we can add Dates and DateComponents

// and subtract DateComponents from Dates

// Date + DateComponents

func +(_ lhs: Date, _ rhs: DateComponents) -> Date

{

return Calendar.current.date(byAdding: rhs, to: lhs)!

}

// DateComponents + Dates

func +(_ lhs: DateComponents, _ rhs: Date) -> Date

{

return rhs + lhs

}

// Date - DateComponents

func -(_ lhs: Date, _ rhs: DateComponents) -> Date

{

return lhs + (-rhs)

}

// What time will it be 1 day, 5 hours, and 10 minutes from now?

// Here's the standard way of finding out:

Calendar.current.date(byAdding: oneDayFiveHoursTenMinutes, to: Date())

// With our overloads and function definitions, we can now do it this way:

Date() + oneDayFiveHoursTenMinutes

// This will work as well:

oneDayFiveHoursTenMinutes + Date()

// What time was it 3 days, 10 hours, and 30 minutes ago?

// Doing it the standard way takes some work

var minus3Days5Hours30minutes = threeDaysTenHoursThirtyMinutes

minus3Days5Hours30minutes.day = -threeDaysTenHoursThirtyMinutes.day!

minus3Days5Hours30minutes.hour = -threeDaysTenHoursThirtyMinutes.hour!

minus3Days5Hours30minutes.minute = -threeDaysTenHoursThirtyMinutes.minute!

Calendar.current.date(byAdding: minus3Days5Hours30minutes, to: Date())

// With our overloads and function definitions, it's so much easier:

Date() - threeDaysTenHoursThirtyMinutes

// Extending Date so that creating dates is simpler

// ------------------------------------------------

extension Date {

init(year: Int,

month: Int,

day: Int,

hour: Int = 0,

minute: Int = 0,

second: Int = 0,

timeZone: TimeZone = TimeZone(abbreviation: "UTC")!) {

var components = DateComponents()

components.year = year

components.month = month

components.day = day

components.hour = hour

components.minute = minute

components.second = second

components.timeZone = timeZone

self = Calendar.current.date(from: components)!

}

init(dateString: String) {

let formatter = DateFormatter()

formatter.dateFormat = "yyyy-MM-dd HH:mm:ss zz"

self = formatter.date(from: dateString)!

}

}

// The Stevenote where the original iPhone was announced took place

// on January 9, 2007 at 10:00 a.m. PST

let iPhoneStevenoteDate = Date(year: 2007,

month: 1,

day: 9,

hour: 10,

minute: 0,

second: 0,

timeZone: TimeZone(abbreviation: "PST")!)

// The original iPhone went on sale on June 27, 2007

let iPhoneReleaseDate = Date(year: 2007, month: 6, day: 27) // June 27, 2007, 00:00:00 UTC

// The Stevenote where the original iPhone was announced took place

// on January 27, 2010 at 10:00 a.m. PST

let iPadStevenoteDate = Date(dateString: "2010-01-27 10:00:00 PST")

// Overloading - so that we can use it to find the difference between two Dates

func -(_ lhs: Date, _ rhs: Date) -> DateComponents

{

return Calendar.current.dateComponents([.year, .month, .day, .hour, .minute],

from: rhs,

to: lhs)

}

let timeFromAnnouncementToRelease = iPhoneReleaseDate - iPhoneStevenoteDate

timeFromAnnouncementToRelease.year // 0

timeFromAnnouncementToRelease.month // 5

timeFromAnnouncementToRelease.day // 17

timeFromAnnouncementToRelease.hour // 7

timeFromAnnouncementToRelease.minute // 0

// How long ago was the first moon landing, which took place

// on July 20, 1969, 20:18 UTC?

Date() - Date(dateString: "1969-07-20 20:18:00 UTC")

// At the time of writing, this value was a Date with the following properties:

// - year: 47

// - month: 1

// - day: 9

// - hour: 22

// - minute: 14

// Extending Int to add some syntactic magic to date components

// ------------------------------------------------------------

extension Int {

var second: DateComponents {

var components = DateComponents()

components.second = self;

return components

}

var seconds: DateComponents {

return self.second

}

var minute: DateComponents {

var components = DateComponents()

components.minute = self;

return components

}

var minutes: DateComponents {

return self.minute

}

var hour: DateComponents {

var components = DateComponents()

components.hour = self;

return components

}

var hours: DateComponents {

return self.hour

}

var day: DateComponents {

var components = DateComponents()

components.day = self;

return components

}

var days: DateComponents {

return self.day

}

var week: DateComponents {

var components = DateComponents()

components.weekOfYear = self;

return components

}

var weeks: DateComponents {

return self.week

}

var month: DateComponents {

var components = DateComponents()

components.month = self;

return components

}

var months: DateComponents {

return self.month

}

var year: DateComponents {

var components = DateComponents()

components.year = self;

return components

}

var years: DateComponents {

return self.year

}

}

// From our earlier test of Date subtraction, we know that

// there were 5 months, 17 days, and 7 hours between

// the Stevenote when the iPhone was announced and

// midnight UTC on the day it was released.

iPhoneStevenoteDate + 5.months + 17.days + 7.hours // June 27, 2007, 00:00:00 UTC

// What was the date 10 years, 9 months, 8 days, 7 hours, and 6 minutes ago?

Date() - 10.years - 9.months - 8.days - 7.hours - 6.minutes

// At the time of writing, this value was Nov 22, 2005, 6:51 a.m. EST.

// Extending DateComponents to add even more syntactic magic: fromNow and ago

// --------------------------------------------------------------------------

extension DateComponents {

var fromNow: Date {

return Calendar.current.date(byAdding: self,

to: Date())!

}

var ago: Date {

return Calendar.current.date(byAdding: -self,

to: Date())!

}

}

2.weeks.fromNow

// At the time of writing, this value was

// Sep 13, 2016, 2:55 PM

3.months.fromNow

// At the time of writing, this value was

// Nov 30, 2016, 2:55 PM

(2.months + 3.days + 4.hours + 5.minutes + 6.seconds).fromNow

// At the time of writing, this value was

// Nov 2, 2016, 7:04 PM

(2.months + 3.days + 4.hours + 5.minutes + 6.seconds).ago

// At the time of writing, this value was

// Nov 2, 2016, 7:04 PM