Swift convert time to time ago

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-03 05:53:01

问题


In Swift5, we have RelativeDateTimeFormatter

Prior to Swift5:


I'm trying to convert time to time ago, what i wanna do is:

from 1 to 15 seconds it will say " Just now "

from 60 minutes to 119 minutes it will say " an hour ago "

from 24 hours to 47 hours it will say " Yesterday "

from 7 days to 7 days and 23 hours it will say " a week ago "

I'm not sure about my counting if am i wrong feel free to fix it for me

Here is the code

    extension NSDate {

    struct Date {
        static let timeFormatter: NSDateFormatter = {
            let df = NSDateFormatter()
            df.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
            df.dateFormat = "h:mm a"
            return df
            }()
    }
    var time: String { return Date.timeFormatter.stringFromDate(self) }

    var year:    Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Year,   fromDate: self) }
    var month:   Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Month,  fromDate: self) }
    var day:     Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Day,    fromDate: self) }
    var hour:     Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Hour,    fromDate: self) }
    var minute:     Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Minute,    fromDate: self) }
    var second:     Int { return NSCalendar.autoupdatingCurrentCalendar().component(.Second,    fromDate: self) }


    struct DateComponents {
        static let formatter: NSDateComponentsFormatter = {
            let dcFormatter = NSDateComponentsFormatter()
            dcFormatter.calendar = NSCalendar(identifier: NSCalendarIdentifierISO8601)!
            dcFormatter.unitsStyle = .Full
            dcFormatter.maximumUnitCount = 1
            dcFormatter.zeroFormattingBehavior = .Default
            dcFormatter.allowsFractionalUnits = false
            dcFormatter.allowedUnits = [.Year, .Month, .Weekday, .Day, .Hour, .Minute, .Second]
            return dcFormatter
            }()
    }

    var elapsedTime: String {


        if isecond(NSDate()) {
            return "Just now"

        }

        if ishour(NSDate()) {
           return "an hour ago"

        }
        if isyesterday(NSDate()) {
            return "Yesterday"
        }
        if isweek(NSDate()) {

            return "a week ago"
        }




        return (DateComponents.formatter.stringFromTimeInterval(NSDate().timeIntervalSinceDate(self)) ?? "") + " ago"
    }
    func isyesterday(date: NSDate) -> Bool {
        return  NSCalendar.autoupdatingCurrentCalendar().isDate(self, inSameDayAsDate: date.yesterday)
    }
    var yesterday: NSDate {
        return  NSCalendar.autoupdatingCurrentCalendar().dateWithEra(1, year: year, month: month, day: day-1, hour: 0, minute: 0, second: 0, nanosecond: 0)!
    }



    func isweek(date: NSDate) -> Bool {
        return  NSCalendar.autoupdatingCurrentCalendar().isDate(self, inSameDayAsDate: date.weekago)
    }
    var weekago: NSDate {
        return  NSCalendar.autoupdatingCurrentCalendar().dateWithEra(1, year: year, month: month, day: day-7, hour: 0, minute: 0, second: 0, nanosecond: 0)!
    }

    func isecond(date: NSDate) -> Bool {
        return  NSCalendar.autoupdatingCurrentCalendar().isDate(self, inSameDayAsDate: date.secondsago)
    }
    var secondsago: NSDate {
        return  NSCalendar.autoupdatingCurrentCalendar().dateWithEra(1, year: year, month: month, day: 0, hour: 0, minute: 0, second: second-15, nanosecond: 0)!
    }

    func ishour(date: NSDate) -> Bool {
        return  NSCalendar.autoupdatingCurrentCalendar().isDate(self, inSameDayAsDate: date.hourago)
    }
    var hourago: NSDate {
        return  NSCalendar.autoupdatingCurrentCalendar().dateWithEra(1, year: year, month: month, day: 0, hour: hour-1, minute: minute-1, second: 0, nanosecond: 0)!
    }
}

here what i got in my app :

