-1

enter image description hereI have two dates in Date format.I have used the following code to find the difference between two dates.

extension Date {
    /// Returns the amount of years from another date
    func years(from date: Date) -> Int {
        return Calendar.current.dateComponents([.year], from: date, to: self).year ?? 0
    }
    /// Returns the amount of months from another date
    func months(from date: Date) -> Int {
        return Calendar.current.dateComponents([.month], from: date, to: self).month ?? 0
    }
    /// Returns the amount of weeks from another date
    func weeks(from date: Date) -> Int {
        return Calendar.current.dateComponents([.weekOfMonth], from: date, to: self).weekOfMonth ?? 0
    }
    /// Returns the amount of days from another date
    func days(from date: Date) -> Int {
        return Calendar.current.dateComponents([.day], from: date, to: self).day ?? 0
    }
    /// Returns the amount of hours from another date
    func hours(from date: Date) -> Int {
        return Calendar.current.dateComponents([.hour], from: date, to: self).hour ?? 0
    }
    /// Returns the amount of minutes from another date
    func minutes(from date: Date) -> Int {
        return Calendar.current.dateComponents([.minute], from: date, to: self).minute ?? 0
    }
    /// Returns the amount of seconds from another date
    func seconds(from date: Date) -> Int {
        return Calendar.current.dateComponents([.second], from: date, to: self).second ?? 0
    }
    /// Returns the a custom time interval description from another date
    func offset(from date: Date) -> String {
        if years(from: date)   > 0 { return "\(years(from: date))y"   }
        if months(from: date)  > 0 { return "\(months(from: date))M"  }
        if weeks(from: date)   > 0 { return "\(weeks(from: date))w"   }
        if days(from: date)    > 0 { return "\(days(from: date))d"    }
        if hours(from: date)   > 0 { return "\(hours(from: date))h"   }
        if minutes(from: date) > 0 { return "\(minutes(from: date))m" }
        if seconds(from: date) > 0 { return "\(seconds(from: date))s" }
        return ""
    }
    func offsetLong(from date: Date) -> String {
        if years(from: date)   > 0 { return years(from: date) > 1 ? "\(years(from: date)) yrs ago" : "\(years(from: date)) yr ago" }
        if months(from: date)  > 0 { return months(from: date) > 1 ? "\(months(from: date)) mo ago" : "\(months(from: date)) mo ago" }
        if weeks(from: date)   > 0 { return weeks(from: date) > 1 ? "\(weeks(from: date)) wks ago" : "\(weeks(from: date)) wk ago"   }
        if days(from: date)    > 0 { return days(from: date) > 1 ? "\(days(from: date)) days ago" : "\(days(from: date)) day ago" }
        if hours(from: date)   > 0 { return hours(from: date) > 1 ? "\(hours(from: date)) hrs ago" : "\(hours(from: date)) hr ago"   }
        if minutes(from: date) > 0 { return minutes(from: date) > 1 ? "\(minutes(from: date)) mins ago" : "\(minutes(from: date)) min ago" }
        if seconds(from: date) > 0 { return seconds(from: date) > 1 ? "\(seconds(from: date)) secs ago" : "\(seconds(from: date)) sec ago" }
        return ""
    }
}

I have applied this extension on two dates in EDT timezone and it is as given below:

let dateFormatterGet = DateFormatter()
                       // dateFormatterGet.timeZone = TimeZone(abbreviation: "UTC")
                        dateFormatterGet.dateFormat = "yyyy-MM-dd HH:mm:ss"

                        let dateFormatterPrint = DateFormatter()
                       // dateFormatterPrint.timeZone = TimeZone(abbreviation: "UTC")
                         dateFormatterPrint.dateFormat = "yyyy-MM-dd HH:mm:ss"

                        let date1: Date? = dateFormatterGet.date(from: date) as Date?
                        
                     //   print("date1 is",date1)

                                let now = Date()
                                
                                let dateFormatterGet1 = DateFormatter()
                                dateFormatterGet1.timeZone = TimeZone(abbreviation: "EDT")
                                dateFormatterGet1.dateFormat = "yyyy-MM-dd HH:mm:ss"
                                
                                let dateFormatterPrint1 = DateFormatter()
                                 dateFormatterPrint1.timeZone = TimeZone(abbreviation: "EDT")
                                 dateFormatterPrint1.dateFormat = "yyyy-MM-dd HH:mm:ss"
                                
                                let dat = dateFormatterPrint1.string(from: now) as String
                             
                                           
                                let date2: Date? = dateFormatterGet1.date(from: dat) as Date?
                              
                               let timeOffset3 = date2!.offsetLong(from: date1!)
                               
                                cell2.dateposted.text = timeOffset3

The time offset value is showing till hours, it is not showing in minutes or seconds. Could anyone tell me what is wrong in this conversion?

