Setting NSDateComponents results in incorrect NSDate - objective-c

I'm trying to get an NSDate object that has 21:00 as the local time - don't care about what day. I'm scratching my head at this very strange result:
NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setHour:21];
[components setMinute:0];
[components setSecond:0];
NSDate *date = [calendar dateFromComponents:components];
NSLog(#"%#", date);
The result is 0001-01-02 04:52:58 +0000
I have no idea why. The current time is 17:34 PST, but the result doesn't change with the local time.
If I adjust the setMinute and setSecond lines to
[components setMinute:7];
[components setSecond:2];
I get 0001-01-02 05:00:00 +0000, which is correct (21:00 PST).

The problem is that railroad time wasn't implemented until November 18, 1883. You're neglecting to set a year so you're getting a date before that. Prior to the implementation of railroad time, the US time zones weren't exact hour differences from GMT. I'm not sure exactly what time zone Apple selects for you but whichever it was seems to have been adjusted by 7 minutes and 2 seconds upon the move to PST in 1883.

Related

Objective-C: Get the number of calendar days between two dates

I just don't get the correct result...so depending on the timezone I want the calendar day difference between two dates. So if one starts at 23:00 on day 1 and ends on 14:00 on day 2 it should return 1. Now my method returns 0, why? Because it's less than 24 hours? Example:
MY Nslog:
CheckForPictures departure date: Tue Jan 28 23:10:00 2020 destinationDate: Wed Jan 29 09:30:00 2020 in timeZone:Europe/Zurich and get a day difference: 0
(Computer has also timezone Zurich, so it is local time)
My Method:
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timeZone];
NSDateComponents *components = [calendar components:NSCalendarUnitDay
fromDate:self.departureTime
toDate:self.destinationTime
options:0];
NSLog(#"CheckForPictures departure date: %# destinationDate: %# in timeZone:%# and get a day difference: %ld", self.departureTime, self.destinationTime, timeZone.name, components.day);
return components.day;
And this code returns 0 and logs above log...
I believe you're asking NSCalendar the wrong question. You want to know if the arrival date is different than the departure date, but you're asking for the number of days between arrival and departure. A date change could happen even if the difference in time is only a few minutes. "A few minutes" rounds to "0 days" if you ask how many days have passed.
You actually want to know if the date has changed. I think I would be doing something like getting the day of the month for each date and comparing that. Since no months have 1 day, I would think that would work.
With Craigs input I came up with this solution:
- (NSInteger)calendarDaysBetweenDepartureAndArrivalTimeForTimeZone:(NSTimeZone *)timeZone
{
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timeZone];
NSDateComponents *departureComponents = [calendar components:(NSCalendarUnitDay) fromDate:self.departureTime];
NSDateComponents *destinationComponents = [calendar components:(NSCalendarUnitDay) fromDate:self.destinationTime];
NSInteger difference = destinationComponents.day - departureComponents.day;
if(difference < 0){
//Month overlapping
NSRange range = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.departureTime];
difference = range.length - departureComponents.day + departureComponents.day;
}
NSLog(#"CheckForPictures departure date: %# destinationDate: %# in timeZone:%# and get a day difference: %ld", self.departureTime, self.destinationTime, timeZone.name, difference);
return difference;
}

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.

NSDate and the next day

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)

NSDateComponents returns week 53 on 1/1/1970

So when extracting the dates components from NSDate object using NSCalendar and NSDateComponents I encountered a weird behavior.
If the date is 0 sec from 1970 the week component will return 53.
Is there an explination for this or a way to fix other than the obvious way of modulus 52?
here is the code you can run on your machine to test:
-
(void)testDate {
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:0];
NSDateComponents *comp = [cal components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSWeekCalendarUnit) fromDate:date];
DLog(#"%d/%d/%d week: %d", [comp day],[comp month], [comp year], [comp week]);
}
and here is the output:
31/12/1969 week: 53
(gdb) po date
1970-01-01 00:00:00 +0000
(gdb)
Well I got this,
2011-06-22 22:38:50.516 SmallTasks[23164:903] 1/1/1970 week: 1
So I am bit surprised by the result you got but I am not that surprised that a week: 53 turned up as 52 * 7 = 364 and we've 365 days in a year. For that to happen I would expect the week to start on Sunday on 1969 but it didn't.
You my friend have discovered the leap week. I have run across this doing some SQL reporting a couple years back.