Finding the time between now and at specific time using swift 3 - swift

I am trying to find the time (in seconds) between now and a specific time during that day e.g 9:00 AM and if the time now is past this specified time in the day, I would want the time between now and that set time in the next day.
My ultimate goal is to run a function at every hour from 8:00 AM to 5:00 PM, while the application is not open/ in background. If there is a better way to do this without using a timer with an interval of 24 hours then I would really appreciate some suggestions.

For adding an hour to the current time. Use the below extension
extension Date {
func addHours(_ hours: Int) -> Date {
let seconds: TimeInterval = Double(hours) * 60 * 60
let newDate: Date = self.addingTimeInterval(seconds)
return newDate
}
}
call this function as Date().addHours(1)
To get specific time of current date
func getDateFromHour(hour: Int) -> Date {
let date = Date()
let calendar = Calendar.current
et componentsCurrent = calendar.dateComponents([.year, .month, .day], from: date)
var components = DateComponents()
components.hour = hour
components.minute = 0
components.second = 0
components.month = componentsCurrent.month
components.day = componentsCurrent.day
components.year = componentsCurrent.year
return calendar.date(from: components)!
}
Note: you should use 24-hour clock while passing the parameter hour.

Related

Swift Correct calculation of time as a proportion of day

I am trying to calculate the proportion of a day that a specific time equates to. For example, 06:00 is 0.25, 18:00 is 0.75 etc.
I am evaluating a series of dates of Date type, which were created in timeZone = "GMT". The routine below works fine. However when I evaluate a time after 23:00 for dates in DST, then the calculation goes wrong, as the time is evaluated as the next day (e.g. 23:08 is evaluated as 00:08)
Is there any way that I can recognise when the move from GMT to DST takes the date into the next day? I can then adjust the calculation accordingly.
My function for determining the proportion that the input time represents is:
func getTimeAsProportionOfDay(time: Date) -> Double {
//calculates the amount of day, between 0 and 1 given the input date
let calendar = Calendar.current
let hours = calendar.component(.hour, from: time)
let minutes = calendar.component(.minute, from: time)
let seconds = calendar.component(.second, from: time)
let totalSeconds = Double(hours * 60 * 60 + minutes * 60 + seconds)
return Double(totalSeconds) / Double(secondsInDay)
}
Also, I'm aware that my constant of secondsInDay (= 24*60*60) may not be technically correct but I'm not sure what system constant to replace it with.
Thanks.
You just need get the day after the original date and subtract a second. Then calculate the number of seconds in that date using calendar method
func ordinality(of smaller: Calendar.Component, in larger: Calendar.Component, for date: Date) -> Int?
You can make your life easier with some helpers
extension Date {
var dayAfter: Date { Calendar.current.date(byAdding: .day, value: 1, to: noon)!}
var noon: Date { Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)! }
var startOfDay: Date { Calendar.current.startOfDay(for: self) }
var endOfDay: Date { Calendar.current.date(byAdding: .init(second: -1), to: dayAfter.startOfDay)! }
}
Testing the endOfDay
Date().endOfDay // "Feb 7, 2020 at 11:59 PM"
And your method:
func getTimeAsProportionOfDay(time: Date) -> Double {
// discarding the fractional seconds
let time = Calendar.current.date(bySetting: .nanosecond, value: 0, of: time)!
return Double(Calendar.current.ordinality(of: .second, in: .day, for: time)!-1) /
Double(Calendar.current.ordinality(of: .second, in: .day, for: time.endOfDay)!-1)
}
Playground testing:
let date = DateComponents(calendar: .current, year: 2020, month: 2, day: 7, hour: 23, minute: 08).date!
date.endOfDay
let result = getTimeAsProportionOfDay(time: date) // 0.9639000451394113
Generally speaking, I would do something like this:
let date = Date()
var dayStart = Date()
var dayDuration: TimeInterval = 0
Calendar.current.dateInterval(of: .day, start: &dayStart, interval: &dayDuration, for: date)
let timeInterval = date.timeIntervalSince(dayStart)
let percentage = timeInterval / dayDuration
print(percentage)
All, thanks for your help. I think I have found a solution to this by using a fixed start of day to compare against.
func getTimeAsProportionOfDay(time: Date) -> Double {
//calculates the amount of day, between 0 and 1 given the input date
if Int(time.timeIntervalSince(tides.tideDate)) > secondsInDay { //time has been moved to next day by BST change) so return 1 for gradient
return 1.0
} else {/// this was my original code
let calendar = Calendar.current
let hours = calendar.component(.hour, from: time)
let minutes = calendar.component(.minute, from: time)
let seconds = calendar.component(.second, from: time)
let totalSeconds = Double(hours * 60 * 60 + minutes * 60 + seconds)
return Double(totalSeconds) / Double(secondsInDay)
}
}

