Difference between boarding time and current time in UTC in iOS , Swift - ios

I am getting Boarding Time from service ( lets say BT- Boarding Time)
I need to find out the differnce between Boarding Time and current time and then find out the difference in Hour , Min.
The condition is user may check the difference between these from any country in the world. so i used UTC to calculate but its giving correct result , kindly help me in this.
func dayStringFromTime() -> String {
let currentTimeUnix = Date().timeIntervalSince1970
let date = NSDate(timeIntervalSince1970: currentTimeUnix)
let dateFormatter = DateFormatter()
// dateFormatter.dateFormat = "HH:mm:ss"
return date.description
}
let CT = dayStringFromTime() //time1
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-mm-dd HH:mm:ss"
let CTDate = formatter.date(from: CT)
let time1 = boardingDateTime//timeformatter.date(from: CT)
let time2 = CT_Date//timeformatter.date(from: ETD)
//You can directly use from here if you have two dates
let interval = time1.timeIntervalSince(time2! as Date)
let hour = (interval ) / 3600;
let minute = interval.truncatingRemainder(dividingBy: 3600) / 60
let intervalInt = Int(interval)
print("\(intervalInt < 0 ? "-" : "+") \(Int(hour)) Hours \(Int(Int(minute))) Minutes")
let minText = Int(minute) > 0 && Int(minute) != 0 ? " \(Int(minute)) min" : (Int(minute) < 0 ? " \(Int(abs(minute))) min" : "")
let hrText = Int(hour) > 0 && Int(hour) != 0 ? " \(Int(hour)) hr" : (Int(hour) < 0 ? " \(Int(abs(hour))) hr" : "")
this url https://stackoverflow.com/a/28608779/3400991 shows the exact problem about this result, kindly help

This is way easier that you have made it out to be:
let boardingTime = Date().addingTimeInterval(3200) // the `addingTimeInterval` is for demonstration purposes only.
let now = Date()
let difference = Calendar.current.dateComponents([.hour, .minute, .second], from: now, to: boardingTime)
print("Boarding will be in: \(difference.hour!):\(difference.minute!):\(difference.second!)")

First of all, be very careful with date/time mathematics, it's not a straight linear conversion, there are lots and lots of rules which go around it and make it ... complicated.
The first thing you need is to calculate the difference between the two times, lucky for you, this is relatively easy...
var boardingTime = Date()
boardingTime = bordingTime.addingTimeInterval(Double.random(in: 0.0..<86400.0))
let now = Date()
let difference = boardingTime.timeIntervalSince(now)
This gives you the number of seconds between these two values (a positive value been the time till, a negative value been the time after)
Next, you need the hours/minutes in some form of human readable notation. It might seem tempting to just start by multiplying and dividing everything by 60, but that would be a mistake and lead you into bad habits (sure over a short range it's not bad, but you need to be very careful)
A better solution would be to use a DateComponentsFormatter...
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute]
formatter.unitsStyle = .abbreviated
formatter.string(from: difference)
Which will take care of all the "rules" for you, but, it will also localise the results, always a bonus.
The above example will print something like...
10h 28m

Related

Have I found a bug in DateComponentsFormatter?

In answering this question on how to create a "xxx <largest_time_units> ago" string based on comparing two dates, I wrote out a complete solution to the problem.
It involves using a DateComponentsFormatter with maximumUnitCount set to 1 and unitsStyle set to .full. Here is the code to create the DateComponentsFormatter:
var timeFormatter:DateComponentsFormatter = {
let temp = DateComponentsFormatter()
temp.allowedUnits = [.year, .month, .weekOfMonth, .day, .hour, .minute, .second]
temp.maximumUnitCount = 1
temp.unitsStyle = .full
return temp
}()
Then I wrote a function that uses the DateComponentsFormatter to output "xxx units ago" strings:
//Use our DateComponentsFormatter to generate a string showing "n <units> ago" where units is the largest units of the difference
//Between the now date and the specified date in te past
func timefromDateToNow(_ pastDate: Date) -> String {
if let output = timeFormatter.string(from: pastDate, to: now) {
return output + " ago"
} else {
return "error"
}
}
I calculate dates in the past with code like this:
let value = Double.random(in: min ... max)
let past = now.addingTimeInterval(value)
Strangely, for certain values of value, I'm getting the string "0 months ago". The value I was able to capture was -408754.0, which is about 4 days, 17 hours.
Why would calling timeFormatter.string(from: Date(timeIntervalSinceNow: -408754.0), to: Date()) return a string of 0 months? It should display a result of "4 days"!
If you want a reference to how far long ago, consider RelativeDateTimeFormatter, e.g.
let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .named
formatter.unitsStyle = .spellOut
formatter.formattingContext = .beginningOfSentence
let date = Date()
let longTimeAgo = date.addingTimeInterval(-10_000_000)
print(formatter.localizedString(for: longTimeAgo, relativeTo: date)) // Three months ago
let veryLongTimeAgo = date.addingTimeInterval(-100_000_000)
print(formatter.localizedString(for: veryLongTimeAgo, relativeTo: date)) // Three years ago

