Getting the difference between two Dates (months/days/hours/minutes/seconds) in Swift

前端 未结 19 2315
时光说笑
时光说笑 2020-11-22 02:16

I am trying to get the difference between the current date as NSDate() and a date from a PHP time(); call for example: NSDate(timeIntervalSin

19条回答
  •  野趣味
    野趣味 (楼主)
    2020-11-22 03:06

    With Swift 3, according to your needs, you may choose one of the two following ways to solve your problem.


    1. Display the difference between two dates to the user

    You can use a DateComponentsFormatter to create strings for your app’s interface. DateComponentsFormatter has a maximumUnitCount property with the following declaration:

    var maximumUnitCount: Int { get set }
    

    Use this property to limit the number of units displayed in the resulting string. For example, with this property set to 2, instead of “1h 10m, 30s”, the resulting string would be “1h 10m”. Use this property when you are constrained for space or want to round up values to the nearest large unit.

    By setting maximumUnitCount's value to 1, you are guaranteed to display the difference in only one DateComponentsFormatter's unit (years, months, days, hours or minutes).

    The Playground code below shows how to display the difference between two dates:

    import Foundation
    
    let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
    let newDate = Date(timeIntervalSinceReferenceDate: 0)
    
    let dateComponentsFormatter = DateComponentsFormatter()
    dateComponentsFormatter.allowedUnits = [NSCalendar.Unit.year, .month, .day, .hour, .minute]
    dateComponentsFormatter.maximumUnitCount = 1
    dateComponentsFormatter.unitsStyle = DateComponentsFormatter.UnitsStyle.full
    let timeDifference = dateComponentsFormatter.string(from: oldDate, to: newDate)
    
    print(String(reflecting: timeDifference)) // prints Optional("5 hours")
    

    Note that DateComponentsFormatter rounds up the result. Therefore, a difference of 4 hours and 30 minutes will be displayed as 5 hours.

    If you need to repeat this operation, you can refactor your code:

    import Foundation
    
    struct Formatters {
    
        static let dateComponentsFormatter: DateComponentsFormatter = {
            let dateComponentsFormatter = DateComponentsFormatter()
            dateComponentsFormatter.allowedUnits = [NSCalendar.Unit.year, .month, .day, .hour, .minute]
            dateComponentsFormatter.maximumUnitCount = 1
            dateComponentsFormatter.unitsStyle = DateComponentsFormatter.UnitsStyle.full
            return dateComponentsFormatter
        }()
    
    }
    
    extension Date {
        
        func offset(from: Date) -> String? {
            return Formatters.dateComponentsFormatter.string(from: oldDate, to: self)
        }
        
    }
    
    let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
    let newDate = Date(timeIntervalSinceReferenceDate: 0)
    
    let timeDifference = newDate.offset(from: oldDate)
    print(String(reflecting: timeDifference)) // prints Optional("5 hours")
    

    2. Get the difference between two dates without formatting

    If you don't need to display with formatting the difference between two dates to the user, you can use Calendar. Calendar has a method dateComponents(_:from:to:) that has the following declaration:

    func dateComponents(_ components: Set, from start: Date, to end: Date) -> DateComponents
    

    Returns the difference between two dates.

    The Playground code below that uses dateComponents(_:from:to:) shows how to retrieve the difference between two dates by returning the difference in only one type of Calendar.Component (years, months, days, hours or minutes).

    import Foundation
    
    let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
    let newDate = Date(timeIntervalSinceReferenceDate: 0)
    
    let descendingOrderedComponents = [Calendar.Component.year, .month, .day, .hour, .minute]
    let dateComponents = Calendar.current.dateComponents(Set(descendingOrderedComponents), from: oldDate, to: newDate)
    let arrayOfTuples = descendingOrderedComponents.map { ($0, dateComponents.value(for: $0)) }
    
    for (component, value) in arrayOfTuples {
        if let value = value, value > 0 {
            print(component, value) // prints hour 4
            break
        }
    }
    

    If you need to repeat this operation, you can refactor your code:

    import Foundation
    
    extension Date {
        
        func offset(from: Date) -> (Calendar.Component, Int)? {
            let descendingOrderedComponents = [Calendar.Component.year, .month, .day, .hour, .minute]
            let dateComponents = Calendar.current.dateComponents(Set(descendingOrderedComponents), from: from, to: self)
            let arrayOfTuples = descendingOrderedComponents.map { ($0, dateComponents.value(for: $0)) }
            
            for (component, value) in arrayOfTuples {
                if let value = value, value > 0 {
                    return (component, value)
                }
            }
            
            return nil
        }
    
    }
    
    let oldDate = Date(timeIntervalSinceReferenceDate: -16200)
    let newDate = Date(timeIntervalSinceReferenceDate: 0)
    
    if let (component, value) = newDate.offset(from: oldDate) {
        print(component, value) // prints hour 4
    }
    

提交回复
热议问题