saranya
  • 3
  • 4
  • There is a built-in class DateComponentsFormatter that does exactly what you want. Why don't you use that? – Duncan C Dec 04 '20 at 18:39
  • It would be really helpful if you could a) reformat your second section of code to avoid it having huge amounts of (inconsistent) whitespace at the start of each line so we have to scroll to reach the end, and b) show the values you're using, what you expect to see, and what you actually see. – Jon Skeet Dec 04 '20 at 18:39
  • @JonSkeet could you tell me where? – saranya Dec 04 '20 at 18:41
  • @DuncanC any reference link pls? – saranya Dec 04 '20 at 18:41
  • Look up DateComponentsFormatter in the Xcode help system. – Duncan C Dec 04 '20 at 18:42
  • Will it take different timezone? – saranya Dec 04 '20 at 18:43
  • I also created a countdown timer sample project using it. See https://github.com/DuncanMC/CountdownTimer.git (It displays hours, minutes and seconds, but it shows how to use the class.) – Duncan C Dec 04 '20 at 18:44
  • Tell you where what? Where to show the values you're using? In your question - edit it to make it as clear as it can be. I'd suggest reading https://codeblog.jonskeet.uk/2010/08/29/writing-the-perfect-question/ – Jon Skeet Dec 04 '20 at 18:45
  • The difference between 2 dates does not involve time zones. A Date object captures an instant in time anywhere in the wold. – Duncan C Dec 04 '20 at 18:45
  • What is the output you are after? – Duncan C Dec 04 '20 at 18:46
  • right now it is showing till hours difference, if the difference between two dates is in minutes or seconds, it is not displaying it.Also the timezone is different.Since mine is in IST,it is showing the hours difference for me, but for them the seconds or minutes is not getting displayed. – saranya Dec 04 '20 at 18:49
  • Show some sample output. – Duncan C Dec 04 '20 at 18:57
  • Have attached a image link for it where you can the time is blank. – saranya Dec 04 '20 at 19:07
  • Don't attach an image along with cryptic words about that image. Edit your question to state the output format you are after. I don't know what "where you can the time is blank" means. Does that mean that you want to display the time, but your code isn't doing that? From looking at your code, I surmise that you want to output the largest unit, and my answer does that. – Duncan C Dec 05 '20 at 19:26

1 Answers1

1

There is a built in class, DateComponentsFormatter, that will create formatted strings from either time intervals or a pair of dates. You configure it with the units you want and the units style you want and it does the rest. It only offers certain pre-set formats (specified with a DateComponentsFormatter.UnitsStyle)

If you want to display the difference between two dates in an arbitrary format then you may need to use the Calendar function dateComponents(_:from:to:). That function takes two dates and a set of the components you want to use, and returns a DateComponents object. You'd then need to write custom code that displayed the value of your DateComponents object in your desired format, which should't be that hard.

It would help if you explained your desired output in your question.

Ok, I looked at the image. It looks like you want a string like "3 minutes ago" "7 hours ago" "Three days ago", "three months ago", etc.

Try code like this:

import Foundation

 var timeFormatter:DateComponentsFormatter = {
    let temp = DateComponentsFormatter()
    temp.allowedUnits = [.year, .month, .weekOfMonth, .day, .hour, .minute, .second]
    temp.maximumUnitCount = 1
    temp.unitsStyle = .full
    return temp
}()

let now = Date()

func timefromDateToNow(_ date: Date) -> String {
    if let output = timeFormatter.string(from: date, to: now) {
        return output + " ago"
    } else {
        return "error"
    }
}

enum Ranges: Int, CaseIterable {
    case seconds
    case minutes
    case hours
    case days
    case weeks
    case months
    case years
}

for _ in 1 ... 20 {
    var min: Double
    var max: Double = 0
    let range = Ranges.allCases.randomElement()!
    switch range {
    case .seconds:
        min = -59.999
    case .minutes: //0 ... 1 hours
        min = -3600
    case .hours: //0 ... 1 day
        min = -86400
    case .days: //0 ... 1 month
        min = -3600 * 24 * 31
    case .weeks:
        min = -3600 * 24 * 31
        max = -3600 * 24 * 7
    case .months:
        min = -3600 * 24 * 365
    case .years: // 0 ... 5 years
        min = -3600 * 24 * 365 * 5
    }
    let past = now.addingTimeInterval(Double.random(in: min ... max))
    print("Range = \(range): \(timefromDateToNow(past))")
}


var units: NSCalendar.Unit

//Note that all 3 methods of populating `units' yields the same value:

print("\nNow show that the order of OptionSet values does not matter:\n")
units = [.year, .second, .month, .hour]
print(units.rawValue)

units = [.second, .hour, .month, .year]
print(units.rawValue)


units = NSCalendar.Unit( rawValue:
                            NSCalendar.Unit.month.rawValue +
                            NSCalendar.Unit.year.rawValue +
                            NSCalendar.Unit.second.rawValue +
                            NSCalendar.Unit.hour.rawValue
)
print(units.rawValue)

That generates output like this:

Range = years: 3 years ago
Range = seconds: 56 seconds ago
Range = months: 9 months ago
Range = minutes: 27 minutes ago
Range = minutes: 34 minutes ago
Range = hours: 15 hours ago
Range = years: 1 year ago
Range = months: 2 months ago
Range = years: 1 year ago
Range = years: 3 years ago
Range = years: 3 years ago
Range = years: 3 years ago
Range = years: 2 months ago
Range = years: 2 years ago
Range = seconds: 59 seconds ago
Range = days: 6 days ago
Range = weeks: 3 weeks ago
Range = years: 11 months ago
Range = hours: 16 hours ago
Range = hours: 7 hours ago

Now show that the order of OptionSet values does not matter:

172
172
172
Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Have attached a image link to the question.Suppose the time difference between two dates is in seconds or minutes,the offset is blank.If it is in hours, then it displaying it. – saranya Dec 04 '20 at 19:01
  • It has seconds. You will only see it if you send it 2 dates that are less than a minute apart though, which is unlikely with my test data. – Duncan C Dec 04 '20 at 19:36