the quotes which is "an hour ago" , " Just now", etc not showing, only " Yesterday" and i'm not sure if it is from 7 days to 23 hours only!

and here is my first question link : Swift How to convert Parse createdAt time to time ago?

Please help me to fix the code, thanks to @Leo Dabus for the code.


回答1:


I'll just update the Truongky's answer for Swif 3:

extension Date {

func getElapsedInterval() -> String {

    let interval = Calendar.current.dateComponents([.year, .month, .day], from: self, to: Date())

    if let year = interval.year, year > 0 {
        return year == 1 ? "\(year)" + " " + "year ago" :
            "\(year)" + " " + "years ago"
    } else if let month = interval.month, month > 0 {
        return month == 1 ? "\(month)" + " " + "month ago" :
            "\(month)" + " " + "months ago"
    } else if let day = interval.day, day > 0 {
        return day == 1 ? "\(day)" + " " + "day ago" :
            "\(day)" + " " + "days ago"
    } else {
        return "a moment ago"

    }

}
}

If you prefer a localizable response instead of only english this code will do the work

extension Date {
func getElapsedInterval() -> String {

    var calendar = Calendar.current
    calendar.locale = Locale(identifier: Bundle.main.preferredLocalizations[0]) 
// IF THE USER HAVE THE PHONE IN SPANISH BUT YOUR APP ONLY SUPPORTS I.E. ENGLISH AND GERMAN
// WE SHOULD CHANGE THE LOCALE OF THE FORMATTER TO THE PREFERRED ONE 
// (IS THE LOCALE THAT THE USER IS SEEING THE APP), IF NOT, THIS ELAPSED TIME 
// IS GOING TO APPEAR IN SPANISH

    let formatter = DateComponentsFormatter()
    formatter.unitsStyle = .full
    formatter.maximumUnitCount = 1
    formatter.calendar = calendar

    var dateString: String?

    let interval = calendar.dateComponents([.year, .month, .weekOfYear, .day], from: self, to: Date())

    if let year = interval.year, year > 0 {
        formatter.allowedUnits = [.year] //2 years
    } else if let month = interval.month, month > 0 {
        formatter.allowedUnits = [.month] //1 month
    } else if let week = interval.weekOfYear, week > 0 {
        formatter.allowedUnits = [.weekOfMonth] //3 weeks
    } else if let day = interval.day, day > 0 {
        formatter.allowedUnits = [.day] // 6 days
    } else {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: Bundle.main.preferredLocalizations[0]) //--> IF THE USER HAVE THE PHONE IN SPANISH BUT YOUR APP ONLY SUPPORTS I.E. ENGLISH AND GERMAN WE SHOULD CHANGE THE LOCALE OF THE FORMATTER TO THE PREFERRED ONE (IS THE LOCALE THAT THE USER IS SEEING THE APP), IF NOT, THIS ELAPSED TIME IS GOING TO APPEAR IN SPANISH
        dateFormatter.dateStyle = .medium
        dateFormatter.doesRelativeDateFormatting = true

        dateString = dateFormatter.string(from: self) // IS GOING TO SHOW 'TODAY'
    }

    if dateString == nil {
        dateString = formatter.string(from: self, to: Date())
    }

    return dateString!
}



回答2:


Swift 4+ Version

extension Date {

    func timeAgoSinceDate() -> String {

        // From Time
        let fromDate = self

        // To Time
        let toDate = Date()

        // Estimation
        // Year
        if let interval = Calendar.current.dateComponents([.year], from: fromDate, to: toDate).year, interval > 0  {

            return interval == 1 ? "\(interval)" + " " + "year ago" : "\(interval)" + " " + "years ago"
        }

        // Month
        if let interval = Calendar.current.dateComponents([.month], from: fromDate, to: toDate).month, interval > 0  {

            return interval == 1 ? "\(interval)" + " " + "month ago" : "\(interval)" + " " + "months ago"
        }

        // Day
        if let interval = Calendar.current.dateComponents([.day], from: fromDate, to: toDate).day, interval > 0  {

            return interval == 1 ? "\(interval)" + " " + "day ago" : "\(interval)" + " " + "days ago"
        }

        // Hours
        if let interval = Calendar.current.dateComponents([.hour], from: fromDate, to: toDate).hour, interval > 0 {

            return interval == 1 ? "\(interval)" + " " + "hour ago" : "\(interval)" + " " + "hours ago"
        }

        // Minute
        if let interval = Calendar.current.dateComponents([.minute], from: fromDate, to: toDate).minute, interval > 0 {

            return interval == 1 ? "\(interval)" + " " + "minute ago" : "\(interval)" + " " + "minutes ago"
        }

        return "a moment ago"
    }
}

