Get NSDate from week and day - objective-c

I am trying to get an NSDate from a day and week post to today.
E.g. week = 2 and day = 4, which would be thursday in two weeks from now (given the week starts on Monday).
I tried a bunch of different things using NSCalendar and NSDateComponents, but nothing came close.
Searched a bit as well, but haven't found any other topic regarding my problem.
The last thing I tried was the following:
- (NSDate *)getDateForIndexPath:(NSIndexPath *)indexPath
{
int week = ((indexPath.row / 7) % 5);
int day = (indexPath.row % 7);
NSDate *referenceDate = [NSDate date];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"es_ES"]];
[calendar setTimeZone:[NSTimeZone localTimeZone]];
NSDateComponents *dateComponents = [calendar components:(NSYearCalendarUnit|NSMonthCalendarUnit | NSWeekCalendarUnit |NSWeekdayCalendarUnit) fromDate:referenceDate];
[dateComponents setWeekday:day];
[dateComponents setWeek:dateComponents.week+(week-1)];
[dateComponents setWeek:dateComponents.week];
NSDate *followingDate = [calendar dateFromComponents:dateComponents];
NSLog(#"Week: %d - Day: %d", week, day);
NSLog(#"followingDate: %#", followingDate);
return followingDate;
}
This gave me the same date over and over again, besides the fact that it sees day 0 as sunday:
2014-04-08 00:02:33.706 TVSports[79878:60b] Week: 1 - Day: 0
2014-04-08 00:02:33.707 TVSports[79878:60b] followingDate: 2014-04-11 22:00:00 +0000
2014-04-08 00:02:33.708 TVSports[79878:60b] Week: 1 - Day: 1
2014-04-08 00:02:33.708 TVSports[79878:60b] followingDate: 2014-04-05 22:00:00 +0000
2014-04-08 00:02:33.709 TVSports[79878:60b] Week: 1 - Day: 2
2014-04-08 00:02:33.709 TVSports[79878:60b] followingDate: 2014-04-06 22:00:00 +0000
2014-04-08 00:02:33.710 TVSports[79878:60b] Week: 1 - Day: 3
2014-04-08 00:02:33.710 TVSports[79878:60b] followingDate: 2014-04-07 22:00:00 +0000

This:
NSDate *referenceDate = [NSDate date];
...
NSDateComponents *dateComponents = [calendar components:(NSYearCalendarUnit|NSMonthCalendarUnit | NSWeekCalendarUnit |NSWeekdayCalendarUnit) fromDate:referenceDate];
Returns a date components populated correctly for today because you've asked for today's date, then asked the calendar to break that down as year, month, week and weekday.
Following that, this:
[dateComponents setWeekday:day];
[dateComponents setWeek:dateComponents.week+(week-1)];
[dateComponents setWeek:dateComponents.week];
Sets the day. Then it reads the current set week and adds week-1 to it (so week 1 will be this week, week 2 will be next week, etc). It then redundantly reads the week again and sets it again.
Your output shows that the code works correctly. Apple is an American company so weekday 0 is always Sunday to them. But they're aware that Sunday isn't the first day of the week in most places so your code asks for "Sunday (day 0), this week" — not "Sunday this week if we pretend that the week started on Sunday". It says Sunday is the 11th. Allowing for timezone differences, that's correct.
Notice that NSDates do not have a time zone. An NSDate is an opaque record of a particular moment in time. How you would communicate that moment is immaterial. They log in GMT for the sake of being able to say something.
You then ask for Monday, Tuesday and Wednesday and it returns the 5th, 6th and 7th. All correct.
Side note: just use:
NSCalendar *calendar = [NSCalendar currentCalendar];
To use the current system calendar.

You can do this to get the Thursday in two weeks:
typedef NS_ENUM(NSUInteger, WeekDay){
WeekDaySunday = 1,
WeekDayModay,
WeekDayTuesday,
WeekDayWensday,
WeekDayThursday,
WeekDayFriday,
WeekDaySaturday
};
NSDate *now = [NSDate date];
NSDate *beginOfThisWeek;
NSCalendar *cal = [NSCalendar currentCalendar];
[cal setFirstWeekday:WeekDayModay];
[cal rangeOfUnit:NSWeekCalendarUnit // get the beginning of this week
startDate:&beginOfThisWeek // save the beginning to this variable
interval:NULL // we do not care for the weeks length
forDate:now];
NSDateComponents *twoWeeks = [[NSDateComponents alloc] init];
twoWeeks.week = 2;
twoWeeks.day = WeekDayThursday - WeekDaySunday -1 ; // we need the difference between thursday and the beginning of the week
NSDate *thurdayInTwoWeeks = [cal dateByAddingComponents:twoWeeks
toDate:beginOfThisWeek
options:0];

Related

NSDateComponents weekOfYear returns wrong value

NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierISO8601];
[calendar setTimeZone:[NSTimeZone defaultTimeZone]];
[calendar setFirstWeekday:2];
[calendar setMinimumDaysInFirstWeek:1];
NSDateComponents *mockTodayComponents = [[NSDateComponents alloc] init];
mockTodayComponents.day = 28;
mockTodayComponents.month = 12;
mockTodayComponents.year = 2015;
NSDate *date = [calendar dateFromComponents:mockTodayComponents];
NSDateComponents *c = [calendar components:NSCalendarUnitWeekOfYear | NSCalendarUnitYearForWeekOfYear fromDate:date];
NSInteger week = [c weekOfYear];
week returns 1 instead of 53 and ofcoaurse the next year weeks enumeration wrong also because of it, what's wrong with my code or it's Apple's bug?
Your minimumDaysInFirstWeek is 1. That means that any week that has at least 1 day in the new year is the 1st week of that year. Docs
According to ISO:
There are mutually equivalent descriptions of week 01:
the week with the year's first Thursday in it (the formal ISO
definition), the week with 4 January in it, the first week with the
majority (four or more) of its days in the starting year, and the week
starting with the Monday in the period 29 December – 4 January.

