loading upcoming events by comparing their dates with the calendar date - ios

I have found the below code online for comparing two dates (the date of the calendar and some another date) what I would like help with is modifying this code so that it compares the calendar date with the date of an event where date here is key for each event within NSArray (json file) .. Also how I could use this later with NSPredicate so that I can load only the events with components (difference between two dates in days) at least more than 1 day since Im going to have UIButton called future events when the user click on it, all the future events should be loaded .. I would really appreciate any help and code examples since Im very new to objective C and Xcode. Thanks.
#implementation HomeViewController {
NSArray *_events;
}
- (BOOL)isSameDay:(NSDate*)date
{
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDate *currDate = [NSDate date];
//date = [_events objectAtIndex:date];
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents* comp1 = [calendar components:unitFlags fromDate:date];
NSDateComponents* comp2 = [calendar components:unitFlags fromDate:currDate];
NSDateComponents *components = [calendar components:unitFlags
fromDate:currDate
toDate:date
options:0];
NSLog(#"Days between dates: %#", components);
return [comp1 day] == [comp2 day] &&
[comp1 month] == [comp2 month] &&
[comp1 year] == [comp2 year];
}

To get the number of days remaining for a date from now, you can use the method below:
-(long)daysRemaining:(NSDate*) date
{
NSDate *now = [NSDate date];
NSTimeInterval dateDiff = [date timeIntervalSinceNow] - [now timeIntervalSinceNow];
CGFloat numberOfDaysRemaining = (dateDiff/(60*60*24));
return ceil(numberOfDaysRemaining);
}
So you can call to get the number of days remaining like this:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd"];
NSDate *dateToCompare = [dateFormatter dateFromString: #"2014-02-08"];
long numberDays = [self daysRemaining:dateToCompare];
For this case, the result is 2 days remaining. Note that a negative result indicates that was passed n days.
To filter the array (2 days remaining events) making a copy of it:
NSArray* dates = [[NSArray alloc] initWithObjects:
[dateFormatter dateFromString: #"2014-02-08"],
[dateFormatter dateFromString: #"2014-02-05"],
[dateFormatter dateFromString: #"2014-02-10"],
[dateFormatter dateFromString: #"2014-02-08"], nil];
NSArray *filteredArray = [dates filteredArrayUsingPredicate:
[NSPredicate predicateWithBlock:
^BOOL(id evaluatedObject, NSDictionary *bindings) {
long daysRemaining = [self daysRemaining:(NSDate*)evaluatedObject];
return daysRemaining == 2; // if someMethod returns YES, the object is kept
}
]];
To filter an NSMutableArray in place:
[mutableArray filterUsingPredicate:[NSPredicate predicateWithBlock:
^BOOL(id evaluatedObject, NSDictionary *bindings) {
long daysRemaining = [self daysRemaining:(NSDate*)evaluatedObject];
return daysRemaining == 2; // if someMethod returns YES, the object is kept
}
]];

[displayedEvents addObjectsFromArray:[_events filteredArrayUsingPredicate:
[NSPredicate predicateWithBlock:
^BOOL(Events * event, NSDictionary *bindings) {
[dateFormatter dateFromString: event.date];
long daysRemaining = [self daysRemaining: (NSDate*)event.date];
return daysRemaining > 1; // if someMethod returns YES, the object is kept
}
]]];

Related

iOS: Get all dates in current week

I am trying to get all dates in the current week of a given date:
In the example below, the input is
2015-11-29 23:40:37 +0000
, so I would expect the output to be an array of dates from November 23 - November 29, but the actual output is November 30 - December 6.
-(NSArray *)datesForWeekOf : (NSDate *) date {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startOfWeek;
NSDate *endOfWeek;
NSTimeInterval interval;
[calendar rangeOfUnit:NSCalendarUnitWeekOfMonth startDate:&startOfWeek interval:&interval forDate:date];
endOfWeek = [startOfWeek dateByAddingTimeInterval:interval-1];
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
NSDateComponents *days = [[NSDateComponents alloc] init];
NSInteger dayCount = 0;
NSMutableArray *allDates = [NSMutableArray array];
while ( TRUE ) {
[days setDay: ++dayCount];
NSDate *date = [gregorianCalendar dateByAddingComponents: days toDate: startOfWeek options: 0];
[allDates addObject:date];
if ( [date compare: endOfWeek] == NSOrderedDescending )
break;
}
return allDates;
}
You can get all dates of this week and next week using following code:-
NSArray * allDatesOfThisWeek = [self daysThisWeek];
NSArray * allDatesOfNextWeek = [self daysNextWeek];
Following methods are used for calculating dates of this week:-
-(NSArray*)daysThisWeek
{
return [self daysInWeek:0 fromDate:[NSDate date]];
}
-(NSArray*)daysNextWeek
{
return [self daysInWeek:1 fromDate:[NSDate date]];
}
-(NSArray*)daysInWeek:(int)weekOffset fromDate:(NSDate*)date
{
NSCalendar *calendar = [NSCalendar currentCalendar];
//ask for current week
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps=[calendar components:NSWeekCalendarUnit|NSYearCalendarUnit fromDate:date];
//create date on week start
NSDate* weekstart=[calendar dateFromComponents:comps];
NSDateComponents* moveWeeks=[[NSDateComponents alloc] init];
moveWeeks.weekOfYear=weekOffset;
weekstart=[calendar dateByAddingComponents:moveWeeks toDate:weekstart options:0];
//add 7 days
NSMutableArray* week=[NSMutableArray arrayWithCapacity:7];
for (int i=1; i<=7; i++) {
NSDateComponents *compsToAdd = [[NSDateComponents alloc] init];
compsToAdd.day=i;
NSDate *nextDate = [calendar dateByAddingComponents:compsToAdd toDate:weekstart options:0];
[week addObject:nextDate];
}
return [NSArray arrayWithArray:week];
}
If you want to get dates of next to next week from today, then pass weekOffset=2 like this:-
NSArray * allDatesOfNextToNextWeek = [self daysInWeek:2 fromDate:now];
If you want to get dates of previous week from today, then pass weekOffset=-1 like this:-
NSArray * allDatesOfPreviousWeek = [self daysInWeek:-1 fromDate:now];
Hope, this is what you're looking for. Any concern get back to me.

Sort an array of dates to extract every sundays

I am quite new to ios and I was wondering how one could from a NSMutableArray of dates ( as strings) extract all the sundays in that array.
So far from my research I found that I have to start from the date of today like this
NSDate *now = [NSDate date];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
Then "select" the sundays like that
NSDateComponents *dateComponents = [calendar components:NSWeekdayCalendarUnit | NSHourCalendarUnit fromDate:now];
NSInteger weekday = [dateComponents 1]; // 1 is sunday right ?!
Then go back into the past and every sunday put the date in a new array or else.
NSDate *dateRelease;
NSDateFormatter *dateFormatter = [ [ NSDateFormatter alloc ] init ];
[ dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"] ];
[ dateFormatter setDateFormat:#"yyyy-MM-dd" ];
for (int i = [ dateFormatter dateFromString:[[[[DessinsManager sharedInstance]imagesList].count ; i > 0; i--){
dateRelease = [ dateFormatter dateFromString:[[[[DessinsManager sharedInstance]imagesList] objectAtIndex:i]dateDrawing] ];
//Check for the sundays here
if ( [now compare: dateRelease] == NSOrderedDescending ) {
//if sundays put in new array
} else {
//if not sundays then go on with loop
}
}
And for this I am a bit confused on how to do the comparaison to get the sundays (my array goes from november 1st 2013 till today)...
Thanks for your help
V.v
First store the dates as NSDate objects as you should always store data using the most appropriate data type:
NSArray *datesAsStrings = ...;
NSMutableArray *dates = [NSMutableArray new];
NSDateFormatter *dateFormatter = [NSDateFormatter new];
dateFormatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"UTC"];
dateFormatter.dateFormat = #"yyyy-MM-dd";
for (NSString *dateStr in datesAsStrings) {
NSDate *date = [dateFormatter dateFromString:dateStr];
[dates addObject:dates];
}
and then filter out the sundays:
NSMutableArray *sundays = [NSMutableArray new];
[dates enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop)) {
NSDate *date = (NSDate *)obj;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [calendar components:NSWeekdayCalendarUnit
fromDate:date];
if (comps.weekday == 1)
[sundays addObject:date];
}];
You should be using the - (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate method, which returns a new array evaluating each object in the original one with a predicate.
Such a predicate may be built with + (NSPredicate *)predicateWithBlock:(BOOL (^)(id evaluatedObject, NSDictionary *bindings))block and maybe some inspiration from Dave DeLong's answer here to recognise the day being evaluated.

get days part from a date interval

I want to get day parts from a date interval.
For example I have a date interval 21/12/2013 to 28/12/2013.
Now I want to get all day part from this interval like as 21,22,23,24,25,26,27,28.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
NSTimeZone *gmt = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
[dateFormatter setTimeZone:gmt];
[dateFormatter setDateFormat:#"MMM dd, yyyy"];
NSString *date_str=[NSString stringWithFormat:#"%#",from_date];
NSLog(#"%#",date_str);
NSString *date_str1=[NSString stringWithFormat:#"%#",to_date];
NSLog(#"%#",date_str1);
NSDate *starting_date = [dateFormatter dateFromString:date_str];
NSDate *end_date = [dateFormatter dateFromString:date_str1];
NSCalendar *gregorian = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger unitFlags = NSDayCalendarUnit;
NSDateComponents *components = [gregorian components:unitFlags fromDate:starting_date toDate:end_date options:0];
NSInteger days = [components day];
That code giving total no of days.
How can i get this output?
Thanks
Continuing from your code and assuming,
NSString *date_str=[NSString stringWithFormat:#"%#",#"December 21, 2013"];
NSString *date_str1=[NSString stringWithFormat:#"%#",#"December 28, 2013"];
If you need it within the same month, it can be in this way,
NSDateComponents *component1 = [gregorian components:unitFlags fromDate:starting_date];
NSDateComponents *component2 = [gregorian components:unitFlags fromDate:end_date];
for (int i = component1.day; i <= component2.day; i++) {
NSLog(#"%i", i);
}
But if your dates expand between different months, then you have to add checking for month components in the loop.
I will continue on your code, with some dirty code of mine, but it gets the job done if what you provided is your date format:
NSArray *begining_day = [date_str componentsSeparatedByString:#"/"];
NSMutableArray *part_days = [[NSMutableArray alloc] init];
for (int i = 0; i < days; i++) {
[part_days addObject:([begining_day[0] integerValue] + i)];
}
Now part_days will have your days, BUT! You have to handle the end of each month.
I'm 100% sure you'll find better solutions, but I thought I could share the first thing I thought of, you might benefit from it.
You can try below code:
NSArray *array = [[NSArray alloc] initWithObjects:#"1/12/14",#"2/12/14",#"3/12/14",#"4/12/14",#"5/12/14", nil];
for (int i = 0; i < [array count]; i++) {
NSArray *myArray = [[array objectAtIndex:i] componentsSeparatedByString:#"/"];
[dayArray addObject:[myArray objectAtIndex:0]];
}
NSLog(#"%#",dayArray);
You can incrementally add 24 hours to your startDate until you pass your endDate. Using NSDateComponents you can pick out the day. Instead of logging them with NSLog just store them in a string or array.
NSDate *startDate = [NSDate dateWithTimeIntervalSinceReferenceDate:300000000];
NSDate *endDate = [NSDate dateWithTimeIntervalSinceReferenceDate:302000000];
for (NSDate *nextDate = startDate; [nextDate compare:endDate] < 0; nextDate = [nextDate dateByAddingTimeInterval:24*60*60] ) {
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:nextDate];
NSInteger day = [components day];
NSLog(#"day %li", (long)day);
}

Iterate through dictionary and store values

I have a dictionary of data which looks like this:
{
asr = "4:23 pm";
dhuhr = "12:02 pm";
fajr = "1:17 am";
isha = "10:47 pm";
maghrib = "8:23 pm";
shurooq = "3:41 am";
}
My goal is to determine which time comes next. My plan is to convert these to actual NSDates using this code:
//create an NSDate with todays date and the right prayer time
NSString *prayerDateString = [curDate stringByAppendingString: #" "];
prayerDateString = [prayerDateString stringByAppendingString: time];
NSLog(#"prayer date string: %#", prayerDateString);
//convert string back to date
NSDate *prayerDateAndTime = [dateAndTimeFormatter dateFromString:prayerDateString];
[dictionaryOfDatesAsDates addObject:prayerDateAndTime];
Which I think works, but I'm not sure if it correctly retains the format of the dictionary. Then to use [currentDate timeIntervalSinceNow] to check each one and assume that the smallest positive time interval will be the next time and to get the name, eg asr.
I'm not sure how to do the iteration and store the time interval value somewhere along with the name of the time in order to get which is the smallest value from this?
How would I go about achieving this?
So, you've got your dates and added them to an array:
NSArray *prayerDateAndTimes = ...
Following on from yesterday:
Get a list of the dates, then use an NSPredicate to filter that list to dates >= [NSDate date], then sort it ascending. Then the first item in the filtered, sorted array will be the next date.
First, filter out dates that have already passed:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF >= %#", [NSDate date]];
NSArray *validPrayerDateAndTimes = [prayerDateAndTimes filteredArrayUsingPredicate:predicate];
Now we can tell if there are any more dates today:
if (validPrayerDateAndTimes.count > 0) {
// yay, sort to find the next one
validPrayerDateAndTimes = [validPrayerDateAndTimes sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"next date: %#", [validPrayerDateAndTimes objectAtIndex:0]);
} else {
NSLog(#":-(");
}
EDITED:
This should work. Change your dictionary to 24hour format (20:23). It will return the key that has the closest time to current time (even if it has no more prays for today, it will return the next day's).
-(NSDate*)initDateWithHour:(int)hour andMinutes:(int)minutes
{
NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease];
NSDateComponents *nowComp = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:[NSDate date]];
[components setYear:[nowComp year]];
[components setMonth:[nowComp month]];
[components setDay:[nowComp day]];
[components setHour:hour];
[components setMinute:minutes];
return [calendar dateFromComponents:components];
}
- (NSString*)getClosestTimeKey:(NSDictionary*)dict
{
NSDate* now = [NSDate date];
int secondsOfDay = 86400;
int minDif = secondsOfDay;
NSString* closestKey = #"";
for (NSString* key in dict)
{
NSString* timeStr = [dict objectForKey:key];
NSArray *array = [timeStr componentsSeparatedByString:#":"];
int hour = [[array objectAtIndex:0] intValue];
int minutes = [[array objectAtIndex:1] intValue];
NSDate *d = [self initDateWithHour:hour andMinutes:minutes];
int dif = [d timeIntervalSince1970] - [now timeIntervalSince1970];
if (dif < 0)
dif += secondsOfDay;
if (dif < minDif)
{
minDif = dif;
closestKey = key;
}
}
return closestKey;
}
And you use it by:
NSString* key = [self getClosestTimeKey:dict];

If statement with dates

what I am trying to do is make a if statement with dates using greater than less than signs. For some reason only the greater than sign works. Here is my code:
NSDate *currDate = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"HHmm"];
NSString *dateString = [dateFormatter stringFromDate:currDate];
NSLog(#"%#",dateString);
if (dateString < #"0810" && dateString > #"0800") {
NSLog(#"Homeroom");
}
else {
NSLog(#"no");
}
The output for this code would be if the time was 8:03:
2013-04-08 08:03:47.956 Schedule2.0[13200:c07] 0803
2013-04-08 08:03:47.957 Schedule2.0[13200:c07] no
If I were to make is so where it is only the greater then sign like this:
if (dateString > #"0800") {
NSLog(#"Homeroom");
}
else {
NSLog(#"no");
}
The output would be this:
2013-04-08 08:03:29.748 Schedule2.0[14994:c07] 0803
2013-04-08 08:03:29.749 Schedule2.0[14994:c07] Homeroom
create a NSDate object with the time 8:10 and one with 8:00. Now you can compare the given date with both these dates
if(([date0800 compare:date] == NSOrderingAscending) && [date0810 compare:date] == NSOrderingDescending) )
{
// date is between the other
}
to create the boundaries dates you can do this
NSDate *date = [NSDate date]; // now
NSDateComponents *components = [[NSCalendar currentCalendar] components:( NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit ) fromDate:date];
components.hour = 8;
components.minute = 0;
NSDate *date0800 = [[NSCalendar currentCalendar] dateFromComponents: components];
components.minute = 10;
NSDate *date0810 = [[NSCalendar currentCalendar] dateFromComponents: components];
if you insist of using operators like < and >, you can use the timeinterval of the date objects.
if(([date0800 timeIntervalSince1970] < [date timeIntervalSince1970]) && ([date0810 timeIntervalSince1970] > [date timeIntervalSince1970]))
{
// date lays between the other two
}
but beware of checking == on it, as it could be faulty due to rounding errors.
Here you are comparing string objects, with < and >, which does not do what you are expecting. You can use NSDateComponents to get the hour and minute to compare those:
NSDate *today = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components =
[gregorian components:(NSHourCalendarUnit | NSMinuteCalendarUnit ) fromDate:today];
NSInteger hour = [weekdayComponents hour];
NSInteger minutes = [weekdayComponents minute];
BOOL homeroom = (hour == 8) && (minute < 10);
Or you can create a specific NSDate for 8:10 and 8:00 using NSDateFormater and using the compare: function.
NSString objects are objects, and when you compare objects with C comparison operators (==, >, <, etc.) you are comparing their addresses, not their values. You need to use compare, such as:
if ([dateString compare:#"0810"] == NSOrderedAscending &&
[dateString compare:#"0800"] == NSOrderedDescending) { ...
Though I'd recommend converting to NSDate objects in most cases if you want to compare dates and times.
You can't use > or < to compare string objects. That actually compares pointers so we won't get into why > 'works' and < 'doesn't'.
For this kind of date comparison use NSDateComponents
NSDateComponents Reference
Here's the gist of a category I wrote on NSDate. I found it made my code more readable.
https://gist.github.com/nall/5341477
#interface NSDate(SZRelationalOperators)
-(BOOL)isLessThan:(NSDate*)theDate;
-(BOOL)isLessThanOrEqualTo:(NSDate*)theDate;
-(BOOL)isGreaterThan:(NSDate*)theDate;
-(BOOL)isGreaterThanOrEqualTo:(NSDate*)theDate;
#end

Resources