Home > Software engineering >  Trying to do math time calculations with SwiftUI
Trying to do math time calculations with SwiftUI

Time:11-09

I am writing a SwiftUI app with DatePicker to determine a job's cost based on duration of time. I can pick the start / end times, but the time interval functions don't seem to determine the correct time duration. I'm only interested in hours and minutes (preferably military time if that is possible) and performing math with these time differences. My ultimate goal is to compute the cost of the job (in currency) based on time to complete the job.

Also, how do I round time duration up to nearest 15 minutes before calculating job cost?

Here's my code so far:

import SwiftUI

struct ContentView: View {
    
    @State private var startTime = Date()
    @State private var endTime = Date()
    
    var body: some View {
        NavigationView {
            
            Form {
                Section(header: Text("Enter Case Times:")) {
                    DatePicker("Start Time", selection: $startTime , displayedComponents: .hourAndMinute)
                    DatePicker("End Time", selection: $endTime, in: startTime..., displayedComponents: .hourAndMinute)
                }
                Section(header: Text("Dates Selected:")) {
                    Text("Start Time = \(startTime.formatted(date: .omitted, time: .shortened))")
                    Text("End Time = \(endTime.formatted(date: .omitted, time: .shortened))")
                }
                Section(header: Text("Case Duration:")) {
                    let diffTime = Int(endTime.timeIntervalSince1970 - startTime.timeIntervalSince1970)
                    let caseHours = diffTime / 3600
                    let caseMinutes = (diffTime % 3600) / 60
                    Text("\(diffTime) seconds =  \(caseHours) h:\(caseMinutes) m")
                    
                    let interval = DateInterval(start: startTime, end: endTime)
                    Text("duration = \(interval.duration)")
                    let durationHours = interval.duration / 3600
                    let durationMinutes =
                    (interval.duration.truncatingRemainder(dividingBy: 3600)) / 60
                    Text("\(durationHours) hrs: \(durationMinutes) m")
                    
                }
            }
            .navigationTitle("DDA Rates Calculator")
        }
    }
}

I tried using timeIntervalSince1970() and DateInterval() functions but times don't appear to work correctly. For example, if I select a 3 minute difference in times, it will output 158 seconds when it should be 180 seconds.

CodePudding user response:

Because you are using timeIntervalSince1970 with your dates and dates have a seconds (and fractional seconds) component, you get 153 rather than 180 because the difference between, say, "10:00:22" and "10:03:00" is 153 seconds, not 180, even if you don't show the seconds.

However, there is no need to use timeIntervalSince1970 or the division. You already have a TimeInterval from DateInterval. You can use a DateComponentsFormatter to get your string representation.

To get to the nearest 15 minutes you can take the interval, divide by 900 seconds (15 minutes), round up and then multiply by 900 to get back to seconds.

I would use computed variables rather than having all of that procedural code in your view code.

struct ContentView: View {
    
    @State private var startTime = Date()
    @State private var endTime = Date()
    
    private var formatter:DateComponentsFormatter = {
        let df = DateComponentsFormatter()
        df.allowedUnits = [.day, .hour, .minute]
        return df
    }()
    
    var body: some View {
        NavigationView {
            
            Form {
                Section(header: Text("Enter Case Times:")) {
                    DatePicker("Start Time", selection: $startTime , displayedComponents: .hourAndMinute)
                    DatePicker("End Time", selection: $endTime, in: startTime..., displayedComponents: .hourAndMinute)
                }
                Section(header: Text("Dates Selected:")) {
                    Text("Start Time = \(startTime.formatted(date: .omitted, time: .shortened))")
                    Text("End Time = \(endTime.formatted(date: .omitted, time: .shortened))")
                }
                Section(header: Text("Case Duration:")) {
                    Text("duration = \(self.durationStr) \(self.duration)")
                    Text("duration (15m) = \(self.duration15Str) \(self.duration15)")
                }
            }
            .navigationTitle("DDA Rates Calculator")
        }
    }
    
    var duration: TimeInterval {
        guard endTime > startTime else {
            return 0
        }
        return DateInterval(start: startTime, end: endTime).duration
    }
    
    var durationStr: String {
        let duration = self.duration
        guard duration > 1 else {
            return "----"
        }
        return formatter.string(from: duration) ?? "----"
    }
    
    var duration15: TimeInterval {
        return  (self.duration/900.0).rounded(.up)*900
    }
    var duration15Str: String {
        let duration = duration15
        guard duration > 1 else {
            return "----"
        }
        return formatter.string(from: duration) ?? "----"
    }
}

enter image description here

CodePudding user response:

First off, I think I figured out how to post an image. But I have to use "Post Your Answer" button instead of "Add a comment" which is a little confusing.

But if I'm understanding you correctly, you changed the code you posted up above a bit. So I recompiled that code and the answer appears worse than before.

As an example, for a 4 minute interval I get this:

enter image description here

So what does this mean? It doesn't appear to be working correctly. Remember, AFTER I calculate the time duration, I need to plug it into a formula for currency. So I think the time needs to be a double and not of type string.

  • Related