Get first day of week and last day in objective-c

I want to get the fist day of current week with a specific locale for everyone.
For example in US week starts with Sunday and other countries on Monday.
I want to start on monday for everyone, this is because i want to use this for a SQLQuery.
I have this:
NSDate *weekDay = [NSDate date]; //any date in the week in which to calculate the first or last weekday
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:weekDay];
[components setDay:([components day]-([components weekday]-[[NSCalendar currentCalendar] firstWeekday]))];
NSDate *firstWeekday = [gregorian dateFromComponents:components];
NSDate *lastWeekday = [[gregorian dateFromComponents:components] dateByAddingTimeInterval:7 * 24 * 3600 - 1];
NSLog(#"first - %# \nlast - %#", firstWeekday, lastWeekday);
Which works fine if in your locale week starts with Monday but if starts with Sunday doesn't return what i want.
So imagine today is 11 October 2015
With Sunday locale will return first day of the week 11, last day, 17
With Monday locale will return first day of the week 5, last day 11
I want to return the second option wherever my app is executed.
Thanks.
Best regards.
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
cal.firstWeekday = 2;// set first week day to Monday
// 1: Sunday, 2: Monday, ..., 7:Saturday
NSDate *now = [NSDate date];
NSDate *startOfTheWeek;
NSDate *endOfWeek;
NSTimeInterval interval;
[cal rangeOfUnit:NSCalendarUnitWeekOfYear
startDate:&startOfTheWeek
interval:&interval
forDate:now];
//startOfTheWeek holds the beginning of the week
endOfWeek = [startOfTheWeek dateByAddingTimeInterval:interval - 1];
// endOfWeek now holds the last second of the last week day
[cal rangeOfUnit:NSCalendarUnitDay
startDate:&endOfWeek
interval:NULL
forDate:endOfWeek];
// endOfWeek now holds the beginning of the last week day
testing:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
NSLog(#"start: %#", [formatter stringFromDate:startOfTheWeek]);
NSLog(#"end: %#", [formatter stringFromDate:endOfWeek]);
prints
start: 12.10.15, 00:00
end: 18.10.15, 00:00
So Monday is the beginning of the week
if I set
cal.firstWeekday = 1;
it will print
start: 11.10.15, 00:00
end: 17.10.15, 00:00
Sunday is the first day of the week

Producing an NSDate with fixed time

I am trying to produce an NSDate with fixed hour and minutes. I need this to make an equal comparison with other date stored in CoreData.
So far I wrote this code:
NSDate date = [NSDate date];
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:flags fromDate:date];
NSDate* newDate = [calendar dateFromComponents:components];
however with a breakpoint in xcode4 I can see the values:
Printing description of date:
2012-01-10 11:20:47 +0000
Printing description of newDate:
2012-01-09 23:00:00 +0000
Why newDate is one day back in respect of date ?
/* EDIT */
I also have tried to set manually all the components, but calendar dateFromComponents always give back same one hour back date, seems ignoring the components.
components.hour=0;
components.minute=0;
components.second=0;
components.timeZone=[NSTimeZone localTimeZone];
This is the description of component after being set:
<NSDateComponents: 0x7464de0>
TimeZone: Europe/Rome (CET) offset 3600
Calendar Year: 2012
Month: 1
Day: 10
Hour: 0
Minute: 0
Second: 0
which is exactly what I would like to have, but the calculated date with this component is still
Printing description of newDate:
2012-01-09 23:00:00 +0000
I wonder why I cannot get a precise NSDate even with specifying all the components in an NSDateComponents. Just because NSCalendar is ignoring my requirements, what's the meaning of components ?
What am I doing wrong ?
I guess you are +01:00 time zone. Actually the NSDate always gives values in GMT. So if it is Jan 10th, 00:00, then at the same time GMT time is Jan 9th, 23:00.
Even, while printing the following line
2012-01-10 11:20:47 +0000,
it should have printed 1 hour less than your current time. Please check.
Use this....
NSDate *date = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setLocale:[NSLocale currentLocale]];
NSDateComponents *nowComponents = [gregorian components:NSYearCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:date];
NSDate* newDate = [gregorian dateFromComponents:nowComponents];
NSLog(#"%#\n%#",date,newDate);
You may have problems with time zones, try setting a time zone for the calendar.

Get an array of future NSDates

I have a date picker.
After choosing a time from this I would like to get the dates of the next 64 Mondays.
How would I go about writing a method to take a date and return an NSArray of NSDates for the next 64 Mondays from that date
for e.g.
I picked time 6:45 pm from date picker then I want to fetch next 64 mondays with there time set to that time.
Example (ARC):
NSDate *pickerDate = [NSDate date];
NSLog(#"pickerDate: %#", pickerDate);
NSDateComponents *dateComponents;
NSCalendar *calendar = [NSCalendar currentCalendar];
dateComponents = [calendar components:NSWeekdayCalendarUnit fromDate:pickerDate];
NSInteger firstMondayOrdinal = 9 - [dateComponents weekday];
dateComponents = [[NSDateComponents alloc] init];
[dateComponents setDay:firstMondayOrdinal];
NSDate *firstMondayDate = [calendar dateByAddingComponents:dateComponents toDate:pickerDate options:0];
dateComponents = [[NSDateComponents alloc] init];
[dateComponents setWeek:1];
for (int i=0; i<64; i++) {
[dateComponents setWeek:i];
NSDate *mondayDate = [calendar dateByAddingComponents:dateComponents toDate:firstMondayDate options:0];
NSLog(#"week#: %i, mondayDate: %#", i, mondayDate);
}
NSLog output:
pickerDate: 2011-12-09 20:38:25 +0000
week#: 0, mondayDate: 2011-12-12 20:38:25 +0000
week#: 1, mondayDate: 2011-12-19 20:38:25 +0000
week#: 2, mondayDate: 2011-12-26 20:38:25 +0000
week#: 3, mondayDate: 2012-01-02 20:38:25 +0000
-the remaining 60 here-
Start with the NSDate from the picker, and keep adding 24*60*60 seconds to it until it's a Monday. Add the resulting date to the result. Continue adding 7*24*60*60 seconds to the last date you added and pushing the result onto the return list until you have all 64 Mondays. Here is how you tell if a NSDate falls on Monday:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents =[gregorian components:NSWeekdayCalendarUnit fromDate:dateOfInterest];
NSInteger weekday = [weekdayComponents weekday];
if (weekday == 2) ... // 2 represents Monday
EDIT: DaveDeLong pointed out a deficiency in the above algorithm: it will shift the time two times on the days of changing to daylight savings time. Instead of counting seconds manually, use this code to add a day to NSDate:
NSDate *currentDate = [NSDate date];
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setDay:1]; // Add 1 when searching for the next Monday; add 7 when iterating 63 times
NSDate *date = [gregorian dateByAddingComponents:comps toDate:currentDate options:0];
[comps release];
You can use NSCalendar to determine what day of the week today (at the chosen time) is; bump it up to get to the next Monday, and then bump that by by 7 days 63 times to get the Mondays you seem to want.

Difference between NSDate and NSDateComponent value for the same date

I created a simple function to get first and last day of a week for a day in it.
Looking at the NSLog output i found that different values are returned from a NSDate descriptor and component day for the same date, why ?
Here NSLog outputs:
NSDATE: 2011-04-03 22:00:00 +0000, DAY COMPONENT: 4
NSDATE: 2011-04-09 22:00:00 +0000, DAY COMPONENT: 10
As you can see, NSDATE is 3 of April and day component is 4 for the first row, and respectively 9 and 10 for the second one.
Here the code:
NSDate *date = [NSDate date]; //Today is April 5th 2011
NSCalendar *cal =[[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
[cal setFirstWeekday:2]; //My week starts from Monday
//DEFINE BEGINNING OF THE WEEK
NSDate *beginningOfWeek = nil;
[cal rangeOfUnit:NSWeekCalendarUnit startDate:&beginningOfWeek interval:nil forDate:date];
NSDateComponents *beginComponents = [cal components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSWeekdayCalendarUnit) fromDate:beginningOfWeek];
//DEFINE END OF THE WEEK, WITH 6 days OFFSET FROM BEGINNING
NSDateComponents *offset = [[NSDateComponents alloc]init];
[offset setDay:6];
NSDate *endOfWeek = [cal dateByAddingComponents:offset toDate:beginningOfWeek options:0];
NSDateComponents *endComponents = [cal components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSWeekdayCalendarUnit) fromDate:endOfWeek];
NSLog(#"NSDATE: %#, DAY COMPONENT: %d",beginningOfWeek, [beginComponents day]);
NSLog(#"NSDATE: %#, DAY COMPONENT: %d",endOfWeek, [endComponents day]);
Your dates are being printed with a timezone of +0000 (UTC), while your NSCalendar instance (and therefore your NSDateComponents) is using your device's default timezone (which I would guess is UTC+2).