Search code examples
swiftdateuidatepickernsdatecomponents

dateComponents(_, from, to) returning wrong data until Picker touched


I have 2 DatePickers where users can select hours and minutes.

Once a Picker is changed, the "calcDuration"-function is called that is supposed to calculate the difference in time and display it on a label in hours and minutes.

basic storyboard setup

    func calcDuration()  {

    
    let diffComponents = Calendar.current.dateComponents([.minute, .hour], from: datePicker1.date, to: datePicker2.date)
    
    let hours = diffComponents.hour!
    let minutes = diffComponents.minute!


    durationLabel.text = "Δ \(hours)h : \(minutes)m"
}

If I run the app and increase the number of minutes in the lower datePicker (datePicker2) by one minute, the result of the "let minutes = diffComponents.minute!" is still zero.

Debugger-Result showing 0 minutes after DatePicker2 is changed by 1min

If I increase it further, it'll always show one minute less than the difference actually is.

Example:

Picker 1: 20:30 Picker 2: 20:31 -> calculated difference: 0 min

Picker 1: 20:30 Picker 2: 20:32 -> calculated difference: 1 min

Picker 1: 20:30 Picker 2: 20:41 -> calculated difference: 10 min

If I reduce the minute of the lower DatePicker (datePicker2), everything works fine and difference in minutes is shown correctly. Same effect when I increase the hours of datePicker2 by 1 -> result is 59min, if I reduce it by 1 hour its correct (-1h).

Example:

Picker 1: 20:30 Picker 2: 20:29 -> calculated difference: -1 min

Picker 1: 20:30 Picker 2: 20:20 -> calculated difference: -10 min

The only way to fix it is to move the upper DatePicker (datePicker1). No matter if I move the hours or the minutes of datePicker1, afterwards everything is working fine. So if I start the app, increase the hours of datePicker1 by 1h, then reduce it by 1h and finally change datePicker2 everything is working fine and calculated correctly.

Im working with iOS 14.5 / Xcode 12.5.

Thanks for your help!!

Edit:

  1. If I look at the seconds after adding one minute to datePicker2 is get a difference of 59s:

Seconds

  1. If I look at the Dates() objects, they look fine:

Date()-Objects


Solution

  • The problem is that the internal initial picker dates are very slightly different from one another. This causes a "problem" which to you is expressed as a rounding error.

    The solution is to set the picker dates to the very same date. Here's a way. In viewDidLoad, say this:

    let now = Date(timeIntervalSince1970: Double(Int(Date().timeIntervalSince1970)))
    picker1.date = now
    picker2.date = now
    

    That gets rid of the rounding error and now the pickers behave as you expect.

    Or, if you are setting the picker time yourself in code, do the same sort of thing. Eliminate the rounding error so that you are absolutely assigning the very same value to both pickers, initially.

    The point is that, one way or another, the difference between the picker dates should be in integer values only.