How to calculate time (minutes) between two dates in swift?

What do we got: Date+time (format yyyy-mm-dd hh:mm a)
What are we looking for: Time difference in minutes
What operation: NewDate - OldDate
So, I wonder how I could accomplish above goal? I would like to format the date and time to US, regardless from which locale the user has. How can I do that?
Then I will save the 'oldTime' into UserDefaults, and use it for later calculation. The goal is to put the user on delay for 5 minutes and the calculations will be performed to determine if user should be on delay or not.
Just make a function that takes two dates and compares them like this.
import UIKit
func minutesBetweenDates(_ oldDate: Date, _ newDate: Date) -> CGFloat {
//get both times sinces refrenced date and divide by 60 to get minutes
let newDateMinutes = newDate.timeIntervalSinceReferenceDate/60
let oldDateMinutes = oldDate.timeIntervalSinceReferenceDate/60
//then return the difference
return CGFloat(newDateMinutes - oldDateMinutes)
}
//Usage:
let myDateFormatter = DateFormatter()
myDateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
//You'll need both dates to compare, you can get them by just storing a Date object when you first start the timer.
//Then when you need to check it, compare it to Date()
let oldDate: Date = myDateFormatter.date(from: String("2019-06-22 11:25"))
func validateRefresh() {
//do the comparison between the old date and the now date like this.
if minutesBetweenDates(oldDate, Date()) > 5 {
//Do whatever
}
}
You can, of course, change the .dateFormat value on the date formatter to be whatever format you'd like. A great website for finding the right format is: https://nsdateformatter.com/.
You say:
I would like to format the date and time to US, regardless from which locale the user has. How can I do that?
Specify a Locale of en_US_POSIX:
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd hh:mm a"
formatter.locale = Locale(identifier: "en_US_POSIX")
The locale is not the only question.
There’s also a timezone question. For example, you're driving out of Chicago and go from Central to Eastern timezones; do you really want to consider that one hour has passed?
Do you really want to discard seconds? If you do that, the 59 seconds between going from 8:00:00pm to 8:00:59pm will be considered “zero minutes” but the one second between 8:00:59pm and 8:01:00pm will be considered “one minute”.
Frankly, if I wanted to save a locale and timezone invariant date string, I’d suggest using ISO8601DateFormatter.
Then I will save the 'oldTime' into UserDefaults, and use it for later calculation.
If that’s why you’re using this DateFormatter, I’d suggest saving the Date object directly.
UserDefaults.standard.set(oldTime, forKey: "oldTime")
And to retrieve it:
if let oldTime = UserDefaults.standard.object(forKey: "oldTime") as? Date {
...
}
In terms of calculating the number of minutes between two Date objects
let minutes = Calendar.current
.dateComponents([.minute], from: date1, to: date2)
.minute
If you want the number of seconds, you can also use timeIntervalSince:
let seconds = date2.timeIntervalSince(date1)
And if you wanted to show the amount of elapsed time as a nice localized string:
let intervalFormatter = DateComponentsFormatter()
intervalFormatter.allowedUnits = [.minute, .second]
intervalFormatter.unitsStyle = .full
let string = intervalFormatter.string(from: date1, to: date2)
I'm not convinced that your question is the best way to go about accomplishing your aim, but the code below will work.
let dateFormatterNow = DateFormatter()
dateFormatterNow.dateFormat = "yyyy-MM-dd hh:mm a"
dateFormatterNow.timeZone = TimeZone(abbreviation: "EST")
let oldDateString = "2019-06-23 12:44 p"
let oldDate = dateFormatterNow.date(from: oldDateString)
let newDateString = "2019-06-23 12:54 p"
let newDate = dateFormatterNow.date(from: newDateString)
if let oldDate = oldDate, let newDate = newDate {
let diffInMins = Calendar.current.dateComponents([.minute], from: oldDate, to: newDate).minute
print(diffInMins)
}

Calculate average time between an array of dates

