I have a model in Swift with an NSDate field named expirationDate and I want to calculate the hours remaining before expiration based on the current date.
Is there an easy way to do this with existing NSDate functionality?
Can't check now, but should be something like
expirationDate.timeIntervalSinceNow / 3600
Just find the number of seconds (NSTimeInterval) between the two dates ('now' and your expiration date) and divide by 60*60 = 3600:
let secondsUntilExpiration = expirationDate.timeIntervalSinceDate(NSDate());
let hoursUntilExpiration = secondsUntilExpiration / 3600
For example:
7> let now = NSDate()
now: NSDate = 2016-02-01 03:44:06 UTC
8> let expirationDate = now.dateByAddingTimeInterval(60*60*10) // ten hours from now
expirationDate: NSDate = 2016-02-01 13:44:06 UTC
9> let secondsUntilExpiration = expirationDate.timeIntervalSinceDate(NSDate());
secondsUntilExpiration: NSTimeInterval = 35991.422316968441
10> let hoursUntilExpiration = secondsUntilExpiration / 3600
hoursUntilExpiration: Double = 9.9976173102690122
// Slightly less than the 10 hours above because of the time it took me to type.
Related
The current date time today was May 9, 2020 10:03 PM, and I have a target string date with the value of 2020-05-09 22:07:30 with the format of yyyy-MM-dd HH:mm:ss.
How can I get the remaining date from that 2 date and print the value with the string of 04:30 as the range of those 2 dates are 4 minutes and 30 seconds
What I can only do is convert the milliseconds to time format like
func msToTime(ms: Int) {
let seconds = ms % 60
let minutes = ms / 60000
return String(format: "%0.2d:%0.2d",minutes,seconds)
}
Output 04:30
But I don't know how to get the range of milliseconds from today's date time to target's date time.
Or if there's any other easier way to do it?
You can use Calendar and DateComponents to easily calculate differences between dates in whatever units you desire. For example, this gets the difference in minutes and seconds:
let dateformatter = DateFormatter()
dateformatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = dateformatter.date(from: "2020-05-09 22:07:30")!
let now = Date()
let components = Calendar.current.dateComponents([.minute, .second], from: now, to: date)
print("difference: \(components.minute!):\(components.second!)")
A straightforward way, with no calculation of any kind needed:
let d1 = Date()
let s = "2020-05-09 22:07:30"
let f = DateFormatter()
f.dateFormat = "yyyy-MM-dd HH:mm:ss"
f.timeZone = TimeZone.current // or whatever
if let d2 = f.date(from: s) {
let f = DateComponentsFormatter()
f.unitsStyle = .positional
let result = f.string(from: d1, to: d2)
}
If you don't like the resulting string format of result, you can eliminate pieces of it. — However, note that this works only because no full days are involved. It isn't clear what the range of possible inputs might be or what the desired output would be if the second date were three months into the future, for example.
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.
I have two Doubles, and need to query for NSDate's with a timeinterval that matches the two doubles.. How can I convert a Double to a NSDate with time matching my Double? :) If it's possible at all..
Edit: Sorry, I was in a bit of a hurry - my apologies! The Doubles I have, represent a time of the day, like 8.00 or 17.00 .. So I need to convert that Double, to a time in an NSDate, If that makes sense :)
Simple use timeIntervalSince1970 and pass double value as parameter.
let date = NSDate(timeIntervalSince1970: doubleValue)
I found the solution with a bit of hints from your answers. Here's what I ended up with and it works perfectly:
let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)
let secondsSince1970ToYesterdayAt23 = newDate.timeIntervalSince1970
let secondsInOneHour = 3600.0
let secondsSince1970FromMidnight = secondsSince1970ToYesterdayAt23 + secondsInOneHour
let secondsFromMidnightToStartTime = (secondsInOneHour * (slider?.lowerValue)!) + secondsSince1970FromMidnight
let dateWithStartTime = NSDate(timeIntervalSince1970: secondsFromMidnightToStartTime)
let secondsFromMidnightToEndTime = (secondsInOneHour * (slider?.upperValue)!) + secondsSince1970FromMidnight
let dateWithEndTime = NSDate(timeIntervalSince1970: secondsFromMidnightToEndTime)
print("Start \(dateWithStartTime)") // Returning 2017-03-18 08:00:00 +0000
print("End: \(dateWithEndTime)") // Returning 2017-03-18 17:00:00 +0000
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)
}
I am creating an NSDate from components of hour and minute. It is in GMT and prints correctly as:
0001-01-01 07:30:00 +0000
I then want to convert this to my local timezone (CET), so I set up the NSDateFormatter like so:
formatter.timeZone = NSTimeZone.localTimeZone()
This prints (using .Longstyle):
08.13.00 GMT+0.43
This is wrong—it is supposed to be GMT+1. Printing the .localTimeZone() gives the correct offset:
Local Time Zone (Europe/Oslo (CET) offset 3600)
Edit1: By adding this extension(from an answer linked in comments) to NSDate, I can offset the timezone by hand. But then I need to set the NSDateFormatter timezone to GMT, which I don't think is right.
extension NSDate {
func toLocalTime() -> NSDate {
let timeZone = NSTimeZone.localTimeZone()
let seconds : NSTimeInterval = Double(timeZone.secondsFromGMTForDate(self))
let localDate = NSDate(timeInterval: seconds, sinceDate: self)
return localDate
}
}
Edit2: I made a test project. The expected output is for the time printed to match the offset in the timezone. Instead it adds 43 minutes.
func applicationDidFinishLaunching(aNotification: NSNotification) {
let calendar = NSCalendar.currentCalendar()
let realDateComponents = calendar.components([.Hour, .Minute], fromDate: NSDate())
guard let realDate = calendar.dateFromComponents(realDateComponents)
else{fatalError("Unable to get real date.")}
let formatter = NSDateFormatter()
formatter.timeStyle = .ShortStyle
print(realDate)
print(formatter.stringFromDate(realDate))
print(NSTimeZone.localTimeZone())
}
// OUTPUT
0001-01-01 21:03:00 +0000
21.46
Local Time Zone (Europe/Oslo (CET) offset 3600)
NSDate objects encapsulate a single point in time, independent of any
particular calendrical system or time zone. Date objects are
immutable, representing an invariant time interval relative to an
absolute reference date (00:00:00 UTC on 1 January 2001).
Apparently you're creating the date from the .Hour and .Minute components, that sets the (indeterminate) year information to 01 (not 2001).
The point in time about 2000 years ago is a pretty large time interval which probably causes the weird behavior.