Reputation: 8841
I have the following formatter:
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .short
dateFormatter.doesRelativeDateFormatting = true
My goal is to turn my dates into strings like:
Today at 3pm
Tomorrow at 3pm
Sun Sep 27 at 3pm
It works great for dates that are today or tomorrow, but for dates beyond that I want the following format:
"Sun Sep 27 at 4pm"
When I print the result of dateFormatter.string(from: DATE)
for a date 3 days from now, I get the following:
Sep 27, 2020 at 4 PM
Is there a way to customize the dateStyle
so I can instead get the desired string? I messed around with .full, .short, etc. but want to customize it my way.
Upvotes: 1
Views: 173
Reputation: 54466
Here is a Locale-independent solution:
Date
is relative:For this you can use Calendar functions .isDateInYesterday(date:)
etc.
private func isDateRelative(_ date: Date) -> Bool {
Calendar.current.isDateInYesterday(date)
|| Calendar.current.isDateInToday(date)
|| Calendar.current.isDateInTomorrow(date)
}
date
part with the date in the localizedFormat
but keep the time
part intact.DateFormatter
extension
extension DateFormatter {
func relativeStringWithFormat(from date: Date, localizedFormat: String) -> String {
if isDateRelative(date) {
return relativeDateTimeString(from: date)
}
let dateStr = localizedDateString(from: date, localizedFormat: localizedFormat)
let timeStr = relativeTimeString(from: date)
return dateStr + timeStr
}
private func relativeDateTimeString(from date: Date) -> String {
dateStyle = .medium
timeStyle = .short
doesRelativeDateFormatting = true
return string(from: date)
}
private func relativeDateString(from date: Date) -> String {
dateStyle = .medium
timeStyle = .none
doesRelativeDateFormatting = true
return string(from: date)
}
private func relativeTimeString(from date: Date) -> String {
relativeDateTimeString(from: date).replacingOccurrences(of: relativeDateString(from: date), with: "")
}
private func localizedDateString(from date: Date, localizedFormat: String) -> String {
setLocalizedDateFormatFromTemplate(localizedFormat)
doesRelativeDateFormatting = false
return string(from: date)
}
private func isDateRelative(_ date: Date) -> Bool {
Calendar.current.isDateInYesterday(date)
|| Calendar.current.isDateInToday(date)
|| Calendar.current.isDateInTomorrow(date)
}
}
Testing
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US")
let today = Date()
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: today)!
let nextWeek = Calendar.current.date(byAdding: .day, value: 7, to: today)!
let localizedFormat = "EEE d MMM"
print(dateFormatter.relativeStringWithFormat(from: today, localizedFormat: localizedFormat))
// Today at 6:20 PM
print(dateFormatter.relativeStringWithFormat(from: tomorrow, localizedFormat: localizedFormat))
// Tomorrow at 6:20 PM
print(dateFormatter.relativeStringWithFormat(from: nextWeek, localizedFormat: localizedFormat))
// Sat, Oct 3 at 6:20 PM
Upvotes: 1
Reputation: 2566
Here's a DateFormatter subclass that should give you what you want. Note that I handled the "Yesterday" case even though you didn't mention it in your question. Also note the if you want to display times other than just the hour portion you'll need to adjust the "ha" part of the dateFormat
string.
class MyDateFormatter: DateFormatter {
override init() {
super.init()
amSymbol = "am"
pmSymbol = "pm"
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func string(from date: Date) -> String {
setDateFormat(forDate: date)
return super.string(from: date)
}
private func setDateFormat(forDate date: Date) {
if calendar.isDateInYesterday(date) {
dateFormat = "'Yesterday' 'at' ha"
return
} else if calendar.isDateInToday(date) {
dateFormat = "'Today' 'at' ha"
return
} else if calendar.isDateInTomorrow(date) {
dateFormat = "'Tomorrow' 'at' ha"
return
}
dateFormat = "E MMM d 'at' ha"
}
}
Just use it as you would a normal DateFormatter:
let formatter = MyDateFormatter()
let dateStr = formatter.string(from: Date())
print(dateStr) // Today at 12pm
Upvotes: 1
Reputation: 71
Try it like this:
dateformater.dateformat = "E, d MMM yyyy HH:mm:ss"
Output: Wed, 12 Sep 2018 14:11:54
Upvotes: 1
Reputation: 36
The quickest way to get what you want would just be
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE MMM d 'at' ha"
If you want to ensure the AM and PM are lowercased, you could add
dateFormatter.amSymbol = "am"
dateFormatter.pmSymbol = "pm"
Upvotes: 0