Usage

yourDate.timeAgoSinceDate()



回答3:


Change your text as you want.

extension NSDate {

    func getElapsedInterval() -> String {

        var interval = NSCalendar.currentCalendar().components(.Year, fromDate: self, toDate: NSDate(), options: []).year

        if interval > 0 {
            return interval == 1 ? "\(interval)" + " " + "year" :
            "\(interval)" + " " + "years"
        }

        interval = NSCalendar.currentCalendar().components(.Month, fromDate: self, toDate: NSDate(), options: []).month
        if interval > 0 {
            return interval == 1 ? "\(interval)" + " " + "month" :
            "\(interval)" + " " + "months"
        }

        interval = NSCalendar.currentCalendar().components(.Day, fromDate: self, toDate: NSDate(), options: []).day
        if interval > 0 {
            return interval == 1 ? "\(interval)" + " " + "day" :
            "\(interval)" + " " + "days"
        }

        interval = NSCalendar.currentCalendar().components(.Hour, fromDate: self, toDate: NSDate(), options: []).hour
        if interval > 0 {
            return interval == 1 ? "\(interval)" + " " + "hour" :
            "\(interval)" + " " + "hours"
        }

        interval = NSCalendar.currentCalendar().components(.Minute, fromDate: self, toDate: NSDate(), options: []).minute
        if interval > 0 {
            return interval == 1 ? "\(interval)" + " " + "minute" :
            "\(interval)" + " " + "minutes"
        }

        return "a moment ago"
    }
}



回答4:


Swift 3 version of truongky's code:

extension Date {

    var timeAgoSinceNow: String {
        return getTimeAgoSinceNow()
    }

    private func getTimeAgoSinceNow() -> String {

        var interval = Calendar.current.dateComponents([.year], from: self, to: Date()).year!
        if interval > 0 {
            return interval == 1 ? "\(interval)" + " year" : "\(interval)" + " years"
        }

        interval = Calendar.current.dateComponents([.month], from: self, to: Date()).month!
        if interval > 0 {
            return interval == 1 ? "\(interval)" + " month" : "\(interval)" + " months"
        }

        interval = Calendar.current.dateComponents([.day], from: self, to: Date()).day!
        if interval > 0 {
            return interval == 1 ? "\(interval)" + " day" : "\(interval)" + " days"
        }

        interval = Calendar.current.dateComponents([.hour], from: self, to: Date()).hour!
        if interval > 0 {
            return interval == 1 ? "\(interval)" + " hour" : "\(interval)" + " hours"
        }

        interval = Calendar.current.dateComponents([.minute], from: self, to: Date()).minute!
        if interval > 0 {
            return interval == 1 ? "\(interval)" + " minute" : "\(interval)" + " minutes"
        }

        return "a moment ago"
    }
}



回答5:


Using Date instead of NSDate: Answer taking from:

