Swift - check if a timestamp is yesterday, today, tomorrow, or X days ago

IosSwiftNsdateNscalendar

Ios Problem Overview


I'm trying to work out how to decide if a given timestamp occurs today, or +1 / -1 days. Essentially, I'd like to do something like this (Pseudocode)

IF days_from_today(timestamp) == -1 RETURN 'Yesterday'
ELSE IF days_from_today(timestamp) == 0 RETURN 'Today'
ELSE IF days_from_today(timestamp) == 1 RETURN 'Tomorrow'
ELSE IF days_from_today(timestamp) < 1 RETURN days_from_today(timestamp) + ' days ago'
ELSE RETURN 'In ' + days_from_today(timestamp) + ' ago'

Crucially though, it needs to be in Swift and I'm struggling with the NSDate / NSCalendar objects. I started with working out the time difference like this:

let calendar = NSCalendar.currentCalendar()
let date = NSDate(timeIntervalSince1970: Double(timestamp))
let timeDifference = calendar.components([.Second,.Minute,.Day,.Hour],
    fromDate: date, toDate: NSDate(), options: NSCalendarOptions())

However comparing in this way isn't easy, because the .Day is different depending on the time of day and the timestamp. In PHP I'd just use mktime to create a new date, based on the start of the day (i.e. mktime(0,0,0)), but I'm not sure of the easiest way to do that in Swift.

Does anybody have a good idea on how to approach this? Perhaps an extension to NSDate or something similar would be best?

Ios Solutions


Solution 1 - Ios

Swift 3/4/5:

Calendar.current.isDateInToday(yourDate)
Calendar.current.isDateInYesterday(yourDate)
Calendar.current.isDateInTomorrow(yourDate)

Additionally:

Calendar.current.isDateInWeekend(yourDate)

Note that for some countries weekend may be different than Saturday-Sunday, it depends on the calendar.

You can also use autoupdatingCurrent instead of current calendar, which will track user updates. You use it the same way:

Calendar.autoupdatingCurrent.isDateInToday(yourDate)

Calendar is a type alias for the NSCalendar.

Solution 2 - Ios

Calendar has methods for all three cases

func isDateInYesterday(_ date: Date) -> Bool
func isDateInToday(_ date: Date) -> Bool
func isDateInTomorrow(_ date: Date) -> Bool

To calculate the days earlier than yesterday use

func dateComponents(_ components: Set<Calendar.Component>, 
                      from start: Date, 
                          to end: Date) -> DateComponents

pass [.day] to components and get the day property from the result.


This is a function which considers also is in for earlier and later dates by stripping the time part (Swift 3+).

func dayDifference(from interval : TimeInterval) -> String
{
    let calendar = Calendar.current
    let date = Date(timeIntervalSince1970: interval)
    if calendar.isDateInYesterday(date) { return "Yesterday" }
    else if calendar.isDateInToday(date) { return "Today" }
    else if calendar.isDateInTomorrow(date) { return "Tomorrow" }
    else {
        let startOfNow = calendar.startOfDay(for: Date())
        let startOfTimeStamp = calendar.startOfDay(for: date)
        let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
        let day = components.day!
        if day < 1 { return "\(-day) days ago" }
        else { return "In \(day) days" }
    }
}

Alternatively you could use DateFormatter for Yesterday, Today and Tomorrow to get localized strings for free

func dayDifference(from interval : TimeInterval) -> String
{
    let calendar = Calendar.current
    let date = Date(timeIntervalSince1970: interval)
    let startOfNow = calendar.startOfDay(for: Date())
    let startOfTimeStamp = calendar.startOfDay(for: date)
    let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
    let day = components.day!
    if abs(day) < 2 {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .none
        formatter.doesRelativeDateFormatting = true
        return formatter.string(from: date)
    } else if day > 1 {
        return "In \(day) days"
    } else {
        return "\(-day) days ago"
    }
}

Update:

In macOS 10.15 / iOS 13 RelativeDateTimeFormatter was introduced to return (localized) strings relative to a specific date.

Solution 3 - Ios

Swift 4 update:

let calendar = Calendar.current
let date = Date()
        
calendar.isDateInYesterday(date)
calendar.isDateInToday(date)
calendar.isDateInTomorrow(date)

Solution 4 - Ios

NSCalender has new methods that you can use directly.

NSCalendar.currentCalendar().isDateInTomorrow(NSDate())//Replace NSDate() with your date
NSCalendar.currentCalendar().isDateInYesterday()
NSCalendar.currentCalendar().isDateInTomorrow()

Hope this helps

Solution 5 - Ios

On Swift 5 and iOS 13 use the RelativeDateTimeFormatter,

let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .named 

formatter.localizedString(from: DateComponents(day: -1)) // "yesterday"
formatter.localizedString(from: DateComponents(day: 1)) // "Tomorrow"
formatter.localizedString(from: DateComponents(hour: 2)) // "in 2 hours"
formatter.localizedString(from: DateComponents(minute: 45)) // "in 45 minutes"

Solution 6 - Ios

1)According to your example you want to receive labels "Yesterday", "Today" and etc. iOS can do this by default:

https://developer.apple.com/documentation/foundation/nsdateformatter/1415848-doesrelativedateformatting?language=objc

2)If you want to compute your custom label when iOS don't add these labels by itself then alternatively you can use 2 DateFormatter objects with both doesRelativeDateFormatting == true and doesRelativeDateFormatting == false and compare if their result date strings are the same or different

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBenView Question on Stackoverflow
Solution 1 - IosKlimczakMView Answer on Stackoverflow
Solution 2 - IosvadianView Answer on Stackoverflow
Solution 3 - IosEdwardView Answer on Stackoverflow
Solution 4 - Iosnishith SinghView Answer on Stackoverflow
Solution 5 - IosrustylepordView Answer on Stackoverflow
Solution 6 - IosVyachaslav GerchicovView Answer on Stackoverflow