Get an array of future NSDates - objective-c

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.

Related

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

Why is constructed NSDate from components not same as components used as input?

I am trying to set a NSDate from components; the hour is the only thing that I need to actually set, the remainder is from current date. This is my code:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
// put in date with start time
int month = [components month];
int day = [components day];
int year = [components year];
[components setHour: startTime];
[components setMinute: 0];
[components setSecond: 0];
NSDate *startDate = [gregorian dateFromComponents: components];
This is the result from the console:
Printing description of components:
Calendar Year: 2015
Month: 10
Leap month: no
Day: 3
Hour: 1730
Minute: 0
Second: 0
Printing description of startDate:
2015-10-25 15:57:12 +0000
Notice that the month and time have changed from what was supplied. My question is why did it change (for future reference) and what do I do to fix this?

NSDate intervals for yesterday

I need to filter search results based on values that were added yesterday. I have seen plenty on finding yesterday using:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:[[NSDate alloc] init]];
[components setHour:-24];
[components setMinute:0];
[components setSecond:0];
NSDate *yesterday = [cal dateByAddingComponents:components toDate:[NSDate date] options:0];
predicate = [NSPredicate predicateWithFormat:#"created_at >= %#", yesterday];
But this finds 24 hours since this exact moment in time. I need to filter yesterday as 12:01am-12:00pm. So the actual 24 hour period that was yesterday.
I'm guessing that I need to do something along the lines of:
1. Take the current date
2. Find the time from the current date to 12:01am of the same day
3. Then subtract 24 hours from that date
I feel confident I can do #3 (and #1 of course), but I'm not sure how to go about #2. I maybe over thinking it but I can't seem to grasp how to say: "Ok, it's 8:03am, I need to remove 8 hours and 2 minutes which will put me at 12:01am".
Start with some date of today, for example "now":
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
Subtract one day to get some date of yesterday:
NSDateComponents *minusOneDay = [[NSDateComponents alloc] init];
[oneDay setDay:-1];
NSDate *nowMinusOneDay = [cal dateByAddingComponents:minusOneDay toDate:now options:0];
Compute start and end date of the "day calendar unit" that contains yesterday's date:
NSDate *startOfYesterday;
NSTimeInterval lengthOfYesterday;
[cal rangeOfUnit:NSDayCalendarUnit startDate:&startOfYesterday interval:&lengthOfYesterday forDate:nowMinusOneDay];
NSDate *endOfYesterday = [startOfYesterday dateByAddingTimeInterval:lengthOfYesterday];
This should work even if a daylight savings time transition occurs between today and yesterday.
Generally one should avoid to use explicit time intervals such as "24 hours", because not every day has that length.

How to get the date of the weekends?

I would like to use the date of the weekends to use it in the NSLocalNotification, but i don't how to get it, i tried to do it mathematically, but sometimes i gets a number greater than the days of the month.
Note that iOS supports several Calendars, I am not sure, if all cultures that uses those calendars have a concept of weekends and if they are always meant to have two days.
Something you also needs to deal with: even in countries that uses the Gregorian calendar a week might start with monday or sunday.
But if we assume that a weekend are equivalent to saturday and sunday, this might be helpful for you:
NSDate *referenceDate = [NSDate date];
NSDate *startOfThisWeek;
NSDate *saturday;
NSUInteger backupStartWeekday = [[NSCalendar currentCalendar] firstWeekday];
[[NSCalendar currentCalendar] setFirstWeekday:1]; // ensure week begins at sunday
[[NSCalendar currentCalendar] rangeOfUnit:NSWeekCalendarUnit
startDate:&startOfThisWeek
interval:NULL
forDate:referenceDate];
NSDateComponents *components = [[NSDateComponents alloc] init];
components.day = [[NSCalendar currentCalendar] maximumRangeOfUnit:NSWeekdayCalendarUnit].length; //the start of the next week
components.day = components.day - 2;
saturday = [[NSCalendar currentCalendar] dateByAddingComponents:components
toDate:startOfThisWeek
options:0];
[[NSCalendar currentCalendar] setFirstWeekday:backupStartWeekday];
Your first problem about getting the date can be solved as :
NSCalendar *calender=[NSCalendar currentCalendar];
NSRange daysRange=[calender rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:[NSDate date]];
NSUInteger numberOfDaysInMonth=daysRange.length;
//NSLog(#"num of days in current month : %ld",numberOfDaysInMonth);
NSDateComponents *dateComponents = [calender components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
NSInteger dayCount=[dateComponents weekday];
//if today itself is saturday what you want to display? today or upcoming one... then do small changes here, for sat & sun.
NSInteger daysForSaturday=7-dayCount;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"dd"];
NSUInteger todaysDate = [[formatter stringFromDate:[NSDate date]]integerValue];
// NSLog(#"Today is : %#",todaysDate);
NSUInteger comingSaturday=todaysDate+daysForSaturday;
if (comingSaturday>numberOfDaysInMonth) {
comingSaturday-=numberOfDaysInMonth;
}
NSUInteger comingSunday=comingSaturday+1;
NSLog(#"Coming.. Sat is : %ld, Sun in : %ld",comingSaturday, comingSunday);

How to get first day of the week in IOS [duplicate]

Does anyone know if there is a way to set the first day of the week on a NSCalendar, or is there a calendar that already has Monday as the first day of the week, instead of Sunday.
I'm currently working on an app that is based around a week's worth of work, and it needs to start on Monday, not Sunday. I can most likely do some work to work around this, but there will be a lot of corner cases. I'd prefer the platform do it for me.
Thanks in advance
Here's some the code that I'm using. it's saturday now, so what I would hope is that weekday would be 6, instead of 7. that would mean that Sunday would be 7 instead of rolling over to 0
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setFirstWeekday:0];
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSWeekdayCalendarUnit;
NSDateComponents *todaysDate = [gregorian components:unitFlags fromDate:[NSDate date]];
int dayOfWeek = todaysDate.weekday;
Edit: This does not check the edge case where the beginning of the week starts in the prior month. Some updated code to cover this: https://stackoverflow.com/a/14688780/308315
In case anyone is still paying attention to this, you need to use
ordinalityOfUnit:inUnit:forDate:
and set firstWeekday to 2. (1 == Sunday and 7 == Saturday)
Here's the code:
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[gregorian setFirstWeekday:2]; // Sunday == 1, Saturday == 7
NSUInteger adjustedWeekdayOrdinal = [gregorian ordinalityOfUnit:NSWeekdayCalendarUnit inUnit:NSWeekCalendarUnit forDate:[NSDate date]];
NSLog(#"Adjusted weekday ordinal: %d", adjustedWeekdayOrdinal);
Remember, the ordinals for weekdays start at 1 for the first day of the week, not zero.
Documentation link.
This code constructs a date that is set to Monday of the current week:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *today = [NSDate date];
NSDate *beginningOfWeek = nil;
BOOL ok = [gregorian rangeOfUnit:NSWeekCalendarUnit startDate:&beginningOfWeek
interval:NULL forDate: today];
setFirstWeekday: on the NSCalendar object.
Sets the index of the first weekday for the receiver.
- (void)setFirstWeekday:(NSUInteger)weekday
Should do the trick.
In my opinion this settings should be dynamic according to the user locale.
Therefore one should use:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setLocale:[NSLocale currentLocale]];
This will cause the calendar to set the first week day according to the user locale automatically. Unless you are developing your app for a specific purpose/user locale (or prefer to allow the user to choose this day).
I've done it like this.
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *today = [NSDate date];
NSDateComponents *compForWeekday = [gregorian components:(NSWeekdayCalendarUnit) fromDate:today];
NSInteger weekDayAsNumber = [compForWeekday weekday]; // The week day as number but with sunday starting as 1
weekDayAsNumber = ((weekDayAsNumber + 5) % 7) + 1; // Transforming so that monday = 1 and sunday = 7
I had trouble with a lot of the answers here. . maybe it was just me. .
Here's an answer that works for me:
- (NSDate*)firstDayOfWeek
{
NSCalendar* cal = [[NSCalendar currentCalendar] copy];
[cal setFirstWeekday:2]; //Override locale to make week start on Monday
NSDate* startOfTheWeek;
NSTimeInterval interval;
[cal rangeOfUnit:NSWeekCalendarUnit startDate:&startOfTheWeek interval:&interval forDate:self];
return startOfTheWeek;
}
- (NSDate*)lastDayOfWeek
{
NSCalendar* cal = [[NSCalendar currentCalendar] copy];
[cal setFirstWeekday:2]; //Override locale to make week start on Monday
NSDate* startOfTheWeek;
NSTimeInterval interval;
[cal rangeOfUnit:NSWeekCalendarUnit startDate:&startOfTheWeek interval:&interval forDate:self];
return [startOfTheWeek dateByAddingTimeInterval:interval - 1];
}
Update:
As pointed out (elsewhere) by #vikingosegundo, in general its best to let the local determine which day is the start of the week, however in this case the OP was asking for the start of the week to occur on Monday, hence we copy the system calendar, and override the firstWeekDay.
The problem with Kris' answer is the edge case where the beginning of the week starts in the prior month. Here's some easier code and it also checks the edge case:
// Finds the date for the first day of the week
- (NSDate *)getFirstDayOfTheWeekFromDate:(NSDate *)givenDate
{
NSCalendar *calendar = [NSCalendar currentCalendar];
// Edge case where beginning of week starts in the prior month
NSDateComponents *edgeCase = [[NSDateComponents alloc] init];
[edgeCase setMonth:2];
[edgeCase setDay:1];
[edgeCase setYear:2013];
NSDate *edgeCaseDate = [calendar dateFromComponents:edgeCase];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:edgeCaseDate];
[components setWeekday:1]; // 1 == Sunday, 7 == Saturday
[components setWeek:[components week]];
NSLog(#"Edge case date is %# and beginning of that week is %#", edgeCaseDate , [calendar dateFromComponents:components]);
// Find Sunday for the given date
components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:givenDate];
[components setWeekday:1]; // 1 == Sunday, 7 == Saturday
[components setWeek:[components week]];
NSLog(#"Original date is %# and beginning of week is %#", givenDate , [calendar dateFromComponents:components]);
return [calendar dateFromComponents:components];
}
I see misunderstanding in the other messages. The first weekday, whichever it is, has number 1 not 0. By default Sunday=1 as in the "Introduction to Date and Time Programming Guide for Cocoa: Calendrical Calculations":
"The weekday value for Sunday in the Gregorian calendar is 1"
For the Monday as a first workday the only remedy I have is brute force condition to fix the calculation
NSCalendar *cal=[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps = [cal components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
// set to 7 if it's Sunday otherwise decrease weekday number
NSInteger weekday=[comps weekday]==1?7:[comps weekday]-1;
Below also covers the edge case,
- (NSDate *)getFirstDayOfTheWeekFromDate:(NSDate *)givenDate
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:givenDate];
[components setWeekday:2]; // 1 == Sunday, 7 == Saturday
if([[calendar dateFromComponents:components] compare: curDate] == NSOrderedDescending) // if start is later in time than end
{
[components setWeek:[components week]-1];
}
return [calendar dateFromComponents:components];
}
You can just change .firstWeekday of the calendar.
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
calendar.firstWeekday = 2;
Then use rangeOfUnit:startDate:interval:forDate: to get the first day
NSDate *startOfWeek;
[calendar rangeOfUnit:NSCalendarUnitWeekOfYear startDate:&startOfWeek interval:nil forDate:[NSdate date]];
Try this:
NSCalendar *yourCal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]
[yourCal setFirstWeekday:0];
Iv found out the way to display any weekday name using nscalender..using the following code..
Just open your console from xcode menu bar to see the results.Copy Paste the following code in your viewDidLoad method to get the first day of the week
NSDate *today = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MM/dd/yyyy :EEEE"];
NSString *dateString = [dateFormat stringFromDate:today];
NSLog(#"date: %#", dateString);
[dateFormat release];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
[components setDay:([components day]-([components weekday]-1))];
NSDate *beginningOfWeek = [gregorian dateFromComponents:components];
NSDateFormatter *dateFormat_first = [[NSDateFormatter alloc] init];
[dateFormat_first setDateFormat:#"MM/dd/yyyy :EEEE"];
NSString *dateString_first = [dateFormat_first stringFromDate:beginningOfWeek];
NSLog(#"First_date: %#", dateString_first);
The Output will be:
date: 02/11/2010 :Thursday
First_date: 02/07/2010 :Sunday
since i had run this program on 2/11/2010 u will get the desired output depending on the current date.
Similarly if u want to get the first working day of the week i.e Monday's date then just modify the code a bit:
CHANGE :[components setDay:([components day]-([components weekday]-1))];
TO
[components setDay:([components day]-([components weekday]-2))];
to get Mondays date for that week..
Similarly u can try to find the date of any of seven workdays by changing the integer -1,-2 and so on...
Hope u r question is answered..
Thanks,
Bonson Dias
The ISO 8601 calendar appears to have it's first weekday set to monday by default.
Using the Calendar nextWeekend (iOS 10 or later) and ordinality (thanks #kris-markel). I've gotten Monday as first of the week for the en_US calendar.
Here is an example of it with fallback to firstWeekday:
extension Calendar {
var firstWorkWeekday: Int {
guard #available(iOS 10.0, *) else{
return self.firstWeekday
}
guard let endOfWeekend = self.nextWeekend(startingAfter: Date())?.end else {
return self.firstWeekday
}
return self.ordinality(of: .weekday, in: .weekOfYear, for: endOfWeekend) ?? self.firstWeekday
}
}
The Swift solution (note, use .yearForWeekOfYear, not .year):
let now = Date()
let cal = Calendar.current
var weekComponents = cal.dateComponents([.yearForWeekOfYear, .weekOfYear,
.weekday], from: now)
//weekComponents.weekday = 1 // if your week starts on Sunday
weekComponents.weekday = 2 // if your week starts on Monday
cal.date(from: weekComponents) // returns date with first day of the week
… is there a calendar that already has Monday as the first day of the week, instead of Sunday.
Someday, there will be.
My simple way of doing this is to get Monday = 0, Sunday = 6:
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
NSInteger dayNumStartingFromMonday = ([dateComponents weekday] - 2 + 7) % 7; //normal: Sunday is 1, Monday is 2