NSDate of the week - ios

I an using the current methods to get the first and the last day of the current week:
NSDate *weekDate = [NSDate date];
NSCalendar *myCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *currentComps = [myCalendar components:( NSYearCalendarUnit | NSMonthCalendarUnit | NSWeekOfYearCalendarUnit | NSWeekdayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate:weekDate];
[currentComps setWeekday:1]; // 1: sunday
NSDate *firstDayOfTheWeek = [myCalendar dateFromComponents:currentComps];
[currentComps setWeekday:7]; // 7: saturday
NSDate *lastDayOfTheWeek = [myCalendar dateFromComponents:currentComps];
This was working perfect, but now in ios 4.3 and it's not working.
Any idea what can be the problem?

I have started Xcode 4.1 on my OS X 10.6 partition and tried to compile your code against the iOS 4.3 SDK. It turned out that NSWeekOfYearCalendarUnit is undefined, so that must have been introduced in later iOS versions. This might explain why it does not work on iOS 4.3.
The following alternative code works and gives the correct result:
NSDate *now = [NSDate date];
NSCalendar *myCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *firstDayOfTheWeek;
NSTimeInterval length;
[myCalendar rangeOfUnit:NSWeekCalendarUnit
startDate:&firstDayOfTheWeek
interval:&length
forDate:now];
NSDate *lastDayOfTheWeek = [firstDayOfTheWeek dateByAddingTimeInterval:length];
Update: The above code gives the (start of) the first day in the week and the (start of) the next week. If you add (length - 1) instead of length then you will get the end of the last day in the week (thanks #rmaddy!). Alternatively, you can add 6 days to the first day:
NSDate *now = [NSDate date];
NSDate *firstDayOfTheWeek;
NSCalendar *myCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[myCalendar rangeOfUnit:NSWeekCalendarUnit
startDate:&firstDayOfTheWeek
interval:NULL
forDate:now];
NSDateComponents *sixDays = [[NSDateComponents alloc] init];
[sixDays setDay:6];
NSDate *lastDayOfTheWeek = [myCalendar dateByAddingComponents:sixDays toDate:firstDayOfTheWeek options:0];
Remark: This code also handles the "start of week" correctly according to the locale.

Your third component, NSWeekOfYearCalendarUnit, is only available as of iOS 5. See https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSCalendar_Class/Reference/NSCalendar.html. I believe for what you are doing, you don't need that component, you should be able to just remove it.
As for the localization comments from the others on this post, they are correct that things change in different locales, but not the way you might expect. Sunday is indeed always day 1 (and Saturday day 7), but in cultures where Monday is the first day of the week, your code will return the following Saturday and the following Sunday, seemingly at the end of the week. You can fix this by explicitly setting your calendar to use the en-US locale, or attempt to account for other locales by using the firstDayOfWeek property. You can test this by switching the Region Format of you simulator to German > Germany, in Settings > General > International > Region Format.

It's unclear in which sense you are having difficulties, but it might be simply that there's no consensus on which is the first day of the week.
[old example redacted; Mike below correctly pointed out that it was invalid]
Per the NSDateComponents documentation, "For example, in the Gregorian calendar, n is 7 and Sunday is represented by 1."; if your device is using any of the nine other calendars supported by iOS — such as the Buddhist, Chinese or Hebrew calendars — then Apple doesn't guarantee that Sunday is day 1.

Related

NSDate counting is not right. How to add a week without doing the math? [duplicate]

I have in the past used the below function to add on a specific time interval using NSDateComponents to an existing date.
(NSDate *)dateByAddingComponents:(NSDateComponents *)comps
toDate:(NSDate *)date
options:(NSCalendarOptions)opts
From iOS8 the week value is deprecated for NSDateComponents, which means I can't achieve what I want to do: generate a new NSDate by adding a certain number of weeks to a given NSDate.
Any help would be greatly appreciated.
Just use weekOfYear:
Apple docs for NSDateComponents week:
Deprecation Statement
Use weekOfYear or weekOfMonth instead, depending on what you intend.
NSDate *date = [NSDate date];
NSDateComponents *comp = [NSDateComponents new];
comp.weekOfYear = 3;
NSDate *date1 = [[NSCalendar currentCalendar] dateByAddingComponents:comp toDate:date options:0];
NSLog(#"date: %#", date);
NSLog(#"date1: %#", date1);
Output:
date: 2015-01-13 04:06:26 +0000
date1: 2015-02-03 04:06:26 +0000
If you use week you get this warning:
'week' is deprecated: first deprecated in ... - Use weekOfMonth or weekOfYear, depending on which you mean
When using the weekOfMonth or weekOfYear as a delta they work the same. Where they are different is when they are used to obtain the week number where you will get the week of the month with a range of 6 or the week of the year with a range of 53.
Update: As Zaph said in his answer, Apple actually recommends using weekOfYear or weekOfMonth instead of the answer I provided. View Zaph's answer for details.
You'll probably quickly realize that you're overthinking it, but here's how you can add a certain number of weeks to a date even though the week value's been deprecated, ex:
NSDateComponents *comp = [NSDateComponents new];
int numberOfDaysInAWeek = 7;
int weeks = 3; // <-- this example adds 3 weeks
comp.day = weeks * numberOfDaysInAWeek;
NSDate *date = [[NSCalendar currentCalendar] dateByAddingComponents:comp toDate:date options:0];
I prefer to use dateByAddingUnit. It's more intuitive
return [NSDate[[NSCalendar currentCalendar] dateByAddingUnit:NSCalendarUnitMonth value:3 toDate:toDate options:0];
You can add a category on NSDate with the following method:
- (NSDate *) addWeeks:(NSInteger)weeks
{
NSCalendar *gregorian=[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *components=[[NSDateComponents alloc] init];
components.day = weeks * 7;
return [gregorian dateByAddingComponents:components toDate:self options:0];
}

Finding the locale-dependent first day of the week

Given an NSDate, how do I find the first day of that date's week, given the user's locale. For example, I've heard that some countries treat Monday as the first day of the week and others use Sunday. I need to return the preceding Monday in the first case but the preceding Sunday in the latter case.
My best effort thus far always returns the preceding Sunday, regardless of the device settings applied:
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setLocale:[NSLocale currentLocale]];
NSDateComponents *components = [calendar components:NSYearForWeekOfYearCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSWeekCalendarUnit | NSWeekdayCalendarUnit fromDate:originalDate];
[components setWeekday:1];
NSDate *firstDayOfWeek = [calendar dateFromComponents:components];
Bonus question: on iOS, which setting drives this? Is it the 'Region Format'?
Try changing:
[components setWeekday:1];
to:
[components setWeekday:[calendar firstWeekday]];
You should also remove the NSYearForWeekOfYearCalendarUnit and NSWeekCalendarUnit components.
Bonus Question: "Region Format" should be the setting that changes the first day of the week.
An smarter way for old style for those who do not want to set calendar firstWeekday.
NSDate *date = [NSDate dateWithTimeIntervalSince1970:1483620311.228];
NSLog(#"current date ===> : %#", date);
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *previousMonday = [calendar nextDateAfterDate:date
matchingUnit:NSCalendarUnitWeekday
value:2 //use 1-7 for Sunday to Saturday week day.
options:NSCalendarMatchNextTime | NSCalendarSearchBackwards];
NSLog(#"previousMonday date ===> : %#", previousMonday);

Numeric value: day of the week

Im changing text value based on the day of the week, I was able to achieve this using string values, however I want to be able to achieve on a numeric value instead - to remove issues with different languages. For example, if today is monday do... but I want if today is day 1 then do. Ive tried the code below but it gives me a numeric value of 0;
NSDateFormatter *dayofweekformatter = [[NSDateFormatter alloc] init];
[dayofweekformatter setDateFormat:#"E"];
NSString *DayOfWeek = [dayofweekformatter stringFromDate:[NSDate date]];
NSInteger weekDay = [DayOfWeek integerValue];
NSLog(#"The day of the week is: %d", weekDay);
Is it possible to do this?
According to this link, "E" on its own will give you the day of the week as a textual format, i.e.: "Mon"/"Tue" etc.
If you want the day of the week as an integer, you should use a lower case "e" or "c".
A different implementation would be to use NSCalendar and NSDateComponents to determine the day of the week as this is more likely to take into consideration different settings across different devices based on the users preferred calendar.
NSDate *date = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *dateComponents = [calendar components:NSCalendarUnitWeekday fromDate:date];
NSLog(#"day of the week: %i", [dateComponents weekday]);
Here is solution proposed on Apple forums https://discussions.apple.com/thread/1700102?start=0&tstart=0 (I am not the author)
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents =[gregorian components:NSWeekdayCalendarUnit fromDate:dateOfInterest];
NSInteger weekday = [weekdayComponents weekday];
// weekday 1 = Sunday for Gregorian calendar
[gregorian release];

Get an Accurate Time Difference Between two NSDate?

Is there any way to find out an accurate difference between two NSDate?
I have found solutions but they aren't accurate enough. I need to take into account daylight saving, the fact that different months have a different number of days, etc.
A simple calculation such as /60/60/24 etc. to work out minutes, hours and days doesn't take them into account.
Lets say I need to work out the difference between the time right now ([NSDate date]) and December 25th 10:22PM (date chosen by user using date picker [datePicker date]) just as an example, how would I do this?
Knowing the exact time difference isn't the key, so long as I have an accurate difference of days, months and years, it will do.
From Apple's Date & Time Programming Guide:
Listing 12 Getting the difference between two dates
NSDate *startDate = ...;
NSDate *endDate = ...;
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents *components = [gregorian components:unitFlags
fromDate:startDate
toDate:endDate options:0];
NSInteger months = [components month];
NSInteger days = [components day];
NSDate is completely independent of Timezone. Daylight saving doesn't even come into the picture for NSDate. Only when you convert NSDate into a human readable format (MM/DD/YY, HH:MM:SS format or the like), does Time Zone come into picture.
Make sure that you take into account correct timezone, day-light saving setting when you create NSDate(s). Subsequently, the method, [date1 timeIntervalSinceDate:date2] should always give you accurate time difference.
So, the more accurate question you meant to ask was: How can I get a nicely formatted days, months, years from a difference between two dates. First you want to get the nsTimerInterval (time difference in seconds) and then format it:
How do I break down an NSTimeInterval into year, months, days, hours, minutes and seconds on iPhone?
Small update on the code for latest iOS:
NSDate *startDate = ...;
NSDate *endDate = ...;
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSUInteger unitFlags = NSCalendarUnitMonth | NSCalendarUnitDay;
NSDateComponents *components = [gregorian components:unitFlags
fromDate: startDate
toDate:endDate options:0];
NSInteger months = [components month];
NSInteger days = [components day];

Wrong week from date component on IOS5

My region format is US, and I'm getting different week numbers from the ones I get on Mac OSx which is also in US.
I'm using iOS 5 and have not programmed on iOS before this sdk version. So I just want to make sure I'm not doing something wrong. Whit this code below I'm getting for the last week of the year the number 53 which I think is wrong. My first day of the week is set to sunday.
Please advice, thank you.
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDate *date = [NSDate date];
NSDateComponents *dateComponents = [calendar components:( NSWeekCalendarUnit) fromDate:date ];
int week = (int)[dateComponents week];
NSLog(#"%i", week);
Being [NSDate date] december 8, it returns week number 50, but on Mac OSX I get week 49
First off, I see you changed your code to get rid of the 'gregorianCalendar' nonsense. Good.
When I tried your code in a test app:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *dateComponents = [calendar components:( NSWeekCalendarUnit) fromDate:[NSDate date]];
int week = (int)[dateComponents week];
[calendar release];
NSLog( #"week is %d", week);
return week;
I'm getting 49 (which makes sense to me).
If you're getting 53, then you're passing in the wrong date.
Did you check the locale? There are cases where the week numbers differ between the US and Europe for example.

Resources