Difference between NSDate and NSDateComponent value for the same date - objective-c

I created a simple function to get first and last day of a week for a day in it.
Looking at the NSLog output i found that different values are returned from a NSDate descriptor and component day for the same date, why ?
Here NSLog outputs:
NSDATE: 2011-04-03 22:00:00 +0000, DAY COMPONENT: 4
NSDATE: 2011-04-09 22:00:00 +0000, DAY COMPONENT: 10
As you can see, NSDATE is 3 of April and day component is 4 for the first row, and respectively 9 and 10 for the second one.
Here the code:
NSDate *date = [NSDate date]; //Today is April 5th 2011
NSCalendar *cal =[[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
[cal setFirstWeekday:2]; //My week starts from Monday
//DEFINE BEGINNING OF THE WEEK
NSDate *beginningOfWeek = nil;
[cal rangeOfUnit:NSWeekCalendarUnit startDate:&beginningOfWeek interval:nil forDate:date];
NSDateComponents *beginComponents = [cal components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSWeekdayCalendarUnit) fromDate:beginningOfWeek];
//DEFINE END OF THE WEEK, WITH 6 days OFFSET FROM BEGINNING
NSDateComponents *offset = [[NSDateComponents alloc]init];
[offset setDay:6];
NSDate *endOfWeek = [cal dateByAddingComponents:offset toDate:beginningOfWeek options:0];
NSDateComponents *endComponents = [cal components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSWeekdayCalendarUnit) fromDate:endOfWeek];
NSLog(#"NSDATE: %#, DAY COMPONENT: %d",beginningOfWeek, [beginComponents day]);
NSLog(#"NSDATE: %#, DAY COMPONENT: %d",endOfWeek, [endComponents day]);

Your dates are being printed with a timezone of +0000 (UTC), while your NSCalendar instance (and therefore your NSDateComponents) is using your device's default timezone (which I would guess is UTC+2).

Related

Get first day of week and last day in objective-c

I want to get the fist day of current week with a specific locale for everyone.
For example in US week starts with Sunday and other countries on Monday.
I want to start on monday for everyone, this is because i want to use this for a SQLQuery.
I have this:
NSDate *weekDay = [NSDate date]; //any date in the week in which to calculate the first or last weekday
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:weekDay];
[components setDay:([components day]-([components weekday]-[[NSCalendar currentCalendar] firstWeekday]))];
NSDate *firstWeekday = [gregorian dateFromComponents:components];
NSDate *lastWeekday = [[gregorian dateFromComponents:components] dateByAddingTimeInterval:7 * 24 * 3600 - 1];
NSLog(#"first - %# \nlast - %#", firstWeekday, lastWeekday);
Which works fine if in your locale week starts with Monday but if starts with Sunday doesn't return what i want.
So imagine today is 11 October 2015
With Sunday locale will return first day of the week 11, last day, 17
With Monday locale will return first day of the week 5, last day 11
I want to return the second option wherever my app is executed.
Thanks.
Best regards.
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
cal.firstWeekday = 2;// set first week day to Monday
// 1: Sunday, 2: Monday, ..., 7:Saturday
NSDate *now = [NSDate date];
NSDate *startOfTheWeek;
NSDate *endOfWeek;
NSTimeInterval interval;
[cal rangeOfUnit:NSCalendarUnitWeekOfYear
startDate:&startOfTheWeek
interval:&interval
forDate:now];
//startOfTheWeek holds the beginning of the week
endOfWeek = [startOfTheWeek dateByAddingTimeInterval:interval - 1];
// endOfWeek now holds the last second of the last week day
[cal rangeOfUnit:NSCalendarUnitDay
startDate:&endOfWeek
interval:NULL
forDate:endOfWeek];
// endOfWeek now holds the beginning of the last week day
testing:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
NSLog(#"start: %#", [formatter stringFromDate:startOfTheWeek]);
NSLog(#"end: %#", [formatter stringFromDate:endOfWeek]);
prints
start: 12.10.15, 00:00
end: 18.10.15, 00:00
So Monday is the beginning of the week
if I set
cal.firstWeekday = 1;
it will print
start: 11.10.15, 00:00
end: 17.10.15, 00:00
Sunday is the first day of the week

Why is constructed NSDate from components not same as components used as input?

I am trying to set a NSDate from components; the hour is the only thing that I need to actually set, the remainder is from current date. This is my code:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
// put in date with start time
int month = [components month];
int day = [components day];
int year = [components year];
[components setHour: startTime];
[components setMinute: 0];
[components setSecond: 0];
NSDate *startDate = [gregorian dateFromComponents: components];
This is the result from the console:
Printing description of components:
Calendar Year: 2015
Month: 10
Leap month: no
Day: 3
Hour: 1730
Minute: 0
Second: 0
Printing description of startDate:
2015-10-25 15:57:12 +0000
Notice that the month and time have changed from what was supplied. My question is why did it change (for future reference) and what do I do to fix this?

NSDateComponents keep changing the GMT?

This might be basic but i couldn't find a similar problem to clearly understand that.
I have some date which is this :
2015-07-19 12:00:00 +0000
then i am taking this date and try to get the hour of it with :
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *components = [gregorianCalendar components:NSCalendarUnitDay | NSCalendarUnitHour |NSCalendarUnitMinute | NSCalendarUnitMonth | NSCalendarUnitYear| NSCalendarUnitWeekday fromDate:tdate];
long hour=[components hour];
NSLog(#"-- %ld ",hour); //gives 15
Then i get the hour as 15 and not 12 as the original date .
The 3 hours different is i guess my GMT +3 .
How can i get 12 - the original date hour ?
adding this
[components setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"GMT"]];
didn't helped.
When i create that original date with components class, i do it in the exact way that i later do it again, so there shouldn't be any change in the GMT times.
To create the original date i do :
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay |NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitWeekday | NSCalendarUnitHour | NSCalendarUnitMinute fromDate:new];
[components setHour:12];
So, why when you extract it in the exact same way you dont get the same result ?
thanks .
When Using NSCalender and NSDateCompoment it will always translate date to your systemDate. If you want to have that Date invariant and meanwhile use DateCompoment to access components you need to remove the timeInterval between your systemeData to that date system(GMT in your case).
NSTimeZone* currentTimeZone = [NSTimeZone localTimeZone];
NSTimeInterval timeDiff = [currentTimeZone secondsFromGMTForDate:aGMTDate];
NSDate *gmtDate = [[NSDate alloc] initWithTimeInterval:(-timeDiff) sinceDate:[NSDate date]];

NSDate calculations summertime

I have a timestamp (NSDate) and I want to validate if another timestamp happened in the same calendar week, month, year, day, etc.
I tried to do this by defining 2 other NSDates, one as start date and one as end date.
And then representing the desired timespan with these two dates.
Example for defining the start of the current day:
NSDate *myDate = [[NSDate alloc] init];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:(
NSEraCalendarUnit |
NSMonthCalendarUnit |
NSYearCalendarUnit |
NSDayCalendarUnit |
NSHourCalendarUnit |
NSMinuteCalendarUnit |
NSSecondCalendarUnit |
NSTimeZoneCalendarUnit
) fromDate:myDate];
[components setHour:1];
[components setMinute:0];
[components setSecond:0];
startDate = [cal dateFromComponents:components];
This should set startDate to the time 00:00:00 on the current date.
The problem is that this only works if myDate has current timezone. E.g. if we have winter time now, and NSDate is summer time it sets the time 1 hour wrong.
How can I consider the summer/winter time in this calculation, or is there a better way to represent a concrete timespan a calendar based timespan a timestamp lies in?
The easiest way to get a day/week/month/... timespan for a given date is the
rangeOfUnit method. For example:
NSDate *date1, *date2;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startOfTimespan;
NSTimeInterval lengthOfTimespan;
[calendar rangeOfUnit:NSWeekOfYearCalendarUnit
startDate:&startOfTimespan
interval:&lengthOfTimespan
forDate:date1];
Now startOfTimespan is the start of the week that contains date1,
and lengthOfTimespan is the length of that week. So you can test date2 with
NSTimeInterval diff = [date2 timeIntervalSinceDate:startOfTimespan];
if ( diff >= 0 && diff < lengthOfTimespan) {
// date1 and date2 are in the same week
}

Producing an NSDate with fixed time

I am trying to produce an NSDate with fixed hour and minutes. I need this to make an equal comparison with other date stored in CoreData.
So far I wrote this code:
NSDate date = [NSDate date];
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:flags fromDate:date];
NSDate* newDate = [calendar dateFromComponents:components];
however with a breakpoint in xcode4 I can see the values:
Printing description of date:
2012-01-10 11:20:47 +0000
Printing description of newDate:
2012-01-09 23:00:00 +0000
Why newDate is one day back in respect of date ?
/* EDIT */
I also have tried to set manually all the components, but calendar dateFromComponents always give back same one hour back date, seems ignoring the components.
components.hour=0;
components.minute=0;
components.second=0;
components.timeZone=[NSTimeZone localTimeZone];
This is the description of component after being set:
<NSDateComponents: 0x7464de0>
TimeZone: Europe/Rome (CET) offset 3600
Calendar Year: 2012
Month: 1
Day: 10
Hour: 0
Minute: 0
Second: 0
which is exactly what I would like to have, but the calculated date with this component is still
Printing description of newDate:
2012-01-09 23:00:00 +0000
I wonder why I cannot get a precise NSDate even with specifying all the components in an NSDateComponents. Just because NSCalendar is ignoring my requirements, what's the meaning of components ?
What am I doing wrong ?
I guess you are +01:00 time zone. Actually the NSDate always gives values in GMT. So if it is Jan 10th, 00:00, then at the same time GMT time is Jan 9th, 23:00.
Even, while printing the following line
2012-01-10 11:20:47 +0000,
it should have printed 1 hour less than your current time. Please check.
Use this....
NSDate *date = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setLocale:[NSLocale currentLocale]];
NSDateComponents *nowComponents = [gregorian components:NSYearCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:date];
NSDate* newDate = [gregorian dateFromComponents:nowComponents];
NSLog(#"%#\n%#",date,newDate);
You may have problems with time zones, try setting a time zone for the calendar.