I'm trying to get NSDate
from UIDatePicker
, but it constantly returns me a date time with trailing 20 seconds. How can I manually set NSDate
's second to zero?
NSDate is immutable, so you cannot modify its time. But you can create a new date object that snaps to the nearest minute:
NSTimeInterval time = floor([date timeIntervalSinceReferenceDate] / 60.0) * 60.0;
NSDate *minute = [NSDate dateWithTimeIntervalSinceReferenceDate:time];
Edit to answer Uli's comment
The reference date for NSDate
is January 1, 2001, 0:00 GMT. There have been two leap seconds added since then: 2005 and 2010, so the value returned by [NSDate timeIntervalSinceReferenceDate]
should be off by two seconds.
This is not the case: timeIntervalSinceReferenceDate
is exactly synchronous to the wall time.
When answering the question I did not make sure that this is actually true. I just assumed that Mac OS would behave as UNIX time (1970 epoch) does: POSIX guarantees that each day starts at a multiple of 86,400 seconds.
Looking at the values returned from NSDate
this assumption seems to be correct but it sure would be nice to find a definite (documented) statement of that.
You can't directly manipulate the NSTimeInterval since that is the distance in seconds since the reference date, which isn't guaranteed to be a 00-second-time when divided by 60. After all, leap seconds may have been inserted to adjust for differences between solar time and UTC. Each leap second would throw you off by 1. What I do to fix the seconds of my date to 0 is:
NSDate * startDateTime = [NSDate date];
NSDateComponents * startSeconds = [[NSCalendar currentCalendar] components: NSSecondCalendarUnit fromDate: startDateTime];
startDateTime = [NSDate dateWithTimeIntervalSinceReferenceDate: [startDateTime timeIntervalSinceReferenceDate] -[startSeconds second]];
This takes care of leap seconds. I guess an even cleaner way would be to use -dateByAddingComponents:
NSDate * startDateTime = [NSDate date];
NSDateComponents * startSeconds = [[NSCalendar currentCalendar] components: NSSecondCalendarUnit fromDate: startDateTime];
[startSeconds setSecond: -[startSeconds second]];
startDateTime = [[NSCalendar currentCalendar] dateByAddingComponents: startSeconds toDate: startDateTime options: 0];
That way you're guaranteed that whatever special things -dateByAddingComponents: takes care of is accounted for as well.
Here is a Swift extension for anyone who is interested:
extension Date {
public mutating func floorSeconds() {
let calendar = Calendar.current
let components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: self)
self = calendar.date(from: components) ?? self // you can handle nil however you choose, probably safe to force unwrap in most cases anyway
}
}
Example usage:
let date = Date()
date.floorSeconds()
Using DateComponents
is much more robust than adding a time interval to a date.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With