Time and Date Calculations

Using date format "EEEE:dd:MMM:HH:mm" returns the correct date and time, however, the day of the week is incorrect. For example a 750 nautical mile voyage conducted at 7.5 knots will take 100 hours. If I use my code to calculate the arrival time using a start of, say, Friday 1 Nov at 12:00 it returns Sunday 5 Nov at 16:00. Time and date are correct but day of the week is not. Should be Tuesday.
#IBAction func Calculate(_ sender: UIButton) {
let userCalendar = Calendar.current
let dateMakerFormatter = DateFormatter()
dateMakerFormatter.dateFormat = "EEEE:dd:MMM:HH:mm"
let distance = (Distance.text! as NSString).floatValue
let speed = (GndSpd.text! as NSString).floatValue
let calcDT = ((distance / speed) * 3600)
if var date = dateMakerFormatter.date(from: (DTG.text!)) {
date = dateMakerFormatter.date(from: (DTG.text!))!
var timeAdj = DateComponents()
timeAdj.second = Int(calcDT)
if var adj = userCalendar.date(byAdding: timeAdj, to: date) {
adj = userCalendar.date(byAdding: timeAdj, to: date)!
CalcDTG.text = dateMakerFormatter.string(from: adj)
}
}
}
You should use d for Day, not D
dateMakerFormatter.dateFormat = "EEEE:dd:MMM:HH:mm"
DateFormatter
You can't say the day of the week is incorrect when you're not giving a year.
The date formatter seems to ignore the day of the week when creating a date:
let dateMakerFormatter = DateFormatter()
dateMakerFormatter.dateFormat = "EEEE:dd:MMM:HH:mm"
let date = dateMakerFormatter.date(from: "Friday:01:Nov:12:00")!
print(date) -> 2000-11-01 12:00:00 +0000
print(dateMakerFormatter.string(from: date)) -> Wednesday:01:Nov:12:00
Hey presto, you're now in the year 2000, where 5 November did fall on a Sunday.
The important takeaway you need is that you should never, ever, ever, use strings to pass around date values in your code. Use Date. If you're getting a date from an API response, change it to a date on ingestion. If you're getting one from user entry, use a date picker or other control. If you're getting one from a string the user is typing in, I'd politely suggest you're making unnecessary work for yourself, but do make sure you fill in all the details the user doesn't give you.

Convert hours form 12 hour time to 24 hour time Swift and save as an integer

I want to convert my variable for hours which is an integer into a 24 hour time system (for example, if it is 01:05:13 PM, hours will be saved as 13, minutes will be saved as 5, and seconds will be saved as 13) so that I can use it for some math later in my code to fid some differences on a scheduling app I am working on. This is my first app and I couldn't find an answer to this anywhere else so thanks for your help! Another way this code could work is getting the amount in seconds since the day has begun, if anyone knows how to do that, it would be greatly appreciated!
This is my function for getting the time and saving it as three different integers for hours, seconds, and minutes:
#IBAction func setTime() {
var date = NSDate()
//pickTimes()
var calendar = NSCalendar.current
calendar.timeZone = TimeZone(identifier: "UTC")!
var currentHour = calendar.component(.hour, from: date as Date) + 5
let currentMinutes = calendar.component(.minute, from: date as Date)
let currentSeconds = calendar.component(.second, from: date as Date)
timeText.text = ("\(currentHour):\(currentMinutes):\(currentSeconds)")
}
calendar.component(.hour, from: someDate) already gives you the time of day in 24 hour time so there's nothing else to do to solve your question.
Not sure why you are adding 5 to the hour. You set the timezone to UTC so the date will be treated as the UTC timezone. Then you add 5 to that result. That's kind of strange. If you just want the current hour in the user's locale timezone, don't change the calendar's timezone and don't add 5 to the hour.
Don't use NSDate or NSCalendar. This is Swift. Use Date and Calendar.
Updated code:
#IBAction func setTime() {
var date = Date()
//pickTimes()
var calendar = Calendar.current
var currentHour = calendar.component(.hour, from: date)
let currentMinutes = calendar.component(.minute, from: date)
let currentSeconds = calendar.component(.second, from: date)
timeText.text = ("\(currentHour):\(currentMinutes):\(currentSeconds)")
}
But it would be simpler to use a DateFormatter and set the timeStyle to .medium or maybe .long and format Date() into a string. This will give a properly localized time string.

