NSDate and the next day - objective-c

What I used to do was using the timestamp and then adding 86400 seconds ( 24 * 60 * 60 ). As I read a recent question on stackoverflow noting that that is not correct for every day, I want to change this.
So the next thing I came up with was by using NSDateComponents. I simply get the current day and add 1. Now I was wondering how "smart" that is. Like if the day is equal to 31, does it set the day to 1 and the month to whatever it is +1 ( or when it is 12 to 1 again ) ? I can do this manually but that would only work properly for the gregorian calendar so I don't really know whether that would be a good solution either..

It is smart, unless you want it to be dumb.
From the documentation for -[NSCalendar dateByAddingComponents:toDate:options]:
If you specify no options (you pass 0), overflow in a unit carries into the higher units (as in typical addition).
If you did not want units overflowing into higher units, then you would pass "NSWrapCalendarComponents" as the value of the options: parameter.

Be very careful of DST. It is different for various time zones. AND It happens at Midnight
I believe that if you call [NSCalendar date by addingComponents:toDate:options] and your date is between 11-12 pm, you risk missing or re-running a day because of DST.
To get around this, is suggest changing your date to noon on the day and then adding the day.
// increment by 1 calendar day
// and convert back to NSDate objects
NSDateComponents *comps = [[[NSDateComponents alloc] init] autorelease];
[comps setDay:1];
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[gregorian setTimeZone:self.myTimeZone]; // time zone of location;
// Use local noontime for the date to avoid problems around
// midnight, particularly near daylightsavings time discontinuities.
NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents* dateComponents = [gregorian components:unitFlags fromDate:date];
dateComponents.hour = 12;
NSDate* noonDate = [gregorian dateFromComponents:dateComponents];
NSDate *newDate = [gregorian dateByAddingComponents:comps toDate:noonDate options:0];
I'm not sure about the options:0 setting, but I did test this pretty thoroughly. I use it to get an array of with 10 days of dates. It gives correct wrapping around the various day/month/year discontinuities. Check out SunPose Rise Set on the app store if you want to test it. (yes, it's free)

Related

Core plot CPTTimeFormatter fixed xAxis time interval

I am using core plot to plot a graph with the xAxis.labelFormatter being a CPTTimeFormatter. So I have set a reference date, and I have my data plotted nicely along the xAxis. The first xAsis label is "00" (midnight) and the last xAxis label is "00" which is great, working as required to display data over a 24 hour period.
However, I noticed when I change the iPhone simulator time zone to a different timezone, the first xAxis label is "17" and the last is "17". In other words the xAxis has shifted backwards 7 hours. I need the graph to show data from midnight to midnight (for a specific timezone), regardless of where the user is located physically.
The problem is that my plot range is not setting correctly, in the below snippet the 'baseInt' is not the start of the day in the timezone I want..
CPTPlotRange *plotRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromDouble(baseInt) length:CPTDecimalFromDouble(60*60*24)];
I work out baseInt by:
NSTimeInterval baseInt = [[self beginningOfDay:[NSDate date]] timeIntervalSinceDate:[CommonStuff referenceDate]];
It's the [NSDate date] that's throwing this out. The [CommonStuff referenceDate] is not changing.
How can I specify a NSTimeInterval from my referenceDate (fixed/not changing) to the start of the day in a specific timezone? Perhaps I need to work out the difference between timezones and add that difference in seconds to baseInt?
FYI my beginning of day method is as follows:
//return me the date for start of the day
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSCalendarUnitMonth | NSCalendarUnitYear | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond | NSCalendarUnitDay ) fromDate:date];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
return [cal dateFromComponents:components];
You answered your own question—you need to account for the timezone difference. One way would be to set the timezone of the NSCalendar in the -beginningOfDay: method.

calculate the number of weeks in a daterange

