I am having some trouble calculating the number of seconds until a specified day of the week.
For example, calculate the number of seconds until Sunday from the current time in seconds.
The calculation must be dynamic so it is compatible for every Sunday.
Is there any way to accomplish this without specifying a specific date?
You need to use NSCalendar and NSDateComponents. See the code example below. Make sure to set the calendar to "gregorian" if you want the correct 7 day week for the US.
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
unsigned unitFlags = NSWeekdayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDate *date = [NSDate date];
NSDateComponents *comps = [gregorian components:unitFlags fromDate:date];
// Just a test line for your benefit.
NSLog(#"%li %li %li %li",(long)comps.weekday,(long)comps.hour, (long)comps.minute, (long)comps.second);
// I did this kinda fast - check it over good. Will need some sort of if statement in case the current day is Sunday (7)
NSInteger sec = (60 - comps.second) + (60 - comps.minute)*60 + (24 - comps.hour)*3600 + (6 - comps.weekday)*24*60*60;
NSLog(#"%li",sec);
You will have to modify this somewhat for your use. For instance if you want the day of the week to be a variable, etc. but this should get you on the right track.
Hope this helps. Good luck.
EDIT
See Duncan's remarks below for a better way to actually calculate the seconds.
The NSCalendar class has a whole bunch of methods for this sort of thing. Do a search on "Calendrical calculations" in Xcode for more information.
You'll need a Gregorian NSCalendar (or other type for other calendars like the Chinese, Arabic, Hebrew calendar) NSDates, and NSDateComponents objects.
Look at the method components:fromDate:toDate:options: in particular.
EDIT: That'll teach me to leave an answer unfinished and go do the dishes.
One difference from Dylan's post, though: I would take the current NSDate, convert it to components, then set the day-of-week to the desired future day of week, then convert back to an NSDate, and finally take the difference between the two dates using the NSDate method timeIntervalSinceDate.
Related
I have to determine the day of the week in an Objective C class.
Trying to use this method:
- (NSUInteger)ordinalityOfUnit:(NSCalendarUnit)smaller
inUnit:(NSCalendarUnit)larger
forDate:(NSDate *)date;
This is my code:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
(int) [gregorian ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekday forDate:[NSDate date]];
It always returns -1, no matter what day it is. Even if I change the date, or loop through a whole week.
However, using this method, it works well:
- (NSDateComponents *)components:(NSCalendarUnit)unitFlags
fromDate:(NSDate *)date;
So far so good. The only problem is: this method is deprecated (first deprecated in iOS 8.0, so quite a long time ago).
Little bit afraid of using old deprecated methods for production. Is there a way of making the (not even very) new method work?
From the ordinalityOfUnit:inUnit:forDate: docs:
Returns, for a given absolute time, the ordinal number of a smaller calendar unit (such as a day) within a specified larger calendar unit (such as a week).
You have specified the same unit for the first two arguments, you need to specify day for the first and week for the second:
[gregorian ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitWeekOfMonth forDate:[NSDate date]]
HTH
I have a "time-tracking" app that allows a user to create an entry on whichever days a user desires but only one permitted per day. I store these entries into CoreData.
[rateObject setValue:[Day dateWithHour:currentDate forHour:12] forKey:#"date"];
Later, I retrieve these entries using the following code.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Day"];
request.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES]];
request.predicate = [NSPredicate predicateWithFormat:#"whoWorked == %# && date => %# && date =< %#", whoWorked, [Day dateWithHour:fromDate forHour:0], [Day dateWithHour:toDate forHour:24]];
As you can see I placed the entry in the middle of the day (12 hours) and retrieved the dates from start of the first day (0 hours) and end of the last day (24 hours).
This is a hack which seems to work when I only shift timezones by a few hours or switch for Standard Time to Daylight Savings. It falls apart when I move from NA to Europe i.e. the entries in the old timezone appear on different days in the retrieval.
What I want to get to is an entry made on April 25, 2014 appears on April 25, 2014 no matter what timezone I am currently in.
I have looked at a number of sources to understand NSDate, NSDateComponents, NSCalendar etc but can't seem to land on a good understanding that will allow me to implement this correctly or cleanly. Any advice appreciated.
Here is my method for creating a date with a specific hour (in Day).
+ (NSDate *)dateWithHour:(NSDate *)date forHour:(int)hour {
// Create and initialize date component instance
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit fromDate:date];
if (hour<0) hour = 0;
else if (hour>24) hour = 24;
[dateComponents setHour:hour];
// Create period dates
NSDate *newDate = [[NSCalendar currentCalendar] dateFromComponents:dateComponents];
return newDate;
}
The best to use time-zone and platform independent is the timestamp, which you can get from NSDate as well. With that you can easily do calculations with Time-intervals. If you want to have a specific date it is always related to the time zone of the user. Then you can convert the timestamp into a user-formatted date. Like mentioned by Zaph he might want to see the date where he currently is. Again with the timestamp this is always reliable.
If you want to calculate that an entry can be done only once per calendar day in the location where somebody is, then you can calculate with ˙NSCalendar˙ and ˙NSDateComponents˙ for example what day a timestamp represents and then from the beginning of that day calculate 24 hours up. Conversion back to a timestamp gives you a helpful range for check if something is within the same calendar day. The NSCalendar is the base for calculation even if you will probably use Gregorian most of the time. With the NSDateComponentsyou can what ever part you want from your Timestamp (e. g. only the day, the month, the year, etc.).
I'm putting together a game where there's a tournament every week, and every week there's a different special bonus for the game.
To make this work I need to know which week it is so I can select the right bonus, and make sure the score goes to the right tournament.
A trivial answer is to take the number of days since epoch, offset to get to a monday, then compute the number of days and divide by 7. Obviously this fails because of leap year.
Another option would be to figure out which week of the year you're on, but that gets weird when you transition from one year to the next. Also, the tournament ends at the end of the day on Sunday, so it doesn't follow the normal week borders.
I was about to start doing some fairly complicated stuff using the year, day of year and day of week to try to figure it out, but I thought I'd ask here in case there was an easy solution I was missing.
This will be done in Objective-C on iOS.
This should work:
// Choose any reference date which is a Monday:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *refComp = [[NSDateComponents alloc] init];
refComp.year = 1970;
refComp.month = 1;
refComp.day = 5;
NSDate *refDate = [cal dateFromComponents:refComp];
// Compute number of weeks between your date and the reference date:
NSDateComponents *comp = [cal components:NSCalendarUnitWeekOfYear fromDate:refDate toDate:yourDate options:0];
NSInteger weeks = comp.weekOfYear;
But calculating the number of days (since some Monday) and dividing by 7 should
give the same result because every week has 7 days, regardless of leap years.
I am trying to get the time difference between two NSDates.
NSDate *d1 = somedate;
NSDate *d2 = someOtherdate;
NSTimeInterval sec = [d2 timeIntervalSinceDate:d1];
How accurate is NSTimeInterval?
Does it take care of the variation in number of days in a month, for example if the number of days is 28,29,30 or 31? Also time zone difference?
How accurate is NSTimeInterval
On the platforms I know they have Cocoa, NSTimeInterval is typedeffed to double,
Does it take care of the variation in number of days in a month?
It's not NSTimeInterval that does that, it's a type. It's the various classes (NSDate, whatever) that take care of all these deviances. And they do it really well.
NSTimeInterval is in fact just a typedeffed double. Because NSDate represents a single point in time (independent of time zone, calendar, etc.), NSTimeInterval represents the number of seconds between the two dates (points in time).
NSTimeInterval is actually just a typedef for double.
NSDate encapsulates a time interval and provides an interface to interact with it.
If you want that date to be referenced to our actual calendar, you have to use the NSCalendar and NSDateComponents classes.
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:[NSDate date]];
Then you can get data out of the components object:
components.day
components.week
// etc
Assume the Mayan culture never went extinct. This right instant in time would be represented by the same NSDate object, but through a different NSCalendar (like a NSMayanCalendar) you would get a completely different representation of that date.
How accurate is NSTimeInterval?
It's accurate to milliseconds (if not more).
Does it take care of the variation in number of days in a month, for example if the number of days is 28,29,30 or 31?
Yes, the difference will be in seconds. NSDate deals with actual days in a month as well as leap years and daylight savings, etc.
Also time zone difference?
NSDate values are always stored in UTC so your NSDate objects are always in the same timezone. There is nothing to deal with for this.
Please help me, I'm stuck!
I am trying to make a simple calendar app and are desperate. I need a value of the weeks of a given year (52 or 53) and I understand that the answer to this question is in using initRecurrenceWithFrequency. But I can't get my code to work!!!! Please help.
/John
#AliSoftware
Thannks a lot!
However this code snippet (below) gave me 1 instead of 53. Do you know why?
BTW my Simulator settings state gregorian, as its calendar.
/John
NSDateComponents *comps = [NSDateComponents alloc];
[comps setDay:31];
[comps setMonth:12];
[comps setYear:year];
NSCalendar *usersCalendar =[[NSLocale currentLocale] objectForKey:NSLocaleCalendar];
NSDate *date = [usersCalendar dateFromComponents:comps];
NSDateComponents *weekComps =
[usersCalendar components:NSWeekCalendarUnit fromDate:date];
NSInteger totalNumberOfWeeksInThisYear = [weekComps week];
If you need to know which week # of the year is a given date, or any similar stuff (number of weeks in a year, and so on) you should use NSDateComponents for that.
You should really read the Date and Time Programming Guide in Apple Documentation. It explains everything about dates, calendars and date components, the concepts and subtleties (leap years, etc), how the Cocoa classes related to dates and calendars interact with each other and how to use them.
It is really detailed (as every Programming Guide in Apple's documentation, in fact), and you can't really think about creating a calendar app without reading this documentation.
(BTW, I don't see how you question about the weeks of the year would be related to the initRecurrenceWithFrequency method of EKRecurrenceRule ?!!)