Test whether current time of day is between two TimeIntervals

I have 2 TimeIntervals, which just represent date-agnostic times of day (e.g. 8:00 AM and 5:00 PM). So 0 represents exactly midnight, in this case, and 29,040 represents 8:04 AM. I want to check if the phone's time of day is between the two TimeIntervals.
I found a few similar Stack Overflow questions, but none of them really dealt with TimeIntervals. It seems like just using start <= Date().timeIntervalSinceReferenceDate <= end or something wouldn't work, because it would return a huge value.
What's the best way to handle this type of situation in Swift 3?
Edit: To clarify, I don't need to worry about things like daylight savings. As an example, assume that the user only wants certain things in the app to happen between the hours of X and Y, where X and Y are given to me as TimeInterval values since midnight. So it should be sufficient to check if the phone's TimeInterval since midnight on a typical day is between X and Y before completing the action.
Date().timeIntervalSinceReferenceDate returns the number of seconds since Jan 1, 2000 so no doubt it's a huge number.
It's inadvisable to store time as seconds since midnight due to this naggy little thing called Daylight Saving Time. Every year, different countries do it on different days and on different hours. For example, even though Britain and France change their clock on the same day (March 26, 2017), one makes the shift from 1AM to 2AM, the other goes from 2AM to 3AM. That's very easy to make for a mess!
Use DateComponents instead:
let calendar = Calendar.current
let startTimeComponent = DateComponents(calendar: calendar, hour: 8)
let endTimeComponent = DateComponents(calendar: calendar, hour: 17, minute: 30)
let now = Date()
let startOfToday = calendar.startOfDay(for: now)
let startTime = calendar.date(byAdding: startTimeComponent, to: startOfToday)!
let endTime = calendar.date(byAdding: endTimeComponent, to: startOfToday)!
if startTime <= now && now <= endTime {
print("between 8 AM and 5:30 PM")
} else {
print("not between 8 AM and 5:30 PM")
}
I ended up using DateComponents to calculate a TimeInterval.
let components = Calendar.current.dateComponents(
[.hour, .minute, .second], from: Date())
guard let seconds = components.second,
let minutes = components.minute,
let hours = components.hour else
{
return false
}
let currentTime = Double(seconds + minutes * 60 + hours * 60 * 60)
return startTime <= currentTime && currentTime <= endTime

Difference between two dateTimes in seconds vary due to different time zones - Swift 3

I need to find difference between current time (in seconds) and future start time (fetched from my web service in seconds).
I have coded the following :
let currentTime = Int64(Date().timeIntervalSince1970)
var time = Int64(timeArr[indexPath.row])
print("\(currentTime) vs \(time)")
time = time - currentTime
print("difference in time : \(time)")
let seconds = time % 60
let minutes = (time / 60) % 60
let hours = (time / 3600)
My console shows me this output:
1480400929 vs 1480552620
difference in time : 151691
The problem is that my current time is Tue Nov 29 2016 11:58:49 and the start time is 2016-11-30 17:37:00 so the difference in hours should come to around 30 hours whereas it takes the times in different time zones due to which it comes to around 42 hours. How do I fix this? I have gone through many questions for the same but nothing works. Any help would be appreciated.
If you want the difference between two date in Hours, Minutes and seconds you can use DateComponent like this.
let component = Calendar.current.dateComponents([.hour, .minute, .second], from: Date(), to: startDate)
let hours = component.hour ?? 0
let minutes = component.minute ?? 0
let seconds = component.second ?? 0
Edit: To get date from miliseconds use Date(timeIntervalSince1970:) like this.
let startDate = Date(timeIntervalSince1970: miliSeconds / 1000)
Also you can convert string to Date using DateFormatter like this.
let stringDate = "2016-11-30 17:37:00"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = TimeZone(abbreviation: "GMT")
if let startDate = dateFormatter.date(from: stringDate) {
print(startDate)
}