I have an array of objects which the app gets from a WebService, each object has a createdTime and objects are created randomly from 6 in the morning to midnight.
I want to know what is the average time between each object creation.
What is the best and most efficient way to implement it?
The dates are in this format: "CreatedTime": "2019-02-18T22:06:30.523"
The average date interval is the time elapsed between the first and last date and divide by n-1, the number of intervals. That’s going to be most efficient.
This works because the average is equal to the sum of the intervals divided by the number of intervals. But the sum of all the intervals is equal to the difference between the first and last date.
Assuming your date strings are already in order, just grab the first and last, calculate the difference and divide.
let dateStrings = ["2019-02-18T18:06:30.523", "2019-02-18T19:06:30.523", "2019-02-18T21:06:30.523"]
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) // I’m going to assume it’s GMT; what is it really?
guard dateStrings.count > 1,
let lastDateString = dateStrings.last,
let lastDate = dateFormatter.date(from: lastDateString),
let firstDateString = dateStrings.first,
let firstDate = dateFormatter.date(from: firstDateString) else { return }
let average = lastDate.timeIntervalSince(firstDate) / Double(dateStrings.count - 1)
That’s in seconds. If you’d like a nice string format and don’t care about milliseconds, the DateComponentsFormatter is convenient for localized strings:
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [.hour, .minute, .second]
dateComponentsFormatter.unitsStyle = .full
let string = dateComponentsFormatter.string(from: average)
That produces:
"1 hour, 30 minutes"
Or you can, less efficiently, build the dates array:
let dateStrings = ["2019-02-18T18:06:30.523", "2019-02-18T19:06:30.523", "2019-02-18T21:06:30.523"]
guard dateStrings.count > 1 else { return }
let dates = dateStrings.map { dateFormatter.date(from: $0)! }
Then you could build an array of intervals between those dates:
var intervals: [TimeInterval] = []
for index in 1 ..< dates.count {
intervals.append(dates[index].timeIntervalSince(dates[index-1]))
}
And then average them:
let average = intervals.reduce(0.0, +) / Double(intervals.count)
And format to taste:
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.allowedUnits = [.hour, .minute, .second]
dateComponentsFormatter.unitsStyle = .full
let string = dateComponentsFormatter.string(from: average)

Time Duration From Labels

I've looked at all the Code on here that has to do with getting time duration. All I need to do is simply take a time that is in a label in this format hr:m:s like 20:23:04 for example. I need to take the times from currentTime.StringValue and the time from oldTime.StringValue and then have it show the duration of hours in durationOfTime.StringValue.
So for example say currentTime = 11:00:04 and oldTime = 9:00:04 then I want durationOfTime = 2hr's.
That's it right now I have a code that get's the time and stores it in the format above. However I run into all kinds of problems trying different codes on here that have to do with Time Duration.
Use 2 formatters: a DateFormatter to convert the strings to Date objects and a DateComponentFormatter to display the duration between the 2 Dates:
let currentTimeStr = "11:00:04"
let oldTimeStr = "9:00:04"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "H:mm:ss"
let dateComponentFormatter = DateComponentsFormatter()
dateComponentFormatter.allowedUnits = [.hour, .minute]
dateComponentFormatter.unitsStyle = .short
if let currentTime = dateFormatter.date(from: currentTimeStr),
let oldTime = dateFormatter.date(from: oldTimeStr),
let durationStr = dateComponentFormatter.string(from: oldTime, to: currentTime)
{
print(durationStr) // 2 hrs
}
Hope this is helpful. Edited maddy's suggestion.
let currentTime = "11:00:04"
let oldTime = "9:00:04"
let currentTimeComponents = currentTime.components(separatedBy: ":")
let oldTimeComponents = oldTime.components(separatedBy: ":")
let hrsDifference = Int(currentTimeComponents[0])! - Int(oldTimeComponents[0])!
let minutesDifference = Int(currentTimeComponents[1])! - Int(oldTimeComponents[1])!
let secondsDifference = Int(currentTimeComponents[2])! - Int(oldTimeComponents[2])!
print("hrsDifference = ",hrsDifference)
print("minutesDifference = ",minutesDifference)
print("secondsDifference = ",secondsDifference)

Toggle a stored flag on a weekly basis

I have a value I would like to switch to another once a week either in the background on when the user launches the app on whichever day even if it's more than a week later. As the user uses the app during the week, each day turns a value e.g. variable b to false but every once a week I want to change that variable back to true for any day that it's been turned to false. I have a function I tried putting in the AppDelegate's didFinishLaunchingWithOptions but I am not precisely sure how to do the check and hence it didn't work. This is my attempt:
func resetOnSunday() {
let date = NSDate()
let formatter = NSDateFormatter()
let timeFormatter = NSDateFormatter()
formatter.dateFormat = "EEEE"
let WeekDay = formatter.stringFromDate(date)
timeFormatter.dateFormat = "HH:mm a"
let time = timeFormatter.stringFromDate(date)
if time >= "00:00 AM" && time <= "00:02 AM" && WeekDay == "Sunday" {
var b = true
}
}
Does anyone know how it can be done?
First of all, you can significantly cut down your code (and make it a lot more reliable) by using NSCalendar.
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
//This will return a NSDate optional which you can check against.
let nextSunday = calendar.nextDateAfterDate(date, matchingUnit: .Weekday, value: 1, options: .MatchNextTime)
if date.timeIntervalSinceDate(nextSunday!) > 0 {
//Do your stuff here
}
Note that:
Weekday units are the numbers 1 through n, where n is the number of days in the week. For example, in the Gregorian calendar, n is 7 and Sunday is represented by 1.
Next, I would use NSUserDefaults for your value:
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setBool(true, forKey: "yourVar")
//Then to read it
defaults.boolForKey("yourVar")

Resources