Each of following types have their purposes. Date
is the simplest and best for the current date and dates based on the current date. To show an interval in seconds, use the TimeInterval
. To work with indivdual date components, use DateComponents
.
Date
Date
is an object which represent a single point of time. It is independent of any particular calendrical system or time zone.
import Foundation let date = Date()
This will generate a timestamp at the time code is executed. By default, it uses the GMT+0 timezone.
Create a date by adding time interval-in seconds - to current date
let date = Date.init(timeIntervalSinceNow: 86400)
Create a date by adding time interval – in seconds – since reference date
let date = Date.init(timeIntervalSinceReferenceDate: 86400)
Create a date by adding time interval – in seconds – since 1970
let date = Date(timeIntervalSince1970: 1577232000.0)
This type also supports comparisons with the operators (<, ==) and with the compare
method.
We will use DateFormatter
to change the way it will present to users with specific formating. A formatter providing methods for converting from Date to String and from String to Date.
let date = Date() var formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ" let defaultTimeZoneStr = formatter.string(from: date)
DateFormatter
can change attibute like dateFormat
, and timeZone
.
formatter.timeZone = TimeZone(abbreviation: "UTC") let utcTimeZoneStr = formatter.string(from: date)
If you want to see all the available formats for a DateFormatter you can go to nsdateformatter.com.
Also you can setup date formatters via a style for the date using the dateStyle
property and time property timeStyle
. You have a choice of .full
, .long
, .medium
, .short
and .none
.
On the other hand, you might want a measure of time. For this, use the type TimeInterval
. This is a measure of time using seconds. They are of Double
type. I’ll set a few constants using time intervals for day, hour and minute.
let minute:TimeInterval = 60.0 let hour:TimeInterval = 60.0 * minute let day:TimeInterval = 24 * hour
ISO8601DateFormatter
This is a standard time formatting that being used frequently on web server like Ruby on Rails. New API since iOS 10.
The format is like "2020-01-17T22:03:15Z". Or in dateFormat ""yyyy-MM-dd'T'HH:mm:ssZ". By using this class you don't have to write the dateFormat
anymore to avoid mistakes.
let date = Date(); var isoformatter = ISO8601DateFormatter.init() let timeStr = isoformatter.string(from: date) var dateFromString = isoformatter.date(from: timeStr)
DateComponents
DateComponents encapsulates the components of a date in an extendable, structured manner.
It is used to specify a date by providing the temporal components that make up a date and time in a particular calendar: hour, minutes, seconds, day, month, year, and so on. It can also be used to specify a duration of time, for example, 5 hours and 16 minutes. A DateComponents is not required to define all the component fields.
When a new instance of DateComponents is created, the date components are set to nil
.
var dateCompo = DateComponents() dateCompo.hour = 8 dateCompo.minute = 30 dateCompo.day = 0 dateCompo.calendar = Calendar.current var myDate = dateCompo.date var dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss ZZZ EEEE" var dateString = dateFormatter.string(from: myDate!)
Date components can be changed. The date components are all optional type Int
. While it is easier to change them using Date
and TimeInterval
, you might need to do it this way if you’ve created a series of controls in your UI to set a date. For example, to add five days do this:
dateCompo.day = dateCompo.day! + 5 myDate = dateCompo.date print(dateFormatter.string(from: myDate!))
DateComponentsFormatter
is all about converting a TimeInterval
or DateComponents
into a nicely formatted, human-readable String
.
let formatter = DateComponentsFormatter() formatter.unitsStyle = .full formatter.allowedUnits = [.minute, .second] formatter.string(from: 543.0) // "9 minutes, 3 seconds" formatter.unitsStyle = .abbreviated formatter.string(from: 123.0) // "2m 3s" formatter.unitsStyle = .spellOut formatter.string(from: 123.0) // "two minutes, three seconds" formatter.includesApproximationPhrase = true formatter.includesTimeRemainingPhrase = true formatter.unitsStyle = .brief formatter.string(from: 123.0) // "About 2min 3sec remaining"
Calendar
This class is the class that manipulates all the others. Using Calendar
you can work with dates based on a calendar.
var comps = DateComponents() comps.day = 17 comps.month = 1 comps.year = 2020 comps.hour = 22 comps.minute = 26 let cal = Calendar.current // components to date let date = cal.date(from: comps) // get specific components from date let comp2 = cal.dateComponents([.hour, .minute], from: Date()) // get a specific component from date let weekday = cal.component(.weekday, from: Date()) // adding 2 days to a Date let nextWeek = cal.date(byAdding: .day, value: 2, to: Date()) // return the difference in hours between 2 dates comps.day = 20 let date2 = cal.date(from: comps) let interval = cal.dateComponents([.hour], from: date!, to: date2!)
Calendar has the method func isDateInToday
to check if Date is from today
let date = Date() print(Calendar.current.isDateInToday(date))
Calendar has the method func isDate
to check if Date is from the same day
let date1 = Date() let date2 = Date() print(Calendar.current.isDate(date1, inSameDayAs: date2)) // check if from the same week let date1 = Date() let date2 = Date() print(Calendar.current.isDate(date1, equalTo: date2, toGranularity: .weekOfYear)) // check if from this month let date1 = Date() let date2 = Date() print(Calendar.current.isDate(date1, equalTo: date2, toGranularity: .month)) // check if from this year let date1 = Date() let date2 = Date() print(Calendar.current.isDate(date1, equalTo: date2, toGranularity: .year)
You can use DateComponents
to change the year, month or day of any Date
object. Here’s an example where we change all three:
let date = Date() var dateComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: date) dateComponents.day = 1 dateComponents.month = 2 dateComponents.year = 2000 let newDate = Calendar.current.date(from: dateComponents)
DateInterval
As the name implies, a DateInterval
instance defines an interval between two dates.
let now = Date() let tomorrow = now.addingTimeInterval(24.0 * 3600.0) let dateInterval = DateInterval(start: now, end: tomorrow)
It defines three properties:
start
of type Date
end
of type Date
duration
of type TimeInterval
My favorite method of the DateInterval
structure is the intersection(with:)
method. This method accepts another date interval and returns the intersection of the date intervals.
import Foundation let start1 = Date(timeIntervalSinceNow: -470482.0) let end1 = Date(timeIntervalSinceNow: 20482.0) let start2 = start1.addingTimeInterval(112560.0) let end2 = end1.addingTimeInterval(-222304.0) let dateInterval1 = DateInterval(start: start1, end: end1) let dateInterval2 = DateInterval(start: start2, end: end2) let intersection = dateInterval1.intersection(with: dateInterval2)
DateIntervalFormatter
is similar to the basic DateFormatter
, but it displays both a beginning and end date.
To use it, create a formatter and generate a string from two dates:
let formatter = DateIntervalFormatter() let currentDate = Date() let twoMinutesAgo = Calendar.current.date(byAdding: .minute, value: -2, to: currentDate) ?? currentDate formatter.string(from: currentDate, to: twoMinutesAgo) let fiveDaysAway = Calendar.current.date(byAdding: .day, value: 5, to: currentDate) ?? currentDate formatter.string(from: currentDate, to: fiveDaysAway)
Note that by default, the output will be based on the locale and time style from the device preferences.
DateIntervalFormatter
uses the same date styles that the basic date formatter does.
formatter.dateStyle = .long formatter.string(from: currentDate, to: fiveDaysAway) // 15-20 January 2020 formatter.timeStyle = .short formatter.string(from: currentDate, to: twoMinutesAgo) // 09:00-09:02 AM
Date to String to Date
Sometime we just want to save the date as string. And get it back from String to Date.
// date to string var date = Date() let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH:mm" var dateString = dateFormatter.string(from:date) // string to date let strTime = "2015-07-27 19:29" let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm" var date = formatter.date(from: strTime)
Comparing Dates
Date()
conforms to Comparable
protocol so you can simply use < , > and == to compare two dates.
if leftDate < rightDate { print("leftDate is earlier than rightDate") } else if leftDate > rightDate { print("leftDate is later than rightDate") } else if leftDate == rightDate { print("dates are equal") }
Get day of the week from date
To get the day of the week from a date in Swift, use DateFormatter:
let date = Date() let dateFormatter = DateFormatter() dateFormatter.dateFormat = "EEEE" let dayOfTheWeekString = dateFormatter.string(from: date)
This will give you the full name of the day of the week, such as "Friday".
If you want to get the number 1-7, with 1 representing Sunday and 7 representing Saturday, use dateComponents:
let date = Date() let calendar = Calendar.current let components = calendar.dateComponents([.weekday], from: date) let dayOfWeek = components.weekday
Get day of the month
To get the day of the month, you can use dateComponents:
let date = Date() let calendar = Calendar.current let components = calendar.dateComponents([.day], from: date) let dayOfMonth = components.day
Relative time
Before iOS 13
You can add DateTools or SwiftDate to your Podfile.
Or add follwoing extension to Date
extension Date { func years(from date: Date) -> Int { return Calendar.current.dateComponents([.year], from: date, to: self).year ?? 0 } func months(from date: Date) -> Int { return Calendar.current.dateComponents([.month], from: date, to: self).month ?? 0 } func weeks(from date: Date) -> Int { return Calendar.current.dateComponents([.weekOfYear], from: date, to: self).weekOfYear ?? 0 } func days(from date: Date) -> Int { return Calendar.current.dateComponents([.day], from: date, to: self).day ?? 0 } func hours(from date: Date) -> Int { return Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0 } func minutes(from date: Date) -> Int { return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0 } func seconds(from date: Date) -> Int { return Calendar.current.dateComponents([.second], from: date, to: self).second ?? 0 } var relativeTime: String { let now = Date() if now.years(from: self) > 0 { return now.years(from: self).description + " year" + { return now.years(from: self) > 1 ? "s" : "" }() + " ago" } if now.months(from: self) > 0 { return now.months(from: self).description + " month" + { return now.months(from: self) > 1 ? "s" : "" }() + " ago" } if now.weeks(from:self) > 0 { return now.weeks(from: self).description + " week" + { return now.weeks(from: self) > 1 ? "s" : "" }() + " ago" } if now.days(from: self) > 0 { if now.days(from:self) == 1 { return "Yesterday" } return now.days(from: self).description + " days ago" } if now.hours(from: self) > 0 { return "\(now.hours(from: self)) hour" + { return now.hours(from: self) > 1 ? "s" : "" }() + " ago" } if now.minutes(from: self) > 0 { return "\(now.minutes(from: self)) minute" + { return now.minutes(from: self) > 1 ? "s" : "" }() + " ago" } if now.seconds(from: self) > 0 { if now.seconds(from: self) < 15 { return "Just now" } return "\(now.seconds(from: self)) second" + { return now.seconds(from: self) > 1 ? "s" : "" }() + " ago" } return "" } }
Usage
let timeInterval = TimeInterval(-3600.0) let date = Date(timeIntervalSinceNow: timeInterval) print(date.relativeTime)
After iOS 13
At WWDC19, Apple added a new RelativeDateTimeFormatter
, which formats relative dates from the current date, for example by formatting a past date as "X days ago" or today as "today".
RelativeDateTimeFormatter requires Xcode 11 and the latest beta versions of macOS 10.15 or iOS 13 which it is not available on previous versions.
Using this new formatter is very easy. It provides three methods which you can use to do the formatting itself:
localizedString(fromTimeInterval:)
. This takes a TimeInterval
and formats the difference between the current time in the user’s device and the passed interval.localizedString(for:relativeTo:)
. You can use this method to get the time difference between two different dates.localizedString(from:)
. This methods takes a DateComponents object, so you can easily construct objects and check their time difference relative to the current time on the device.To use it, create a formatter and generate a string from two dates. RelativeDateTimeFormatter
supports different date/time and unit styles.
let formatter = RelativeDateTimeFormatter() formatter.localizedString(fromTimeInterval: 60.0) // "in 1 minute" formatter.dateTimeStyle = .named formatter.unitsStyle = .full let currentDate = Date() formatter.localizedString(for: currentDate, relativeTo: currentDate) // now let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: currentDate) ?? currentDate formatter.localizedString(for: yesterday, relativeTo: currentDate) // yesterday let fiveDays = Calendar.current.date(byAdding: .day, value: 5, to: currentDate) ?? currentDate formatter.localizedString(for: fiveDays, relativeTo: currentDate) // in 5 days
You can also use a instance of DateComponents
, which will format it based on the current date.
let minusOneDay = DateComponents(day: -1) formatter.localizedString(from: minusOneDay) // yesterday let plusOneDay = DateComponents(day: 1) formatter.localizedString(from: plusOneDay) // tomorrow
RelativeDateTimeFormatter
supports two different date/time styles, which can be used by setting them before generating the string.
formatter.dateTimeStyle = .numeric // .numeric or .named
The .numeric
style is the default style and always uses the literal definition of the date.
formatter.dateTimeStyle = .numeric formatter.localizedString(for: currentDate, relativeTo: currentDate) // in 0 seconds formatter.localizedString(for: yesterday, relativeTo: currentDate) // 1 day ago formatter.localizedString(for: fiveDays, relativeTo: currentDate) // in 5 days
The .named
style falls back to the numeric style, but when possible, uses relative names such as yesterday or tomorrow.
formatter.dateTimeStyle = .named formatter.localizedString(for: currentDate, relativeTo: currentDate) // now formatter.localizedString(for: yesterday, relativeTo: currentDate) // yesterday formatter.localizedString(for: fiveDays, relativeTo: currentDate) // in 5 days