I need to make some time-based statistics over logs that goes on over the course of a year,
all I need to do is the count of the (solar) weeks inside a determined range of dates.
i.e. I need to calculated how many weeks have been occurred between the 1st of Aug 2011 and the 3rd of Sep 2012. and things like this.
I've had the idea of counting the days between the two dates and then divide by 7, but that doesn't count the fact that I could be in between weeks an in this case the value wold not be correct / precise.
Anybody had faced a similar issue before?
I assume you have your two dates in NSDates (date1 and date2). Then call
NSCalendar *calendar = [NSCalendar currentCalendar];
//declare your unitFlags
int unitFlags = NSWeekCalendarUnit;
NSDateComponents *dateComponents = [calendar components:unitFlags fromDate:date1 toDate:date2 options:0];
int weeksInBetween = [dateComponents week];
Hope that helps
NSCalendar is the class that you should take a at into. It allows you to get the NSDateComponents from two dates using the components:fromDate:toDate:options: method, while dealing with all the messed up time stuff for you.
NSDateComponents *difference = [[NSCalendar currentCalendar] components:NSCalendarUnitDay
fromDate:startDate]
toDate:endDate]
options:0];
NSInteger weeksDiffCount = (((NSInteger)[difference day])/7);
Hope it helps.

Determining Day Components of an NSDate Object (Time Zone Issues)

How would I be able to determine if a certain NSDate object falls within a certain day? Since all dates calculate to the GMT time zone, a date maybe actually be on the 8th instead of the 9th, for example. What's the best way to account for the time zone difference when calculating things like days, months, day of week, etc.
An NSDate is an absolute point in time. When you want to know which day it falls on, you have to consider your specific time zone. Since you get the NSDateComponents with an NSCalendar, you can use setTimeZone: on the calendar to get the components (month, day, hour etc.) for the time zone you're interested in.
If you deal with "calendar dates" (i.e. dates that should always represent a specific day/month and not an absolute point in time), you could always use a fixed time zone (e.g. GMT) to present the dates. NSDateFormatter (which you use for displaying dates to the user) also has a timeZone property.
You need to set the desired timezone in NSCalendar before using it to break the date into parts:
NSCalendar* gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[gregorian setTimeZone:[NSTimeZone localTimeZone]]; // or timeZoneWithName:#"GMT" etc.
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents* compsDate = [gregorian components:unitFlags fromDate:date];
// [compsDate year] [compsDate month] [compsDate day]

NSDateComponents issue - incorrect day

