Date format in Swift TODAY TOMORROW YESTERDAY
Asked Answered
D

3

12

I want to show a date as Saturday June 13.

If the date is current day it should display Today like that Tomorrow, Yesterday.

I couldn't achieve both.

guard let date = Date(fromString: "16 September 2020",
                      format: "dd MMMM yyyy") else { return nil }
    
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.doesRelativeDateFormatting = true

header.titleLabel.text = dateFormatter.string(from: date)

For the above code I can show date as Today Tomorrow Yesterday but other dates are not showing Saturday June 13. I tried to apply date format dateFormatter.dateFormat = "EEEE, MMM d" for the same dateFormatter it returned nothing.

Defant answered 16/9, 2020 at 7:16 Comment(2)
The basic code seems to work find for me, 2 days ago show 14 Sep 2020, 1 day ago shows Yesterday, today shows Today, 1 day in the future shows Tomorrow and 2 days in the future shows 18 Sep 2020 - I'd be more focus on you parserHavana
Can you show the code for Date.init(fromString:format:)?Hamburg
C
31

The DateFormatter doesn't behave well when setting doesRelativeDateFormatting = true and trying to apply a custom format at the same time. So the easiest solution is to use the format given by a Style and a Locale

let relativeDateFormatter = DateFormatter()
relativeDateFormatter.timeStyle = .none
relativeDateFormatter.dateStyle = .medium
relativeDateFormatter.locale = Locale(identifier: "en_GB")
relativeDateFormatter.doesRelativeDateFormatting = true

Example

let inputFormatter = DateFormatter()
inputFormatter.dateFormat = "yyyy-MM-dd"

let dates = ["2020-09-01", "2020-09-15", "2020-09-16", "2020-09-30"].compactMap { inputFormatter.date(from: $0)}

for date in dates {
    print(relativeDateFormatter.string(from: date))
}

1 Sep 2020
Yesterday
Today
30 Sep 2020

Now if you want to apply a custom format I have not found a solution for this when using the same DateFormatter instance so we need to create a new one for the custom format and use it together with a check so we apply the custom format only when it is not Today etc

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE, MMM dd"

for date in dates {
    let string = relativeDateFormatter.string(from: date)
    if let _ = string.rangeOfCharacter(from: .decimalDigits) {
         print(dateFormatter.string(from: date))
    } else {
        print(string)
    }
}

Tuesday, Sep 01
Yesterday
Today
Wednesday, Sep 30

Caw answered 16/9, 2020 at 9:42 Comment(0)
W
8

You can also use RelativeDateFormatter. Here is an example:

let date = Date().addingTimeInterval(-4 * 24 * 60 * 60)

let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .full

let relativeDate = formatter.localizedString(for: date, relativeTo: Date())

print(relativeDate) // 4 days ago
Woolf answered 1/12, 2020 at 15:45 Comment(0)
H
0

This can be done by using different formatter configurations depending on the distance of the date.

And to make sure the format matches the user's region/preferences, you should not supply your own string to dateFormat but use setLocalizedDateFormatFromTemplate(_). The latter lets you specify the components you want (day of week, day of month, month, etc), and the OS will put them in the right order and add punctuation (e.g. "June 13" would become "13. Juni" in German).

Also, your preferred format in your question doesn't include a year, but to be able to show any date unambiguously, you could check if the year is the current one and display it if it isn't.

This function should do what you want:

func formatDate(_ date: Date) -> String {
    let calendar = Calendar.current
    let now = Date.now
    let startOfToday = calendar.startOfDay(for: now)
    let startOfDayOnDate = calendar.startOfDay(for: date)
    let formatter = DateFormatter()
    
    let daysFromToday = calendar.dateComponents([.day], from: startOfToday, to: startOfDayOnDate).day!
    
    if abs(daysFromToday) <= 1 {
        // Yesterday, today or tomorrow
        formatter.dateStyle = .full
        formatter.doesRelativeDateFormatting = true
    }
    else if calendar.component(.year, from: date) == calendar.component(.year, from: now) {
        // Another date this year
        formatter.setLocalizedDateFormatFromTemplate("EEEEdMMMM")
    }
    else {
        // Another date in another year
        formatter.setLocalizedDateFormatFromTemplate("EEEEdMMMMyyyy")
    }
    return formatter.string(from: date)
}

This test:

let dayBeforeYesterday = Date.now.addingTimeInterval(-48 * 3600)
let distantDate = Date.now.addingTimeInterval(-100_000_000)

print(formatDate(.now))
print(formatDate(dayBeforeYesterday))
print(formatDate(distantDate))

…prints this (on a device with UK locale, thus the day-month-year ordering):

Today
Friday, 15 December
Friday, 16 October 2020

Haversack answered 17/12, 2023 at 14:11 Comment(1)
Realised that my answer initially contained a bug that meant the day difference was counting the number of full 24 periods passed, not the number of midnights crossed. Fixed this by adding calls to startOfDay(for:).Haversack

© 2022 - 2025 — McMap. All rights reserved.