how can I get relative dates in Objective-C?
If I have "today", how do I find "yesterday", "last week", "last two weeks", "one month ago", "two months ago"?
So if you have an NSDate *today = [NSDate date];, then you can use NSDateComponents to get relative dates.
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *today = [NSDate date];
NSDateComponents *yesterdayComponents = [[NSDateComponents alloc] init];
[yesterdayComponents setDay:-1];
NSDate *yesterday = [calendar dateByAddingComponents:yesterdayComponents toDate:today options:0];
[yesterdayComponents release];
NSDateComponents *twoWeeksAgoComponents = [[NSDateComponents alloc] init];
[twoWeeksAgoComponents setWeek:-2];
NSDate *twoWeeksAgo = [calendar dateByAddingComponents:twoWeeksAgoComponents toDate:today options:0];
[twoWeeksAgoComponents release];
Etc.
NSDateComponents and NSCalendar handle underflowing and overflowing automatically (unless you turn it off with the options: bit). So if today is "28 February" and you want "tomorrow", it will automatically choose either "29 February" or "1 March" depending on the year. It's pretty cool. This is also the only proper way of doing date manipulation.
Related
I want to test if two NSDate objects have the same year/month/day but different times of day. Here is my code:
NSDate *date1 = [dataDictionary1 valueForKey:#"date"];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
calendar.timeZone = [NSTimeZone systemTimeZone];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:date1];
NSDate *newDate1 = [calendar dateFromComponents:components];
NSDate *date2 = [dataDictionary2 valueForKey:#"date"];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
calendar.timeZone = [NSTimeZone systemTimeZone];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:date2];
NSDate *newDate2 = [calendar dateFromComponents:components];
However, newDate1 is returning with 2012-05-01 05:00:00 +0000 and newDate2 with 2012-05-01 04:00:00 +0000.
The hours are not zero because my time zone is not GMT, but why are the two hours not equal? Do you know a better way to test if dates with differing times are equal? (That is, more efficient than getting date components for each and testing equality for each day/month/year?)
Try this:
NSDate *date1 = [dataDictionary1 valueForKey:#"date"];
long noTime1 = [date1 timeIntervalSinceReferenceDate] / (60*60*24);
NSDate *date2 = [dataDictionary2 valueForKey:#"date"];
long noTime2 = [date2 timeIntervalSinceReferenceDate] / (60*60*24);
if (noTime1 == noTime2) {
// same date
}
Of course this only works if you simply want to compare the date portion and don't care about the actual day, month, or year values.
I just found a but in one of my apps; the problem is that I am calculating the days in a menu using initWithTimeIntervalSinceNow and adding the caculation (day*X) (X meaning the day in question).
However, the night between 27th and 28th of oct the CEST timezone will become CET thus meaning that for one day the time should be calculated by (day*X)+3600. However, I do not want to use if cases and believe there should be a better way of dealing with this.
How can I calculate future days with keeping summer/winter time in mind?
My code:
int day = (60*60*24);
NSDate *today = [NSDate date];
NSDate *days1 = [[NSDate alloc] initWithTimeIntervalSinceNow:day];
NSDate *days2 = [[NSDate alloc] initWithTimeIntervalSinceNow:(day*2)];
NSDate *days3 = [[NSDate alloc] initWithTimeIntervalSinceNow:(day*3)];
NSDate *days4 = [[NSDate alloc] initWithTimeIntervalSinceNow:(day*4)];
NSDate *days5 = [[NSDate alloc] initWithTimeIntervalSinceNow:(day*5)];
NSDate *days6 = [[NSDate alloc] initWithTimeIntervalSinceNow:(day*6)];
NSDate *days7 = [[NSDate alloc] initWithTimeIntervalSinceNow:(day*7)];
Try this:
- (NSDate *)dateByAddingXDaysFromToday:(int)days
{
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = days;
return [[NSCalendar currentCalendar] dateByAddingComponents:comps toDate:[NSDate date] options:0];
}
Then you can use:
NSDate *days1 = [self dateByAddingXDaysFromToday:1];
//etc...
It's not clear what you are trying to achieve with calculating the date objects. I'm assuming you want to create objects that refer the next seven days, same time as now.
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:1];
NSDate *now = [NSDate date];
NSDate *days1 = [calendar dateByAddingComponents:components toDate:now options:0];
NSDate *days2 = [calendar dateByAddingComponents:components toDate:days1 options:0];
NSDate *days3 = [calendar dateByAddingComponents:components toDate:days2 options:0];
....
I'm trying to create an NSDate from NSCalendar and NSDateComponents with:
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:[NSTimeZone localTimeZone]];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:[self.day intValue]];
[components setMonth:[self.month intValue]];
[components setYear:[self.year intValue]];
self.date = [calendar dateFromComponents:components];
When the day, month, year are : 9/31/2011 the NSDate is for 10/1/2011. I set the timezone for the NSCalendar after testing which didn't change the NSDate. I also tried
[calendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"GMT"]];
Which gave me back 9/30/2011. Anyone see an error in the way I'm calculating this?
Thanks.
Well, september 31 is really october first.
But a component has to know in which calendar to work. The following code is from apple
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:6];
[comps setMonth:5];
[comps setYear:2004];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [gregorian dateFromComponents:comps];
[comps release];
I've been following the apple docs as well as some code on here and I can't seem to get NSDateComponents to work correctly.
NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [calendar components:(NSHourCalendarUnit|NSMinuteCalendarUnit) fromDate:dateValue];
[calendar release];
dateValue is some date, I only care about the time of it, and the time is 8:00AM. When I check what components is after this, Hour and Minute are both nil.
I also tried inserting this, because I read that you needed to (although not every example has this):
[components setCalendar:calendar];
That code will work. Maybe it's because you're trying to access the hour and minute incorrectly. I did this and got the current hour and minute in the Console window:
NSDate *dateValue = [NSDate date];
NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [calendar components:(NSHourCalendarUnit|NSMinuteCalendarUnit) fromDate:dateValue];
NSLog(#"hour: %d, minute: %d", [components hour], [components minute]);
[calendar release];
-hour and -minute return plain integers, not NSNumber objects.
From ios8 onwards above class constant is deprecated, use below code:
NSDate *dateValue = [NSDate date];
NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *components = [calendar components:(NSCalendarUnitHour|NSCalendarUnitMinute) fromDate:dateValue];
NSLog(#"hour: %ld, minute: %d", (long)[components hour], [components minute]);
NSDate *now = [[NSDate alloc] init];
gives the current date.
However if the phone calendar is not Gregorian (on the emulator there is also Japanese and Buddhist), the current date will not be Gregorian.
The question now is how to convert to a Gregorian date or make sure it will be in the Gregorian format from the beginning. This is crucial for some server communication.
Thank you!
NSDate just represents a point in time and has no format in itself.
To format an NSDate to e.g. a string, you should use NSDateFormatter. It has a calendar property, and if you set this property to an instance of a Gregorian calendar, the outputted format will match a Gregorian style.
NSDate *now = [NSDate date];
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setCalendar:gregorianCalendar];
[formatter setDateStyle:NSDateFormatterFullStyle];
[formatter setTimeStyle:NSDateFormatterFullStyle];
NSString *formattedDate = [formatter stringFromDate:now];
NSLog(#"%#", formattedDate);
[gregorianCalendar release];
[formatter release];
The picked answer actually I can't compare them. only display is not enough for my project.
I finally come up with a solution that covert (NSDate) currentDate -> gregorianDate
then we can compare those NSDates.
Just remember that the NSDates should be used temporary .(it do not attach with any calendar)
NSDate* currentDate = [NSDate date];
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *gregorianComponents = [gregorianCalendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:currentDate];
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:[gregorianComponents day]];
[comps setMonth:[gregorianComponents month]];
[comps setYear:[gregorianComponents year]];
[comps setHour:[gregorianComponents hour]];
[comps setMinute:[gregorianComponents minute]];
[comps setSecond:[gregorianComponents second]];
NSCalendar *currentCalendar = [NSCalendar autoupdatingCurrentCalendar];
NSDate *today = [currentCalendar dateFromComponents:comps];