I have an APiI in which there is JSON from which I receive date: String, I convert it to Date type, but I need to somehow define the time difference in the extension.
Should compare this date with the current one and calculate the number of hours/days between these dates? When there are less than 24 hours between the dates, show n
hours ago, otherwise show amount of full days between the dates (n
days ago).
With my extension for date:
extension Date {
func toString(withFormat format: String = "MM.dd") -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
let str = dateFormatter.string(from: self)
return str
}
func toStringCoin(withFormat format: String = "HH") -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
let str = dateFormatter.string(from: self)
return str
}
}
My extension for String:
func toDate(withFormat format: String = "MM/dd/yyyy")-> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
let date = dateFormatter.date(from: self)
return date
}
func toDateCoin(withFormat format: String = "MM/dd/yyyy HH:mm")-> Date?{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
let date = dateFormatter.date(from: self)
return date
}
Snippet of my code how I should get this difference
Text("\(newsModel.date.convertCoin(time: newsModel.date))" + " " + "ago".localized)
(e.g. "Three days ago".)
A DateFormatter
won't do what you want. Those generate strings that represent a date, not the difference between dates.
As of iOS 13/Mac OS 10.15, there is a new formatter RelativeDateTimeFormatter
that sounds like it would do what you want. However, it doesn't seem to offer as much control of the units it uses. If you always want hours and days, but never weeks, I'm not sure if RelativeDateTimeFormatter
will give you that much control.
Here is how you could create such a thing yourself:
DateComponentsFormatter
is pretty close. It will generate strings for time intervals, with good control of the units it uses. It would be pretty easy to build a single language date offset string using a DateComponentsFormatter
(handling localization to other languages would greatly complicate it, but let's ignore that for now.)
Such a function might look like this:
func offsetFromNowString(for otherDate: Date) -> String {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full // <- Change this line to .abbreviated for shorter units
// Only include hours and days in the difference string
formatter.allowedUnits = [.hour, .day]
// Switch this to true if you want it to say "about 3 days ago"
formatter.includesApproximationPhrase = false
// Use hours or days, not both. Change value to 2 if you want both
formatter.maximumUnitCount = 1
let offset = Date().distance(to: otherDate)
let offsetSuffix = offset > 0 ? " from now" : " ago"
return (formatter.string(from: fabs(offset)) ?? "") + offsetSuffix
}
And you could test it with code like this:
let nowString = DateFormatter.localizedString(from: Date(), dateStyle: .medium, timeStyle: .short)
print("Now is \(nowString)\n")
for _ in 1...10 {
// Create a random date ±10 days from now
let random = Double.random(in: -86400*10...86400*10)
let otherDate = Date(timeIntervalSinceNow: random)
let otherDateString = DateFormatter.localizedString(from: otherDate, dateStyle: .medium, timeStyle: .short)
print("\(otherDateString) is \(offsetFromNowString(for:otherDate))")
}
That generates output like
Now is Feb 25, 2023 at 8:31 AM
Feb 17, 2023 at 4:06 PM is 8 days ago
Feb 21, 2023 at 1:24 AM is 4 days ago
Feb 28, 2023 at 1:14 PM is 3 days from now
Feb 18, 2023 at 2:04 PM is 7 days ago
Feb 27, 2023 at 8:51 AM is 2 days from now
Feb 25, 2023 at 9:53 AM is 1 hour from now
Mar 2, 2023 at 1:39 PM is 5 days from now
Mar 1, 2023 at 3:31 PM is 4 days from now
Feb 23, 2023 at 11:25 AM is 2 days ago
Feb 20, 2023 at 7:43 PM is 5 days ago
If you change formatter.unitsStyle to .abbreviated, you get output like this:
Now is Feb 25, 2023 at 9:40 AM
Mar 3, 2023 at 4:24 AM is 6d from now
Feb 28, 2023 at 4:46 PM is 3d from now
Feb 21, 2023 at 7:01 PM is 4d ago
Feb 25, 2023 at 6:39 AM is 3h ago
Feb 22, 2023 at 3:10 AM is 3d ago
Feb 26, 2023 at 2:30 PM is 1d from now
Mar 2, 2023 at 9:20 AM is 5d from now
Feb 23, 2023 at 9:10 AM is 2d ago
Mar 2, 2023 at 9:53 PM is 6d from now
Feb 17, 2023 at 2:05 AM is 8d ago
(Note that formatters are somewhat expensive to create. If you are going to be generating a lot of "date offset strings" you'd want to refactor the code above to lazily create a DateComponentsFormatter
and reuse it.)