skip to Main Content

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?

2

Answers


  1. You’re right, that code is a bit lengthy. Here’s a cleaner and more efficient approach using Swift’s DateComponentsFormatter:

    import Foundation
    
    extension Date {
        func fuzzyDistanceToNow(includeJustNow: Bool = true, appendString: String = "") -> String {
            let formatter = DateComponentsFormatter()
            formatter.allowedUnits = [.year, .month, .day, .hour, .minute]
            formatter.unitsStyle = .short
            formatter.zeroFormattingBehavior = .dropAll
    
            if let string = formatter.string(from: self, to: Date()) {
                return string + appendString
            } else if includeJustNow {
                return "just now" + appendString
            } else {
                return ""
            }
        }
    }
    
    
    let pastDate = Date(timeIntervalSinceNow: -86400 * 3) // 3 days ago
    let fuzzyDescription = pastDate.fuzzyDistanceToNow(appendString: " ago")
    print(fuzzyDescription) // Output: "3 days ago"
    
    Login or Signup to reply.
  2. 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
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search