func timeAgoSinceDate(_ date:Date, numericDates:Bool = false) -> String {
    let calendar = NSCalendar.current
    let unitFlags: Set<Calendar.Component> = [.minute, .hour, .day, .weekOfYear, .month, .year, .second]
    let now = Date()
    let earliest = now < date ? now : date
    let latest = (earliest == now) ? date : now
    let components = calendar.dateComponents(unitFlags, from: earliest,  to: latest)

    if (components.year! >= 2) {
        return "\(components.year!) years ago"
    } else if (components.year! >= 1){
        if (numericDates){
            return "1 year ago"
        } else {
            return "Last year"
        }
    } else if (components.month! >= 2) {
        return "\(components.month!) months ago"
    } else if (components.month! >= 1){
        if (numericDates){
            return "1 month ago"
        } else {
            return "Last month"
        }
    } else if (components.weekOfYear! >= 2) {
        return "\(components.weekOfYear!) weeks ago"
    } else if (components.weekOfYear! >= 1){
        if (numericDates){
            return "1 week ago"
        } else {
            return "Last week"
        }
    } else if (components.day! >= 2) {
        return "\(components.day!) days ago"
    } else if (components.day! >= 1){
        if (numericDates){
            return "1 day ago"
        } else {
            return "Yesterday"
        }
    } else if (components.hour! >= 2) {
        return "\(components.hour!) hours ago"
    } else if (components.hour! >= 1){
        if (numericDates){
            return "1 hour ago"
        } else {
            return "An hour ago"
        }
    } else if (components.minute! >= 2) {
        return "\(components.minute!) minutes ago"
    } else if (components.minute! >= 1){
        if (numericDates){
            return "1 minute ago"
        } else {
            return "A minute ago"
        }
    } else if (components.second! >= 3) {
        return "\(components.second!) seconds ago"
    } else {
        return "Just now"
    }

}

How to use

var createdDate : String = "123232323" //time stamp
var createdDataDisplayString: String {
    let date = Date(timeIntervalSince1970: Double(self.createdDate)!).timeAgoSinceDate(numericDates: true)
    return "\(date)"
}
print(self.createdDataDisplayString)



回答6:


Swift 4.1. Pass you date in function param and function will return past time.

func getPastTime(for date : Date) -> String {

    var secondsAgo = Int(Date().timeIntervalSince(date))
    if secondsAgo < 0 {
        secondsAgo = secondsAgo * (-1)
    }

    let minute = 60
    let hour = 60 * minute
    let day = 24 * hour
    let week = 7 * day

    if secondsAgo < minute  {
        if secondsAgo < 2{
            return "just now"
        }else{
            return "\(secondsAgo) secs ago"
        }
    } else if secondsAgo < hour {
        let min = secondsAgo/minute
        if min == 1{
            return "\(min) min ago"
        }else{
            return "\(min) mins ago"
        }
    } else if secondsAgo < day {
        let hr = secondsAgo/hour
        if hr == 1{
            return "\(hr) hr ago"
        } else {
            return "\(hr) hrs ago"
        }
    } else if secondsAgo < week {
        let day = secondsAgo/day
        if day == 1{
            return "\(day) day ago"
        }else{
            return "\(day) days ago"
        }
    } else {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMM dd, hh:mm a"
        formatter.locale = Locale(identifier: "en_US")
        let strDate: String = formatter.string(from: date)
        return strDate
    }
}



回答7:


My needs are simpler, either "now" or "x ago"

Since iOS 8 you can use (NS)DateComponentsFormatter

func getElapsedInterval() -> String {

    let formatter = DateComponentsFormatter()
    formatter.allowedUnits = [.minute, .hour, .day, .month, .year]
    formatter.unitsStyle = .abbreviated
    formatter.zeroFormattingBehavior = .dropAll
    formatter.maximumUnitCount = 1

    var dateString: String?

    if self.timeIntervalSince(Date()) > -60*5 {
        dateString = NSLocalizedString("now", comment: "")
    } else {
        dateString = String.init(format: NSLocalizedString("%@ ago", comment: ""), locale: .current, formatter.string(from: self, to: Date())!)
    }

    return dateString ?? ""
}

e.g.