I have a NSDateComponents problem. I have two NSDates that I am trying to compare by checking if their year, month and day match. This I am doing by converting the NSDate values to these integer components as follows:
//NSDate *cgiDate is previously set to 2011-08-04 00:00:00 +0000
//NSDate *orderDate is previously set to 2011-08-04 14:49:02 +0000
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *cgiDateComponents = [calendar components:( NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit ) fromDate:cgiDate];
NSCalendar *orderCalendar = [NSCalendar currentCalendar];
NSDateComponents *orderDateComponents = [orderCalendar components:( NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit ) fromDate:orderDate];
if (([cgiDateComponents day] == [orderDateComponents day]) &&
([cgiDateComponents month] == [orderDateComponents month]) &&
([cgiDateComponents year] == [orderDateComponents year])) {
GHTestLog(#"MATCHED");
} else {
GHTestLog(#"Not matched");
GHTestLog(#"Day: %d vs. %d", [cgiDateComponents day], [orderDateComponents day]);
}
My result is Not Matched, Day: 3 vs. 4. Why would this be?
I have read with great interest the following questions:
NSDateComponents - day method returning wrong day and
https://stackoverflow.com/questions/3920445/nsdatecomponents-incorrectly-reporting-day however neither answer my question of why this is not working.
Any advice?
I think the issue is the following:
Your dates are set in GMT time zone (+0000)
If you are in the US, for example at GTM-6, then by the -currentCalendar the first date will be 6pm on Aug 3rd while second date will be 8:49am on Aug 4th.
You should force your calendar to have the UTC (GMT) timezone, or put the dates in your time zone, depending what is correct for your application.
It looks like you have a time zone issue. Though your dates are set at time zone +0000, your [NSCalendar calendar] call likely returns you a calendar for your local time zone. Given the adjustment to local time, the orderDate is likely in the next day. Manually set your calendars to time zone +0000.
NSDate has an isEqualToDate: function. so what you are trying to do can be done by:
[datea isEqualToDate:dateb];
there is also a Compare: function available, that returns the ordering of the dates.
edit: I'm now aware this doesn't answer your question about why the days return different values when they are set the same. Sorry! this still may help make your code a bit cleaner.

How to determine if an NSTimeInterval occurred during an arbitrary NSDate?

I have an NSTimeInterval, and I'd like to know whether that timestamp falls between the start and end of a date. Basically I need to be able to do something like:
NSDate *today = [NSDate date];
NSTimeInterval myInterval = someInterval;
[date returnYesIfFallsThisDate:myInterval];
Is there a straight forward method to figure out if my NSTimeInterval falls on the same day as an NSDate object?
Thanks all.
An NSTimeInterval is just a number of seconds. To compare it to an NSDate, you need to interpret the time interval relative to some reference point, then create an NSDate with the result, then compare the two NSDates.
There are two standard reference dates: 2001-01-01 and 1970-01-01 (the latter being “UNIX time”). If neither of those is the correct reference date, use NSCalendar to create an NSDate for the correct reference date, then create your NSDate of interest relative to that.
Now for the comparison.
If all you care about is the date on the calendar, then you'll create the NSDate, then use an NSCalendar to get NSDateComponents for the date. Specifically, you'll get the year, month, and day-of-the-month, then compare those.
There's no need to worry about “start” and “end” times; in fact, if you only care about the date on the calendar, you can ignore the time-of-day entirely.
Keep in mind that NSDate doesn't represent a "day" in any form, just a point in time. You'll have to do a little work with NSDateComponents, converted using an NSCalendar (typically the user's default calendar) to figure out the start and end NSDate values, and compare use those to compare your time interval to.
I would start by taking the NSDate that falls within your day, and converting it to an NSDateComponents, but without hours, minutes or seconds. Here's a quick (untested) example to help you get started:
NSDateComponents *comps = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:[NSDate date]];
NSDateComponents *day = [[[NSDateComponents alloc] init] autorelease];
[day setDay:1];
NSDate *start = [[NSCalendar currentCalendar] dateFromComponents:comps];
NSDate *end = [[NSCalendar currentCalendar] dateByAddingComponents:day toDate:start options:0];
Then you can compare the time interval as suggested by Mr. Jalkut (or another method, NSDate can work with NSTimeIntervals in a few different ways). I would definitely spend some time looking over NSCalendar and NSDateComponents in the docs though, you might find a better method than what I'm suggesting for what you need to do.
Is it just me or does the OP seem to think that a NSTimeInterval is a representation of an interval in time from A to B? I.e. an absolute distance (in seconds) from Date A to Date B.
The original question needs to be repose to state that you need to find it based on the reference date. Something like this:
NSDate *refDate = someReferenceDate;
NSTimeInterval interval = someInterval;
NSDate *today = [NSDate date];
BOOL isInRange = false;
isInRange = [today isInterval:interval inRangeFromReference:refDate];
Then that method would use some (or parts of all) of the startegies that Damiel, Peter and Marc mention above.
Conceptually you want two dates that constitute the "start" and "end" of the target date in question. Then you can test the time interval directly against the respective time intervals of those dates.
For instance, if I live in San Francisco, then I want the "start" to be 12AM pacific time, and the end to be 11:59:59.99999 PM pacific time.
Once you've figured out what time zones/etc you want to consider as the start and end points, you can just use the time intervals to do the test:
if ((myInterval >= [earlyMorningDate timeIntervalSinceReferenceDate]) &&
(myInterval <= [lateEveningDate timeIntervalSinceReferenceDate]))
{
NSLog(#"Whoo hoo - it's the right date!");
}