How can I check to see if a date is inherently TOMORROW?
I don't want to add hours or anything to a date like today, because if today is already 22:59, adding too much would go over to the day after, and adding too little if the time is 12:00 would miss tomorrow.
How can I check two NSDates and ensure that one is the equivalent of tomorrow for the other?
Using NSDateComponents you can extract day/month/year components from the date representing today, ignoring the hour/minutes/seconds components, add one day, and rebuild a date corresponding to tomorrow.
So imagine you want to add exactly one day to the current date (including keeping hours/minutes/seconds information the same as the "now" date), you could add a timeInterval of 24*60*60 seconds to "now" using dateWithTimeIntervalSinceNow, but it is better (and DST-proof etc) to do it this way using NSDateComponents:
NSDateComponents* deltaComps = [[[NSDateComponents alloc] init] autorelease];
[deltaComps setDay:1];
NSDate* tomorrow = [[NSCalendar currentCalendar] dateByAddingComponents:deltaComps toDate:[NSDate date] options:0];
But if you want to generate the date corresponding to tomorrow at midnight, you could instead just retrieve the month/day/year components of the date representing now, without hours/min/secs part, and add 1 day, then rebuild a date:
// Decompose the date corresponding to "now" into Year+Month+Day components
NSUInteger units = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSDateComponents *comps = [[NSCalendar currentCalendar] components:units fromDate:[NSDate date]];
// Add one day
comps.day = comps.day + 1; // no worries: even if it is the end of the month it will wrap to the next month, see doc
// Recompose a new date, without any time information (so this will be at midnight)
NSDate *tomorrowMidnight = [[NSCalendar currentCalendar] dateFromComponents:comps];
P.S.: You can read really useful advice and stuff about date concepts in the Date and Time Programming Guide, especially here about date components.
In iOS 8 there is a convenience method on NSCalendar called isDateInTomorrow.
Objective-C
NSDate *date;
BOOL isTomorrow = [[NSCalendar currentCalendar] isDateInTomorrow:date];
Swift 3
let date: Date
let isTomorrow = Calendar.current.isDateInTomorrow(date)
Swift 2
let date: NSDate
let isTomorrow = NSCalendar.currentCalendar().isDateInTomorrow(date)
You might be able to leverage NSCalendar/Calendar to create tomorrow:
extension Calendar {
var tomorrow: Date? {
return date(byAdding: .day, value: 1, to: startOfDay(for: Date()))
}
}
Related
I am creating an iOS application that uses two UIDatePickers. They show just hours and minutes (my app still uses the month, day, and year though). In main.storyboard I have set the UIDatePickers' Date values to Current Date. And, I have set their interval to 15 minutes.
The issue is that if I don't scroll the UIDatePickers, the date value I get from them isn't in 15 minute intervals. If I scroll them I do get 15 minute intervals.
Example:
The actual time is 8:47PM. The UIDatePicker loads to 8:45PM. The date value I get from it without scrolling will be 8:47PM, not 8:45PM. If I scroll up to 8:30PM or 9:00PM, I will get those times.
I would like to get my time in 15 minute intervals. Is there a way to do this, or do I have to write a function to round to the nearest 15 minute interval?
Actually, it turns out someone has written an extension for this in Swift 3. My mistake. This solution worked perfectly for me.
https://stackoverflow.com/a/42263214/7025448
I came across this issue and succeeded in implementing a method to resolve this:
- (NSDate *)roundToNearestQuarterHour:(NSDate *)date{
NSCalendar *calendar = [NSCalendar currentCalendar];
unsigned unitFlags = NSCalendarUnitYear| NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitWeekday | NSCalendarUnitWeekdayOrdinal | NSCalendarUnitWeekOfYear;
NSDateComponents *components = [calendar components:unitFlags fromDate:date];
NSInteger roundedToQuarterHour = round((components.minute/15.0)) * 15;
components.minute = roundedToQuarterHour;
return [calendar dateFromComponents:components];
}
So you call like this:
NSDate *now = [self roundToNearestQuarterHour:[NSDate date]];
Unfortunately I come from an Objective C background and inexperienced with Swift.
You have to set the datePicker's date ideally in a function you call in viewDidLoad.
// Do your date computation here to get nearestFifteenMinuteInterval
self.yourDatePicker.date = nearestFifteenMinuteInterval
For the computation use the Date's timeInterval methods/ computed properties which use the number of seconds.
For example, the timeIntervalSince1970 is the number of seconds that elapsed since January 1, 1970 0:00:00.
Also you can use the start of the day as a reference date using this:
let START_OF_DAY: Date = {
let calendar: Calendar = Calendar(identifier: Calendar.Identifier.gregorian)
return calendar.startOfDay(for: Date())
}()
This question already has answers here:
Comparing the time of two NSDates, ignoring the date component
(3 answers)
Closed 8 years ago.
How can I find the difference between two NSDates but ignore the date and only care about the time?
So far, I've found lots of solutions to calculate the difference between NSDates but none that ignore the date and only care about the time.
e.g., The difference betweeen 01/01/01 04:35 PM and 01/09/09 04:36 PM is 1 minute
As with everything date calculation related on iOS, NSCalendar has got you covered! Specifically, you want to use components:fromDate:toDate:options: and pass NSWrapCalendarComponents as the option, which will prevent overflowing of components into the higher date components.
Try this:
int (^timeFromDate)(NSDate *) = ^(NSDate *date) {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit
fromDate:date];
return (components.hour * 60 + components.minute) * 60 + components.second;
};
int diff = timeFromDate(date1) - timeFromDate(date2);
I've been playing around with NSDates and need to compare if two dates are in the same week.
So far I've managed to use the [NSDateComponents week] to check if they are WITHIN a week of each other. However I need to be able to check instead if they are in the same week of a month, e.g. week 1 {monday-sunday}. week 2 {monday-sunday}.
Is this possible in iOS?
Yes.
While this sound suspiciously like it might be a homework assignment. I think this is what you want, but if you only want it to be part of the 3rd week (or whatever) and it doesn't matter which month it's in; you can take out the month part.
// Assume date1 and date2 are NSDates
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *date1Components = [cal components:(NSMonthCalendarUnit | NSWeekCalendarUnit) fromDate:date1];
NSDateComponents *date2Components = [cal components:(NSMonthCalendarUnit | NSWeekCalendarUnit) fromDate:date2];
if (date1Components.week == date2Components.week &&
date1Components.month == date2Components.month) {
// same week of same month
} else {
// either different week or different month (or both)
}
For more information on this, check out Apple's Date and Time Programming Guide
I'm trying to work with dates and create dates in the future, but daylight savings keeps getting in the way and messing up my times.
Here is my code to move to midnight of the first day of the next month for a date:
+ (NSDate *)firstDayOfNextMonthForDate:(NSDate*)date
{
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
calendar.timeZone = [NSTimeZone systemTimeZone];
calendar.locale = [NSLocale currentLocale];
NSDate *currentDate = [NSDate dateByAddingMonths:1 toDate:date];
NSDateComponents *components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
fromDate:currentDate];
[components setDay:1];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
return [calendar dateFromComponents:components];
}
+ (NSDate *) dateByAddingMonths: (NSInteger) monthsToAdd toDate:(NSDate*)date
{
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
calendar.timeZone = [NSTimeZone systemTimeZone];
calendar.locale = [NSLocale currentLocale];
NSDateComponents * months = [[NSDateComponents alloc] init];
[months setMonth: monthsToAdd];
return [calendar dateByAddingComponents: months toDate: date options: 0];
}
Which give the dates when I run the method iteratively on a date:
2013-02-01 00:00:00 +0000
2013-03-01 00:00:00 +0000
2013-03-31 23:00:00 +0000 should be 2013-04-01 00:00:00 +0000
2013-04-30 23:00:00 +0000 should be 2013-05-01 00:00:00 +0000
My initial thought was to not use systemTimeZone but that didn't seem to make a difference. Any ideas for how I can make the time constant and not take into account the change in daylight savings?
For a given calendar date/time, it is not possible as a general rule to predict what actual time (seconds since the epoch) that represents. Time zones change and DST rules change. It's a fact of life. DST has a tortured history in Australia. DST rules have been very unpredictable in Israel. DST rules recently changed in the US causing huge headaches for Microsoft who was storing seconds rather than calendar dates.
Never save NSDate when you mean NSDateComponents. If you mean "the first of May 2013 in London," then save "the first of May 2013 in London" in your database. Then calculate an NSDate off of that as close to the actual event as possible. Do all your calendar math using NSDateComponents if you care about calendar things (like months). Only do NSDate math if you really only care about seconds.
EDIT: For lots of very useful background, see the Date and Time Programming Guide.
And one more side note about calendar components: when I say "the first of May 2013 in London," that does not mean "midnight on the first of May." Don't go adding calendar components you don't actually mean.
Remember that what your program is printing to the log is the GMT time, not your local time. Therefore, it's correct for dates after the switch to DST in your local time zone that the GMT will have shifted by one hour.
I had the same problem, and people saying it's not actually a problem (as I saw on some related threads) doesn't help the situation. This kind of problem hurts whenever you have to deal with timezones and DST, and I always feel that I have to re-learn it every time.
I was dealing with epoch times as much as possible (it's a charting application), but there were times when I needed to use NSDate and NSCalendar (namely, for formatting the axis labels, and for marking calendar months, quarters and years). I struggled with this for a day or so, trying to set various timezones on calendars and suchlike.
In the end I found that the following line of code in my app delegate helped immensely:-
// prevent DST bugs by setting default timezone for app
if let utcZone = NSTimeZone(abbreviation: "UTC") {
NSTimeZone.setDefaultTimeZone(utcZone)
}
On top of this, the source data had to be sanitised, so whenever I used an NSDateFormatter on incoming data, I made sure I set its time zone to the correct one for the data source (in my case, it was GMT). This gets rid of nasty DST issues in the data source and ensures all the resultant NSDates can be converted nicely into epoch times without worrying about DST.
Another solution would be to check the hour component of the date, and in case it is 1 or 23 (instead of 0) just change the date by using
Calendar.current.date(byAdding: .hour, value: -1, to: currentDate)
or Calendar.current.date(byAdding: .hour, value: 1, to: currentDate) respectively.
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.
Although this is an old question and Uli has given the "correct" answer, the simplest solution IMHO is to just subtract the seconds from the date, as obtained from the calendar. Mind that this may still leave milliseconds in place.
NSDate *date = [NSDate date];
NSDateComponents *comp = [[NSCalendar currentCalendar] components:NSCalendarUnitSecond
fromDate:date];
date = [date dateByAddingTimeInterval:-comp.second];
NSDate records a single moment in time. If what you want to do is store a specific day, don't use NSDate. You'll get lots of unexpected head-aches related to time-zones, daylight savings time e.tc.
One alternative solution is to store the day as an integer in quasi-ISO standard format, like 20110915 for the 15th of September, 2011. This is guaranteed to sort in the same way as NSDate would sort.
Here is an extension to do this in Swift:
extension NSDate {
func truncateSeconds() -> NSDate {
let roundedTime = floor(self.timeIntervalSinceReferenceDate / 60) * 60
return NSDate(timeIntervalSinceReferenceDate: roundedTime)
}
}
Also Swift ...
extension Date {
func floored() -> Date {
let flooredSeconds = DateComponents(second: 0, nanosecond: 0)
return Calendar.current.nextDate(after: self,
matching: flooredSeconds,
matchingPolicy: .strict,
direction: Calendar.SearchDirection.backward)!
}
}