var date = Date.init(timeIntervalSinceNow: -60)
print(date.getElapsedInterval())
date = Date.init(timeIntervalSinceNow: -60*10)
print(date.getElapsedInterval())
date = Date.init(timeIntervalSinceNow: -60*60)
print(date.getElapsedInterval())
date = Date.init(timeIntervalSinceNow: -60*60*24)
print(date.getElapsedInterval())
date = Date.init(timeIntervalSinceNow: -60*60*24*30)
print(date.getElapsedInterval())
date = Date.init(timeIntervalSinceNow: -60*60*24*31*12)
print(date.getElapsedInterval())

now
10m ago
1h ago
1d ago
1mo ago
1y ago




回答8:


You need to add this date extension in your helper class or any in swift file. Swift 4.2 . if you want in swift 3 just uncomment the commented line and comment the next line.

And call the method as follow:

 let dateText = Date().offsetFrom(date: yourDate!)

yourDate is date that to be change

////MARK: *************** Extension Date ***************
 extension Date{


func convertToString(validDateFormatter:String) -> String {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = validDateFormatter //"dd MMM yyyy" //yyyy-mm-dd hh:mm
    //return dateFormatter.stringFromDate(self)
    return dateFormatter.string(from: self as Date)

}

func yearsFrom(date:Date) -> Int{
    //        return NSCalendar.currentCalendar.components(.Year, fromDate: date, toDate: self, options: []).year
    //        let calendar = NSCalendar.current

    //        let components = Calendar.current.dateComponents([.year], from: date, to: self as Date)

    return Calendar.current.dateComponents([.year], from: date, to: self).year!

}
func monthsFrom(date:Date) -> Int{
    //        return NSCalendar.currentCalendar.components(.Month, fromDate: date, toDate: self, options: []).month
    return Calendar.current.dateComponents([.month], from: date as Date, to: self).month!
}
func weeksFrom(date:Date) -> Int{
    //return NSCalendar.currentCalendar.components(.WeekOfYear, fromDate: date, toDate: self, options: []).weekOfYear
    return Calendar.current.dateComponents([.weekOfYear], from: date, to: self).weekOfYear!
}
func daysFrom(date:Date) -> Int{
    //        return NSCalendar.currentCalendar.components(.Day, fromDate: date, toDate: self, options: []).day
    return Calendar.current.dateComponents([.day], from: date, to: self).day!
}
func hoursFrom(date:Date) -> Int{
    //        return NSCalendar.currentCalendar.components(.Hour, fromDate: date, toDate: self, options: []).hour
    return Calendar.current.dateComponents([.hour], from: date, to: self).hour!
}
func minutesFrom(date:Date) -> Int{
    //        return NSCalendar.currentCalendar.components(.Minute, fromDate: date, toDate: self, options: []).minute
    return Calendar.current.dateComponents([.minute], from: date, to: self).minute!
}
func secondsFrom(date:Date) -> Int{
    //        return NSCalendar.currentCalendar.components(.Second, fromDate: date, toDate: self, options: []).second
    return Calendar.current.dateComponents([.second], from: date, to: self).second!
}
func offsetFrom(date:Date) -> String
{
    if yearsFrom(date: date) > 0 {
        return "\(yearsFrom(date: date))y"
    }
    if monthsFrom(date: date) > 0 {
        return "\(monthsFrom(date: date))M"
    }
    if weeksFrom(date: date) > 0{
        return "\(weeksFrom(date: date))w"
    }
    if daysFrom(date: date) > 0 {
        return "\(daysFrom(date: date))d"
    }
    if hoursFrom(date: date) > 0 {
        return "\(hoursFrom(date: date))h"
    }
    if minutesFrom(date: date) > 0 {
        return "\(minutesFrom(date: date))m"
    }
    if secondsFrom(date: date) > 0 {
        return "\(secondsFrom(date: date))s"
    }
    return ""

}

}



回答9:


