I'm trying to write code to simply describe how much time has elapsed since an event, but described in only one term, eg. years, months, days, hours, minutes...
It shouldn't show the exact elapsed time, just a general sense of how long ago it was.
I've written this code but it feels like there should be a much cleaner and simpler way of doing this. Any suggestions?
func fuzzyDistanceToNow(includeJustNow blIncludeJustNow: Bool, appendString stAppendString: String) -> String {
let dDistance = abs(self.distance(to: Date())) //Distance to now
let iYears = trunc(dDistance/31536000)
let iMonths = trunc(dDistance/2628000)
let iDays = trunc(dDistance/86400)
let iHours = trunc(dDistance/3600)
let iMinutes = trunc(dDistance/60)
let iSeconds = trunc(dDistance) //Not really needed
var sReturn: String
if iYears >= 2 {
sReturn = iYears.formatted()+" years"
} else if iYears == 1 {
sReturn = "1 year"
} else if iMonths >= 2 {
sReturn = iMonths.formatted()+" months"
} else if iMonths == 1 {
sReturn = "1 month"
} else if iDays >= 2 {
sReturn = iDays.formatted()+" days"
} else if iDays == 1 {
sReturn = "1 day"
} else if iHours >= 2 {
sReturn = iHours.formatted()+" hours"
} else if iHours == 1 {
sReturn = "1 hour"
} else if iMinutes >= 2 {
sReturn = iMinutes.formatted()+" minutes"
} else if iMinutes == 1 {
sReturn = "1 minute"
} else if blIncludeJustNow {
sReturn = "just now"
} else {
sReturn = ""
}
if stAppendString != "" && sReturn != "just now" {
sReturn += stAppendString
}
return sReturn
}
}
I've written the above code, but it doesn't feel particularly efficient. Are there some functions or shortcuts I'm missing?
Sweeper already told you the solution in a comment, but it needs to be logged as an answer.
You want RelativeDateTimeFormatter
. The docs describe it as "A formatter that creates locale-aware string representations of a relative date or time."
This code:
let formatter = RelativeDateTimeFormatter()
for _ in 1...10 {
let value = Double.random(in: -1_000_000...1_000_000)
print(formatter.localizedString(fromTimeInterval: value))
}
Generates output like this:
19 hours ago
3 hours ago
in 5 days
2 days ago
in 1 week
2 days ago
in 6 days
1 week ago
23 hours ago
in 3 days
Editing the code that generates TimeInterval
values to cover a larger range of values:
for _ in 1...10 {
let power = Double(Int.random(in: 1...9) )
let mantissa = Double.random(in: 0...1)
let value = pow(10.0, power) * mantissa
print( formatter.localizedString(fromTimeInterval: value))
}
Yields a wider range of time spans:
in 2 seconds
in 4 days
in 11 minutes
in 1 year
in 1 week
in 22 hours
in 9 minutes
in 30 years
in 5 seconds
in 1 minute
Or, if you also set the formatter's dateTimeStyle to .named
:
formatter.dateTimeStyle = .named
You get output like this:
in 2 months
tomorrow
in 1 minute
now
in 2 hours
in 23 hours
in 15 hours
next month
in 18 hours
in 5 months
(For dates that are 1 time unit before or after the current date, .named
gives you output like "last year" and "tomorrow".)