I am trying to find out if the recent Do Not Disturb bug (Ars Technica link) is affecting my code. I am trying to get the date and timestamp for midnight the previous Monday. Since I am writing this on a Monday, I would expect to get today's date (January 7, 2013), yet I am January 4, 2013.
I am following the guide posted by Apple, but I am trying to modify it to find the previous Monday, instead of Sunday.
+(NSTimeInterval)StartOfWeekMonday {
NSTimeInterval finalTime = -1;
NSInteger weekday;
NSDate *monday = 0;
NSCalendar *greg = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdays = [greg components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
NSDateComponents *subtract = [[NSDateComponents alloc] init];
NSDateComponents *final = 0;
weekday = ( weekdays.weekday == 1 ) ? 6 : weekdays.weekday;
[subtract setDay:(0 - weekday - 1)];
monday = [greg dateByAddingComponents:subtract toDate:[NSDate date] options:0];
final = [greg components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:monday];
monday = [greg dateFromComponents:final];
#ifdef DEBUG
NSLog( #"%s - %d -> Weekday: %d", __FILE__, __LINE__, weekday );
NSLog( #"%s - %d -> Monday: %#", __FILE__, __LINE__, [monday descriptionWithLocale:[NSLocale currentLocale]] );
#endif
finalTime = [monday timeIntervalSince1970];
return finalTime;
}
My log out put below, the weekday is correct (I am writing this on a Monday), yet the date is obviously wrong, it should be Monday: Monday, January 7, 2013, 12:00 AM
2013-01-07 15:07:33.792 MY-APP[5524:14c03] Weekday: 2
2013-01-07 15:07:36.757 MY-APP[5524:14c03] Monday: Friday, January 4, 2013, 12:00:00 AM Eastern Standard Time
This is being in the simulator right now, but all my other date calculations are giving the expected values.
Is it likely that I am being affected by the recent Do Not Disturb Bug and will have to wait for tomorrow (January 8, 2013) to see the expected results, or am I missing something entirely different?
The issue was with how to calculate the subtraction. Either I completely mis understood the example provided by Apple, or it was not clear enough. The solution for calculating the subtraction was as follows:
weekday = ( weekdays.weekday == 1 ) ? 8 : weekdays.weekday;
toSubtract = ( 2 - weekday );
[subtract setDay:toSubtract];
Running various tests produced the proper "Monday." In the end, this had nothing to do with the date bug affecting Do Not Disturb.
Related
I'm converting dates from an Excel spreadsheet to NSDate's, but for some reason they always come out two days ahead: Sundays come out as Tuesdays, etc.
My conversion method is based on the following info from cpearson.com:
Excel stores dates and times as a number representing the number of
days since 1900-Jan-0, plus a fractional portion of a 24 hour day:
ddddd.tttttt . This is called a serial date, or serial date-time.
(...) The integer portion of the number, ddddd, represents the number
of days since 1900-Jan-0. (...) The fractional portion of the number,
ttttt, represents the fractional portion of a 24 hour day. For
example, 6:00 AM is stored as 0.25, or 25% of a 24 hour day.
Similarly, 6PM is stored at 0.75, or 75% percent of a 24 hour day.
- (NSDate *)dateFromExcelSerialDate:(double)serialdate
{
if (serialdate == 0)
return nil;
NSTimeInterval theTimeInterval;
NSInteger numberOfSecondsInOneDay = 86400;
double integral;
double fractional = modf(serialdate, &integral);
NSLog(#"%# %# \r serialdate = %f, integral = %f, fractional = %f",
[self class], NSStringFromSelector(_cmd),
serialdate, integral, fractional);
theTimeInterval = integral * numberOfSecondsInOneDay; //number of days
if (fractional > 0) {
theTimeInterval += numberOfSecondsInOneDay / fractional; //portion of one day
}
NSCalendar *nl_gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSTimeZone *nl_timezone = [[NSTimeZone alloc] initWithName:#"Europe/Amsterdam"];
[nl_gregorianCalendar setTimeZone:nl_timezone];
NSDateComponents *excelBaseDateComps = [[NSDateComponents alloc] init];
[excelBaseDateComps setMonth:1];
[excelBaseDateComps setDay:1];
[excelBaseDateComps setHour:00];
[excelBaseDateComps setMinute:00];
[excelBaseDateComps setTimeZone:nl_timezone];
[excelBaseDateComps setYear:1900];
NSDate *excelBaseDate = [nl_gregorianCalendar dateFromComponents:excelBaseDateComps];
NSDate *inputDate = [NSDate dateWithTimeInterval:theTimeInterval sinceDate:excelBaseDate];
NSLog(#"%# %# \r serialdate %f, theTimeInterval = %f \r inputDate = %#",
[self class], NSStringFromSelector(_cmd),
serialdate, theTimeInterval,
[self.nl_dateFormatter stringFromDate:inputDate]);
return inputDate;
}
The spreadsheet was produced in the Netherlands, presumably on a Dutch version of Microsoft Excel.
Spreadsheet date Sunday July 6, 2014 00:00 yields the following results:
dateFromExcelSerialDate:
serialdate = 41826.000000, integral = 41826.000000, fractional =
0.000000 theTimeInterval = 3613766400.000000 inputDate = 08 jul. 2014 01:40
Similarly, Sunday July 13, 2014 00:00 yields:
serialdate = 41833.000000, integral = 41833.000000, fractional =
0.000000 theTimeInterval = 3614371200.000000 inputDate = 15 jul. 2014 01:40
I can correct the output by subtracting 2 days, one hour and 40 minutes:
theTimeInterval -= ((60 * 60 * 24 * 2) + (60*60) + (60*40));
but I have no idea how robust that is.
That difference of two days made me think it had something to do with leap year corrections, so I tried to let the calendar do the calculations by adding the NSTimeInterval seconds to the excelBaseDate, like so:
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setSecond:theInterval];
NSDate *inputDate = [nl_gregorianCalendar dateByAddingComponents:comps
toDate:excelBaseDate
options:0];
Strangely enough, that gave me dates somewhere in the 1870's. Who knows what is going on?
there are two things here:
your start date is 1900-Jan-1 but your referred description clearly says: the reference is 1900-Jan-0 – you may add an extra day here;
year 1900 was not a leap-year – you may add an extra day here;
I guess, this is pretty much the reason why you get two extra days every occasion.
Microsoft knows about that, see more about the topic here.
I want to have the day of week name for a given number, here is the pseudo-code :
getDayStringForInt:0 = sunday
getDayStringForInt:1 = monday
getDayStringForInt:2 = tuesday
getDayStringForInt:3 = wenesday
getDayStringForInt:4 = thursday
getDayStringForInt:5 = friday
getDayStringForInt:6 = saturday
I have tried with the follow code, but some thing is not working ...
- (void) setPeriodicityDayOfWeek:(NSNumber *)dayOfWeek{
gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
dateFormatter = [[NSDateFormatter alloc] init];
NSLocale *frLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[dateFormatter setLocale:frLocale];
[gregorian setLocale:frLocale];
NSDate *today = [NSDate date];
NSDateComponents *nowComponents = [gregorian components:NSYearCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:today];
[nowComponents setWeekday:dayOfWeek];
NSDate *alertDate = [gregorian dateFromComponents:nowComponents];
[dateFormatter setDateFormat:#"EEEE"];
NSLog(#"Day Of Week : %# - Periodicity : %#", dayOfWeek, [dateFormatter stringFromDate:alertDate]);
alert.periodicity = [dateFormatter stringFromDate:alertDate];
}
My log is very strange :
Day Of Week : 0 - Periodicity : monday
Day Of Week : 1 - Periodicity : wenesday
Day Of Week : 2 - Periodicity : friday
Day Of Week : 3 - Periodicity : friday
Day Of Week : 4 - Periodicity : tuesday
Day Of Week : 5 - Periodicity : sunday
Day Of Week : 6 - Periodicity : sunday
Any idea ? any better solution ...
Since this has become the accepted answer, I'll post the "right" solution here too. Credits to Rob's answer.
The whole thing can simply be achieved using the [shortWeekdaySymbols][1] method of NSDateFormatter, so the full solution boils down to
- (NSString *)stringFromWeekday:(NSInteger)weekday {
NSDateFormatter * dateFormatter = [NSDateFormatter new];
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
return dateFormatter.shortWeekdaySymbols[weekday];
}
Original answer
Beware, you're passing a pointer to NSNumber to a method that requires a NSInteger.
The compiler is not warning you since a pointer is indeed an integer, just not the one you would expect.
Consider this simple test:
- (void)foo:(NSInteger)a {
NSLog(#"%i", a);
}
- (void)yourMethod {
[self foo:#1]; // #1 is the boxed expression for [NSNumber numberWithInt:1]
}
This prints something like 185035664, which is the pointer value, i.e. NSNumber * when cast to NSInteger.
You should either use [dayOfWeek integerValue] or directly turn dayOfWeek into a NSInteger in your method signature.
Also I think you're getting something else wrong: from the doc of setWeekday:
Sets the number of weekday units for the receiver. Weekday units are
the numbers 1 through n, where n is the number of days in the week.
For example, in the Gregorian calendar, n is 7 and Sunday is
represented by 1.
Sunday is 1, so you'd better check the correspondence with your representation too.
See https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDateFormatter_Class/Reference/Reference.html#//apple_ref/occ/instm/NSDateFormatter/weekdaySymbols
Simply use weekdaySymbols to retrieve the dayname.
Thanx to Every one, here is a clean response :
/**
* getting the day of week string for a given day of week number
*
* #param dayOfWeekNumber 0 return sunday, 6 return saturday
*
* #return a string corresponding at the given day of week.
*/
- (NSString*) getDayOfWeekStringForDayOfWeek:(NSInteger)dayOfWeek{
return [[dateFormatter shortWeekdaySymbols] objectAtIndex:dayOfWeek];
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Determine if current local time is between two times (ignoring the date portion)
In iOS, how can I do the following:
I have two NSDate objects that represent the opening and closing times for a store. The times within these objects are accurate but the date is unspecified (the store opens and closes at the same time regardless of the date). How can I check if the current time falls between in this time frame?
Note, if it would help for the opening and closing times to be in another format other than NSDate objects, I'm fine with that. Currently, I'm just reading in a date string such as "12:30" from a file and using date formatter to create a matching NSDate object.
Update: Note that this solution is specific to your case and assumes that store opening hours don't span two days. For example it won't work if the opening hour goes from Monday 9pm to Tuesday 10am. Since 10pm is after 9pm but not before 10am (within a day). So keep that in mind.
I cooked up a function which will tell you if the time of one date is between two other dates (it ignores the year, month and day). There's also a second helper function which gives you a new NSDate with the year, month and day components "neutralized" (eg. set to some static value).
The idea is to set the year, month and day components to be the same between all dates so that the comparison will only rely on the time.
I'm not sure if it's the most efficient approach, but it works.
- (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);
}
I used this code to test it. You can see that the year, month and days are set to some random values and don't affect the time checking.
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:#"yyyy:MM:dd HH:mm:ss"];
NSDate *openingDate = [dateFormatter dateFromString:#"2012:03:12 12:30:12"];
NSDate *closingDate = [dateFormatter dateFromString:#"1983:11:01 17:12:00"];
NSDate *targetDate = [dateFormatter dateFromString:#"2034:09:24 14:15:54"];
if ([self isTimeOfDate:targetDate betweenStartDate:openingDate andEndDate:closingDate]) {
NSLog(#"TARGET IS INSIDE!");
}else {
NSLog(#"TARGET IS NOT INSIDE!");
}
Im trying to know if the current day of the week + and hour is in between of 2 other weekday + hour.
Let's say, right now is "Tuesday 16:26" and there is an interval that starts with "Tuesday 16:00" and "Tuesday 22:00" so it should return YES.
Im creating dates from the previous strings, and this function tells me if it's in the interval.
This function is part of a class, whose attributes "fechaInicio" and "fechaFin" are start date and end adate respectively.
- (BOOL)dateInInterval:(NSDate *)testDate {
// date1 is the instance variable containing the starting date
// date2 is the instance variable containing the ending date
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en"] autorelease]];
[dateFormatter setDateFormat:#"EEEE HH:mm"];
NSString *dateString = [dateFormatter stringFromDate:self.fechaInicio];
NSLog(#"La fecha es: %#", dateString);
dateString = [dateFormatter stringFromDate:self.fechaFin];
NSLog(#"La fecha es: %#", dateString);
NSLog(#"time interval nicio: %d", [testDate timeIntervalSinceDate:self.fechaInicio]);
NSLog(#"time interval fin: %d", [testDate timeIntervalSinceDate:self.fechaFin]);
return ([testDate timeIntervalSinceDate:self.fechaInicio] > 0 &&
[testDate timeIntervalSinceDate:self.fechaFin] < 0);
}
The thing is that in never returns YES, even though I can see the date is in the interval. Im afraid how Im turning the string to date. I input "Tuesday 16:00" what year is it, what month, if I format the actual date to "EEEE HH" will it save the month and year?
Thanks
For your case, I'd advice to find some fixed known date for Monday and apply the full date for each EEEE HH:mm string. This will give correct values inside date and i think your logic will start working
I have two dates in format(MM/dd/yyyy hh:mm:ss:SS). For the both dates I have converted the two dates to strings by using (stringFromDate) method. But I could not get the difference between them and show them in my console. Please give me an idea how I should get it?
Thank you.
Example
NSDate *today = [NSDate date];
NSTimeInterval dateTime;
if ([visitDate isEqualToDate:today]) //visitDate is a NSDate
{
NSLog (#"Dates are equal");
}
dateTime = ([visitDate timeIntervalSinceDate:today] / 86400);
if(dateTime < 0) //Check if visit date is a past date, dateTime returns - val
{
NSLog (#"Past Date");
}
else
{
NSLog (#"Future Date");
}
Keep the dates as dates, get the difference between them, then print the difference.
From the docs on NSCalendar and assuming gregorian is an NSCalendar:
NSDate *startDate = ...;
NSDate *endDate = ...;
unsigned int unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents *comps = [gregorian components:unitFlags fromDate:startDate toDate:endDate options:0];
int months = [comps month];
int days = [comps day];
Generally I see day delta calculations handled by converting day/year values into flat days (usually days since some starting epoch, like 01/01/1970).
To aid in this, I've found it helpful to create a table of days into the year that each month starts. Here's a class I used for this recently.
namespace {
// Helper class for figuring out things like day of year
class month_database {
public:
month_database () {
days_into_year[0] = 0;
for (int i=0; i<11; i++) {
days_into_year[i+1] = days_into_year[i] + days_in_month[i];
}
};
// Return the start day of the year for the given month (January = month 1).
int start_day (int month, int year) const {
// Account for leap years. Actually, this doesn't get the year 1900 or 2100 right,
// but should be good enough for a while.
if ( (year % 4) == 0 && month > 2) {
return days_into_year[month-1] + 1;
} else {
return days_into_year[month-1];
}
}
private:
static int const days_in_month[12];
// # of days into the year the previous month ends
int days_into_year[12];
};
// 30 days has September, April, June, and November...
int const month_database::days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
month_database month;
}
As you can see from the start_day method, the main issue you are going to be wrestling with is how many leap days are contained in your range. Within our epoch the calculation I used there is good enough. The actual rule for which years contain leap days is discussed here.
February 29 in the Gregorian calendar,
the most widely used today, is a date
that occurs only once every four
years, in years evenly divisible by 4,
such as 1976, 1996, 2000, 2004, 2008,
2012 or 2016 (with the exception of
century years not divisible by 400,
such as 1900).
If you just want the difference in days, you can do this. (Based on mihir mehta's answer.)
const NSTimeInterval kSecondsPerDay = 60 * 60 * 24;
- (NSInteger)daysUntilDate:(NSDate *)anotherDate {
NSTimeInterval secondsUntilExpired = [self timeIntervalSinceDate:anotherDate];
NSTimeInterval days = secondsUntilExpired / kSecondsPerDay;
return (NSInteger)days;
}