How accurate is NSTimeInterval - ios

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.

Related

Calculate difference between 2 NSDate in year, month, day ignore time?

Giving an example, I have 2 NSDate in the same timezone UTC
NSDate *date1 = [NSDate dateWithString:#"2019-05-31 22:00:00 +0000"];
NSDate *date2 = [NSDate dateWithString:#"2019-06-01 01:00:00 +0000"];
I want to make a function to calculate difference between them in Year, month, day ignore time part. Expect result above
Year: 0
Month: 1
Day: 1
Or another example with
NSDate *date1 = [NSDate dateWithString:#"2019-02-28 22:00:00 +0000"];
NSDate *date2 = [NSDate dateWithString:#"2020-03-01 01:00:00 +0000"];
Will get result
Year: 1
Month: 13
Day: 367
I have tried answers in this question How can I calculate the difference between two dates?
But those approach using NSTimeInterval seems not reliable because of 365 or 366 days of year and does not ignore time. Answer like
NSDateComponents *components;
NSInteger days;
components = [[NSCalendar currentCalendar] components: NSDayCalendarUnit
fromDate: date1 toDate: date2 options: 0];
days = [components day];
Give wrong result too.
You could convert each NSDate value to an NSDateComponents value using NSCalendar's componentsInTimeZone:fromDate: method. This will give you the year, month and day values for your two dates, now implement your difference algorithm, which might be:
subtract the earlier date from the larger one - you can determine that based on comparing the two NSDate values
the year difference is just simple subtraction of the year components of the NSDateComponents values
the month difference is the difference between the month components, if this is negative add 12
the day difference is similar but in the negative case you have to add the length of month, which is 28, 29, 30 or 31 - figuring which is left as an exercise :-) (NSCalendar/NSDate Methods should help here)
While this guess at your required algorithm might be wrong, whatever your algorithm you should be able to implement it based on the year, month and day components. HTH
Update
So my first guess at your algorithm was wrong, my second guess is that your three differences; years, months, days; are all meant to be independent approximations of the difference in the corresponding unit. So the year difference ignores the months, days and time; the month difference ignores the days and time; and the days difference ignores the time. This is why 31 May and 1 June are "1 month" apart - the day is ignored. This guess may also be wrong of course but here is how to do it:
order you two dates so the difference is going to be positive.
get just the year, month and day components (or get them all and then discard the others) – this will discard the time component. Use one of NSCalendar's methods to do this.
your year difference is just the difference between the year components
your month difference is the difference between your month components (which could be negative) plus 12 times your year difference
your day difference can be found using components:fromDateComponents:toDateComponents:options: requesting only the day component
[Note: be careful to use the same timezone as the original dates – this is a bit fiddly as you may need to extract it from the date strings yourself (extract the +hhmm and make a time zone). You must remember that an NSDate does not store the time zone, its just an absolute point in time (so equivalent times in different times zones produce the same NSDate value) and for your calculations you want them based on the original time zone as two times one the same day in one timezone can be on different days in a different time zone...). You can set the timezone of an NSCalendar instance or use methods which take timezones when converting from NSDate to NSDateComponents]

how to give format in NSDate while converting it into timestamp using Swift

I have two different values of timestamp on same day in different time. So I want to provide format 'yymmdd' to the NSDate to covert date into timestamp since the value changes acc. to time. If anyone could help me in this or suggest me since i am new to Swift.
I was a bit confused about what you were asking for but after reading your own comments/answers to the original post I understand that you are looking for "the epoch unix timestamp". You can easily get this from any NSDate object as follows:
NSDate *yourDate = [NSDate date];//now in this example
NSTimeInterval epochTimestamp = [yourDate timeIntervalSince1970];
NSString *epochTimestampString = [#(epochTimestamp) stringValue];
NSLog(#"%#",epochTimestampString);
This way you will get the epoch timestamp defined as the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970.

Querying for a date constraint

I've done research on this question and thought I found the answer which makes sense but it's not working so i'm not sure if I'm making a silly mistake. I want to query for data that is created in the past week, month, year,..etc. I looked at my data already uploaded to parse and to test it out, I wanted to query for the data from the past 5 days. For some reason, it finds 0 objects when there should be five objects created after August 8th. When I switched to lessThanOrEqualTo it found all 9 objects even though there's 4 objects created before August 8th. Any clarity on this. Thanks
NSDate *then=[NSDate dateWithTimeIntervalSinceNow:-7200];
PFQuery *query=[PFQuery queryWithClassName:#"urinationAmount"];
[query whereKey:#"user" equalTo:[PFUser currentUser]];
[query whereKey:#"createdAt" greaterThanOrEqualTo:then];
urinationDataArray=[query findObjects];
NSLog(#"Objects in Array:%i",urinationDataArray.count);
According to Date and Time Programming Guide, you shouldn't manually do calendar calculations.
Quote from the same document says:
NSDate provides the absolute scale and epoch for dates and times, which can then be rendered into a particular calendar for calendrical calculations or user display. To perform calendar calculations, you typically need to get the component elements of a date, such as the year, the month, and the day. You should use the provided methods for dealing with calendrical calculations because they take into account corner cases like daylight savings time starting or ending and leap years.
So five days ago should be something like this:
NSCalendar * currentCalendar = [NSCalendar currentCalendar];
NSDateComponents * dateComponents = [NSDateComponents new];
NSDate * now = [NSDate date];
[dateComponents setDay:-5];//five days ago
NSDate * fiveDaysAgo = [currentCalendar dateByAddingComponents:dateComponents toDate:now options:0];
You can check NSDateComponents Class Reference for further calendar units that you can use for your calculations.
The following code will not give you five days ago:
NSDate *then=[NSDate dateWithTimeIntervalSinceNow:-7200];
You should do:
NSDate *then=[NSDate dateWithTimeIntervalSinceNow:-1 * (86400 * 5)];
There are 86,400 seconds in a day.

Setting and Retrieving Dates in iOS that avoid issues with timezone changes

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.).

Seconds Until a Specified Day of the Week

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.

Resources