Here is a solution you can copy paste

 func timeAgoSinceDate(_ date:Date, numericDates:Bool = false) -> String? {
    let calendar = NSCalendar.current
    let unitFlags: Set<Calendar.Component> = [.minute, .hour, .day, .weekOfYear, .month, .year, .second]
    let now = Date()
    let earliest = now < date ? now : date
    let latest = (earliest == now) ? date : now
    let components = calendar.dateComponents(unitFlags, from: earliest,  to: latest)

    if (components.year! >= 2) {
        return "\(components.year!)yr"
    } else if (components.year! >= 1){
        if (numericDates){
            return "1yr"
        } else {
            return "Last year"
        }
    } else if (components.month! >= 2) {
        return "\(components.month!)mo"
    } else if (components.month! >= 1){
        if (numericDates){
            return "1 mo"
        } else {
            return "Last mo"
        }
    } else if (components.weekOfYear! >= 2) {
        return "\(components.weekOfYear!) weeks"
    } else if (components.weekOfYear! >= 1){
        if (numericDates){
            return "1 week"
        } else {
            return "Last week"
        }
    } else if (components.day! >= 2) {
        return "\(components.day!) d"
    } else if (components.day! >= 1){
        if (numericDates){
            return "1 d"
        } else {
            return "1 d"
        }
    } else if (components.hour! >= 2) {
        return "\(components.hour!) hrs"
    } else if (components.hour! >= 1){
        if (numericDates){
            return "1 hr"
        } else {
            return "1 hr"
        }
    } else if (components.minute! >= 2) {
        return "\(components.minute!) m"
    } else if (components.minute! >= 1){
        if (numericDates){
            return "1 m"
        } else {
            return "2 m"
        }
    } else if (components.second! >= 3) {
        return "\(components.second!)s"
    } else {
        return "now"
    }

}

to call, simply use

    // the date you are getting information about 
let dateToReference = Date()
            if let timeAgo = timeAgoSinceDate(dateToReference) {
                //will be a String value
                print(timeAgo)

            } else {
                //just in case something goes wrong
                print("recently")

            }



回答10:


Swift 4 version of truongky's code:

And Add Optional for TimeEnd not now.

extension Date {

    func getElapsedInterval(to end: Date = Date()) -> String {

        if let interval = Calendar.current.dateComponents([Calendar.Component.year], from: self, to: end).day {
            if interval > 0 {
                return "\(interval) year\(interval == 1 ? "":"s")"
            }
        }

        if let interval = Calendar.current.dateComponents([Calendar.Component.month], from: self, to: end).month {
            if interval > 0 {
                return "\(interval) month\(interval == 1 ? "":"s")"
            }
        }

        if let interval = Calendar.current.dateComponents([Calendar.Component.weekOfMonth], from: self, to: end).weekOfMonth {
            if interval > 0 {
                return "\(interval) week\(interval == 1 ? "":"s")"
            }
        }

        if let interval = Calendar.current.dateComponents([Calendar.Component.day], from: self, to: end).day {
            if interval > 0 {
                return "\(interval) day\(interval == 1 ? "":"s")"
            }
        }

        if let interval = Calendar.current.dateComponents([Calendar.Component.hour], from: self, to: end).hour {
            if interval > 0 {
                return "\(interval) hour\(interval == 1 ? "":"s")"
            }
        }

        if let interval = Calendar.current.dateComponents([Calendar.Component.minute], from: self, to: end).minute {
            if interval > 0 {
                return "\(interval) minute\(interval == 1 ? "":"s")"
            }
        }

        return "Just now."
    }

}



回答11:


On Swift 5 use the RelativeDateTimeFormatter,

let formatter = RelativeDateTimeFormatter()

formatter.localizedString(from: DateComponents(day: -1)) // "1 day ago"
formatter.localizedString(from: DateComponents(hour: 2)) // "in 2 hours"
formatter.localizedString(from: DateComponents(minute: 45)) // "in 45 minutes"

set the dateTimeStyle get localized deictic statements e.g. -

formatter.dateTimeStyle = .named 
formatter.localizedString(from: DateComponents(day: -1)) // "yesterday"


来源:https://stackoverflow.com/questions/34457434/swift-convert-time-to-time-ago

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!