Does Current Time Match a Range of times? - ios

I am trying to create a catch on an iOS app to keep people from accessing things outside of a small window.
Basically, I need the action to only fire if it is between 12-1:30PM on Sundays in London (BST Time Zone). How would I check the current time, convert it to that time zone, and then see if it matches up?
I have tried the following, but it always shows it is between that range:
- (NSDate *)dateByNeutralizingDateComponentsOfDate:(NSDate *)originalDate {
NSCalendar *gregorian = [[[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
// Get the components for this date
NSDateComponents *components = [gregorian components: (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate: originalDate];
// Set the year, month and day to some values (the values are arbitrary)
[components setYear:2000];
[components setMonth:1];
[components setDay:1];
return [gregorian dateFromComponents:components];
}
- (BOOL)isTimeOfDate:(NSDate *)targetDate betweenStartDate:(NSDate *)startDate andEndDate:(NSDate *)endDate {
if (!targetDate || !startDate || !endDate) {
return NO;
}
// Make sure all the dates have the same date component.
NSDate *newStartDate = [self dateByNeutralizingDateComponentsOfDate:startDate];
NSDate *newEndDate = [self dateByNeutralizingDateComponentsOfDate:endDate];
NSDate *newTargetDate = [self dateByNeutralizingDateComponentsOfDate:targetDate];
// Compare the target with the start and end dates
NSComparisonResult compareTargetToStart = [newTargetDate compare:newStartDate];
NSComparisonResult compareTargetToEnd = [newTargetDate compare:newEndDate];
return (compareTargetToStart == NSOrderedDescending && compareTargetToEnd == NSOrderedAscending);
}
-(void)checkDate {
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:#"EEEE HH:mm"];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithAbbreviation:#"BST"];
[dateFormatter setTimeZone:timeZone];
NSDate *openingDate = [dateFormatter dateFromString:#"Sunday 12:00"];
NSDate *closingDate = [dateFormatter dateFromString:#"Sunday 1:30"];
NSDate *targetDate = [NSDate date];
if ([self isTimeOfDate:targetDate betweenStartDate:openingDate andEndDate:closingDate]) {
NSLog(#"TARGET IS INSIDE!");
}else {
NSLog(#"TARGET IS NOT INSIDE!");
}
}

I assume you want to use the current time as observed in England, rather than BST specifically, as BST is British Summer Time. My understanding is that, during the winter, they use UTC (formerly known as GMT). Thus we should specify the time zone in a way that will select the proper offset from UTC based on the time of year.
static BOOL dateIsAcceptable(NSDate *date) {
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
calendar.timeZone = [NSTimeZone timeZoneWithName:#"Europe/London"];
NSDateComponents *components = [calendar
components:NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute
fromDate:date];
if (components.weekday != 1) {
return NO;
}
double hour = components.hour + components.minute / 60.0;
return hour >= 12 && hour < 13.5;
}

Related

Why [calendar dateFromComponents:] always return hour 16 ?

When I pass the [NSDate date] to NSDateComponents , then pass back after the weekday modified, I always got the hour 16:00:00 . Why ?
Code belows:
{
NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *now = [NSDate date];
NSLog(#"now = %#",now);
NSDateComponents *firstDayOfWeek = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday fromDate:now];
firstDayOfWeek.weekday = 1; // Monday as first day of week
NSDate *firstDayOfWeekDate = [calendar dateFromComponents:firstDayOfWeek];
NSLog(#"first day = %#", firstDayOfWeekDate);
}
{
NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *now = [NSDate date];
NSLog(#"now = %#",now);
NSDateComponents *firstDayOfWeek = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:now];
firstDayOfWeek.weekday = 1; // Monday as first day of week
firstDayOfWeek.hour = 1;
firstDayOfWeek.minute = 1;
firstDayOfWeek.second = 1;
NSDate *firstDayOfWeekDate = [calendar dateFromComponents:firstDayOfWeek];
NSLog(#"first day = %#", firstDayOfWeekDate);
}
And the output is :
now = 2016-01-18 03:14:08 +0000
first day = 2016-01-17 16:00:00 +0000
now = 2016-01-18 03:14:08 +0000
first day = 2016-01-17 17:01:01 +0000
Why the hour begins with 16 ?
NSDateComponents isn't making it 16:00:00. It's making it midnight in your current timezone. But the NSLog is showing you the equivalent GMT time. That's what the +0000 means: e.g. GMT +00:00.
Bottom line, if you want to see the resulting NSDate object in your local time zone, you should use NSDateFormatter.
This is due to timezone.
Try to add NSDateFormatter before you output the date with NSLog, e.g.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[NSLocale currentLocale]];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
[dateFormatter setTimeStyle:NSDateFormatterLongStyle];
NSString *dateDisplay = [dateFormatter stringFromDate:firstDayOfWeekDate];
NSLog(#"first day = %#",dateDisplay);
You can refer this article for more examples.

Get days between days

Before giving downvote, comment the reason
I have created a UIDatePicker with minimum & maximum date values (i.e. 6 months of date picker) Now, i need to get 7 days from selected date. There i need to check the conditions,
If date is today date i need to get 7 days from today onwards
If date is last date (i.e. last date of picker) need to get last 7 days including last day
If date is middle of today's date & last date i need to get last 3 days, next 3 days including today date. And, also while getting last & next 3 days it shouldn't get exceed with picker's date limit.
Here's my code snippet:
- (void)addDays:(NSInteger)range {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
NSDate *startDate = self.selectedDate;
for (int x = 0; x <= range; x++) {
NSLog(#"%#", [dateFormat stringFromDate:startDate]);
startDate = [startDate dateByAddingTimeInterval:(60 * 60 * 24)];
}
}
- (void)minusDays:(NSInteger)range {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [NSDateComponents new];
for (NSInteger i=0; i<range; i++) {
comps.day += -1;
NSDate *date = [calendar dateByAddingComponents:comps toDate:self.selectedDate options:0];
NSDateComponents *components = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:date];
NSLog(#"%#", [dateFormat stringFromDate:[calendar dateFromComponents:components]]);
}
}
- (void)calculateDateRange {
if ([dateArray count] > 0) {
[dateArray removeAllObjects];
}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *lastcomponents = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:[NSDate date]];
lastcomponents.month += 6;
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:#"yyyy/MM/dd"];
NSDate *currentDate = [NSDate date];
NSDate *selectedD = self.selectedDate;
NSDate *endDate = [calendar dateFromComponents:lastcomponents];
NSDate *fromDate;
NSDate *toDate;
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:currentDate];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:selectedD];
NSDateComponents *difference = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
NSInteger first = [difference day];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:selectedD];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:endDate];
NSDateComponents *difference2 = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
NSInteger second = [difference2 day];
if ((first == 0 || first < 3) && second > 7) {
[self addDays:7];
} else if (first >= 3 && second > 7) {
[self minusDays:3];
[self addDays:3];
}else if (second == 7 || second < 7) {
[self minusDays:7];
}
}
This is working fine. But, can't get exact last & previous days.
Anyone has idea on this?
My interpretation of your needs
You have a date picker. When a date is picked you need to create a 7 day range around that selected date.
So, if the selected date is 15/11/2014 then you want 3 days either side so...
12/11/2014 - 18/11/2014.
However, the date range cannot exceed the limits of the date picker. So if the minimum date on the date picker is set to 14/11/2014 then (in the above example) the date range would be...
14/11/2014 - 21/11/2014
Even after your additional explanation this is still my interpretation. And my code does exactly this.
Solution
You CANNOT use 60*60*24 to mean one day. This is just wrong. When dealing with dates you should always be using NSDateComponents and NSCalendar.
Also, break down your problem into small steps. There is no reason to do everything in one giant function.
OK I guess you have a datePicker action somewhere so I'd code it like this...
- (void)datePickerDateChanged
{
NSDate *minimumDate = self.datePicker.minimumDate;
NSDate *maximumDate = self.datePicker.maximumDate;
NSDate *selectedDate = self.datePicker.date;
NSDate *startDate;
NSDate *endDate;
if ([self numberOfDaysFromDate:minimumDate toDate:selectedDate] < 3) {
// get 7 days after minimumDate
startDate = minimumDate;
endDate = [self dateByAddingDays:6 toDate:minimumDate];
} else if ([self numberOfDaysFromDate:selectedDate toDate:maximumDate] < 3) {
// get 7 days before maximumDate
startDate = [self dateByAddingDays:-6 toDate:maximumDate];
endDate = maximumDate;
} else {
// get 3 days before and 3 days after selectedDate
startDate = [self dateByAddingDays:-3 toDate:selectedDate];
endDate = [self dateByAddingDays:3 toDate:selectedDate];
}
// Here startDate and endDate define your date range.
}
- (NSDate *)dateByAddingDays:(NSInteger)days toDate:(NSDate *)date
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [NSDateComponents new];
components.day = days;
return [calendar dateByAddingComponents:components toDate:date options:0];
}
- (NSInteger)numberOfDaysFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
// always return positive. We just want magnitude of days.
return components.day > 0 ? components.day : -components.day;
}
This is untested and just a first attempt.

Comparing two NSDate

I have a UIViewController with 2 buttons: today and yesterday. Every time the client clicked on today it needs to set the property _date for today and the same with yesterday.
The method which I set the property value is the following:
-(IBAction)dateButtonTouched:(id)sender
{
if(![sender isSelected])
{
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:[[NSDate alloc] init]];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
if([sender tag] == YESTERDAY)
{
[components setHour:-24];
_date= [cal dateByAddingComponents:components toDate: [NSDate date] options:0]; //YESTERDAY
}
else
{
NSDate *today = [NSDate date];
[components setHour:0];
_date= [cal dateByAddingComponents:components toDate: today options:0]; //TODAY
}
NSLog(#"user selected date %#",_date);
}
}
in the following method I'm trying to check if _date is today or yesterday:
-(void) viewWillLayoutSubviews
{
NSDateFormatter *dateFormat = [NSDateFormatter new];
[dateFormat setDateFormat:#"MM-dd-YY"];
self.dateLabel.text = [dateFormat stringFromDate:_date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:[[NSDate alloc] init]];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
[components setHour:0];
NSDate *today = [cal dateByAddingComponents:components toDate: [NSDate date] options:0]; //TODAY
[components setHour:-24];
NSDate *yesterday = [cal dateByAddingComponents:components toDate: [NSDate date] options:0]; //YESTERDAY
if([_date isEqualToDate:yesterday])
{
//do something
}
else if( [_date isEqualToDate:today])
{
//do something
}
}
The _date is never today or yesterday although if I debug it, print it or compare the description is the same.
An NSDate instance represents a specific moment in time. It is much more specific than "yesterday" or "today". Instead of trying to reset the hours, minutes, and seconds via date components, Maybe you should compare the components you're interested in: day, month, and year. Or you can use a calendar to compare at the component level:
NSDate *today = [NSDate date];
NSDate *someDay; // the date to compare
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *difference = [cal components:NSDayCalendarUnit fromDate:today toDate:someDay options:nil];
if (difference.day == -1) {
// yesterday
} else if (difference.day == 0) {
// today
}
Without knowing exactly what you want to do, it's harder to get more specific.
An NSDate object contains both date and time, encoded as a value that is essentially the number of microseconds since Jan 1 2000.
When you do two successive [NSDate date] operations you will get two different values, if not because the time has actually changed that much between the two then because most computer clock implementations assure that you get distinct values with each access.
NSDate's isEqualToDate function compares all of the bits of the two NSDate objects to see if they are exactly equal. This means that if the two are even a microsecond apart they will not appear to be equal.
If you want to see if two NSDate values are the same day you should either use NSDateFormatter to format them into strings and compare the strings, or use one of the NSDateComponent functions to calculate the number of days between them (several ways to do this).

Mark dates of Hijri Calendar

I'm new to iOS development (coming from Android development) and I need some help. I'm using Vurig's library to display a custom calendar control and mark multiple dates in the calendar.
The issue is I would like to mark Hijri dates rather than gregorian. How can I do that?
So far I've converted the Hijri date to gregorian to mark a date on the calendar, but it marks only one date whereas I would like to mark multiple dates in the calendar months.
The code so far:
-(void)calendarView:(VRGCalendarView *)calendarView switchedToMonth:(int)month targetHeight:(float)targetHeight animated:(BOOL)animated {
NSArray *dates;
NSDate *date = [self addDates:calendarView andDay:6 andMonth:10];
// if (month==8){
// dates = [NSArray arrayWithObjects:[NSNumber numberWithInt:5],[NSNumber numberWithInt:25], nil];
// }
// if (month==9) {
// dates = [NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:5], nil];
//
// }
//if (month==[[NSDate date] month]) {
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:( NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSWeekCalendarUnit) fromDate:date];
int gmonth = [dateComponents month];
if (month == gmonth) {
dates = [NSArray arrayWithObjects:date, nil];
}
[calendarView markDates:dates];
}
-(void)calendarView:(VRGCalendarView *)calendarView dateSelected:(NSDate *)date {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]]; [dateFormatter setDateFormat:#"dd MMMM"];
NSLog(#"Selected date = %#",[dateFormatter stringFromDate:date]);
//NSLog(#"Selected date = %#",date);
}
-(NSDate *)addDates:(VRGCalendarView *)calendarView andDay:(int)day andMonth:(int)month{
// Then create an Islamic calendar
NSCalendar *hijriCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSIslamicCalendar];
// And grab those date components for the same date
NSDateComponents *hijriComponents = [[NSDateComponents alloc] init];
hijriComponents.day = day;
hijriComponents.month = month;
hijriComponents.year = 1434;
NSDate *hdate = [hijriCalendar dateFromComponents:hijriComponents];
// Create a Gregorian Calendar
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
// Set up components of a Gregorian date
NSDateComponents *gregorianComponents = [gregorianCalendar components:(NSDayCalendarUnit |
NSMonthCalendarUnit |
NSYearCalendarUnit)
fromDate:hdate];
// Create the date
NSDate *date = [gregorianCalendar dateFromComponents:gregorianComponents];
NSLog(#"[In Hijri calendar ->] Date is %#", date);
return date;
}
All you need to do is pass an array of NSDates to the markDates: method. You don't need to convert to Gregorian first as an NSDate is simply a time stamp and is independent of calendars, time zones, and so forth.
I downloaded the VURIG Calendar sample project and in VRGViewController replaced the calendarView:switchedToMonth:targetHeight:animated: method with the following, which should illustrate exactly what you need:
-(void)calendarView:(VRGCalendarView *)calendarView switchedToMonth:(int)month targetHeight:(float)targetHeight animated:(BOOL)animated {
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSIslamicCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:6];
[components setMonth:10];
[components setYear:1434];
NSDate *date1 = [calendar dateFromComponents:components];
[components setDay:10];
NSDate *date2 = [calendar dateFromComponents:components];
[calendarView markDates:#[date1,date2]];
}
This marked Aug 12 and Aug 16 on the calendar, and while I'm not familiar with the Hijri calendar from what I can tell that's accurate.

UITableview title bar with date for week?

How can I have the title of UITableview with date?
I know I can edit the title using:
self.title=#"meals for ..";
I want to add the date for one week. How can I do that?
You can get a date like this:
NSDate *theDateToday = [NSDate date];
And format it like this:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"EEE MMM dd"];
NSString *theDateAsAString = [dateFormatter stringFromDate:theDateToday];
Combine that with what you have like this:
self.title = [NSString stringWithFormat:#"meals for %#", theDateAsAString];
As for finding the days of the week, you could try adapting this answer.
- (NSString *)getWeekStartDateAsString:(NSDate *)date {
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
int dayofweek = [[[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:today] weekday];// this will give you current day of week
[components setDay:([components day] - ((dayofweek) - 2))];// for beginning of the week.
NSDate *beginningOfWeek = [gregorian dateFromComponents:components];
NSDateFormatter *dateFormat_first = [[NSDateFormatter alloc] init];
[dateFormat_first setDateFormat:#"yyyy-MM-dd"];
dateString2Prev = [dateFormat stringFromDate:beginningOfWeek];
weekstartPrev = [[dateFormat_first dateFromString:dateString2Prev] retain];
return weekstartPrev;
}
- (NSString *)getWeekEndDateAsString:(NSDate *)date {
NSCalendar *gregorianEnd = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *componentsEnd = [gregorianEnd components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
int Enddayofweek = [[[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:today] weekday];// this will give you current day of week
[componentsEnd setDay:([componentsEnd day]+(7-Enddayofweek)+1)];// for end day of the week
NSDate *EndOfWeek = [gregorianEnd dateFromComponents:componentsEnd];
NSDateFormatter *dateFormat_End = [[NSDateFormatter alloc] init];
[dateFormat_End setDateFormat:#"yyyy-MM-dd"];
dateEndPrev = [dateFormat stringFromDate:EndOfWeek];
weekEndPrev = [[dateFormat_End dateFromString:dateEndPrev] retain];
return weekEndPrev;
}
You end result might look like this:
self.title = [NSString stringWithFormat:#"meals for %#-%#", [self getWeekStartDateAsString:theDateToday], [self getWeekEndDateAsString:theDateToday]];
A rarely known NSCalendar method will be the best option IMO
rangeOfUnit:startDate:interval:forDate:. It gives you the start and the duration (interval) for a certain time unit. With it it is easy to find the start of the week in the used calendar and add the range-1 to get the latest second in that week.
With the also rarely seen localizedStringFromDate:dateStyle:timeStyle: Available in OS X v10.6, iOS 4.0 it is easy to create a localized representation of the dates.
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDate *startOfTheWeek;
NSDate *endOfWeek;
NSTimeInterval interval;
[cal rangeOfUnit:NSWeekCalendarUnit
startDate:&startOfTheWeek
interval:&interval
forDate:now];
//startOfWeek is 2013-06-02 22:00:00 +0000 now (note: it is GMT with timezone support for my timezone Europe/Berlin/DST)
endOfWeek = [startOfTheWeek dateByAddingTimeInterval:interval-1];
// 2013-06-09 21:59:59 +0000
NSString *text = [NSDateFormatter localizedStringFromDate:startOfTheWeek
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterNoStyle];
text = [text stringByAppendingString:#" to "];
text = [text stringByAppendingFormat:#"%#", [NSDateFormatter localizedStringFromDate:endOfWeek
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterNoStyle]];
self.title=text;
results in 03.06.13 to 09.06.13 for my german locale, where Monday is start of the week.

Resources