How to get the order of each day in a week? - ios

I wonder if there is a way to know how each day of a week ordered? I know in the Gregorian calendar, property 'weekday' indicates 1-7, and
Sunday is represented by 1. But when I change the region(setting->General->Language &Region->Region) to Russia, the Apple's calendar app displays 'M'as the first day of a week, while United States displays 'S' as the first day of a week.
How can I work out this programmatically? Any idea, thanks advance.

Try this
[[NSCalendar currentCalendar] firstWeekday]

you can set programmatically start day of week by following code.
To get first day of week after setting
- (NSDate*)firstDayOfWeek
{
NSCalendar* calender = [[NSCalendar currentCalendar] copy];
[calender setFirstWeekday:2]; //Override locale to make week start on Monday
NSDate* startOfTheWeek;
NSTimeInterval interval;
[calender rangeOfUnit:NSWeekCalendarUnit startDate:&startOfTheWeek interval:&interval forDate:self];
return startOfTheWeek;
}
To set First day of week
[[NSCalendar currentCalendar] setFirstWeekday:2]; //Override locale to make week start on Monday

NSCalendar has a property named firstWeekday which indicates the index of the first weekday of the receiver.
NSCalendar documentation

Related

Objective C especial Epoch format to Date [duplicate]

The common question on stackoverflow is how to get the Day of Year from a date but how to you get the date from the Day of Year?
I'm using the following code to generate the Day of Year but how to I do the converse?
// Calculate Day of the Year
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger dayOfYear = [gregorian ordinalityOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:[NSDate date]];
setDayOfYear = dayOfYear;
return setDayOfYear;
Assuming you know the year you want and the actual time of the date doesn't matter and you have the correct locale and time zones set up, you can just convert the day of the year and year from components back into a date. Again, you may need to decide what your application needs to do about the time component and you have to make sure that the day and year actually make sense for the calendar, but assuming the gregorian calendar you listed in your example, and the 200th day of 2013 (19 July, 2013), you could so something like this:
NSDateComponents* components = [[NSDateComponents alloc] init];
[components setDay:200];
[components setYear:2013];
NSDate* july19_2013 = [gregorian dateFromComponents:components];
If I were to run this, I'd get a date that's 19 July, 2013 # 07:00 since my current locale is in a time zone equiv of America/Los_Angeles and I didn't configure the calendar.

Another pair of eyes for my date picking strategy?

I'd like to group dates for a given week, with the starting day being what the user chooses in their preferences.
For example my preference may be that my work week starts on a Friday, and so Friday to Thursday should be my week.
Sunday-Saturday. Monday-Sunday, etc.
So I'd like to present the user with a date picker, and although they can pick any calendar day, I'd like to have the collection of
dates start with their starting day.
For example if I pick a random Wednesday next month, and my preferences are for a starting work week of Monday, then my collection of dates
should start at Monday. The code must be able to walk backwards 2 days, find that monday, then walk forwards until Sunday.
My strategy was to do just that - loop backwards until the start date is found and simply add 6 days to end on a Saturday.
Before I go about it this way, is there a better way (or maybe some sort of specifics in iOS that would make this easier for me than somewhere else)?
Examine the NSCalendar class. In particular is the method: setFirstWeekday:.
Example code:
NSDate *today = [NSDate date]; // Or a date of your choice
NSLog(#"today: %#", today);
NSCalendar *caledar = [NSCalendar currentCalendar];
[caledar setFirstWeekday:5]; // Sunday is 1
NSDate *beginningOfWeek;
[caledar rangeOfUnit:NSWeekCalendarUnit
startDate:&beginningOfWeek
interval:NULL
forDate:today];
NSLog(#"beginningOfWeek: %#", beginningOfWeek);
NSLog output:
today: 2014-07-22 14:45:01 +0000
beginningOfWeek: 2014-07-17 04:00:00 +0000
Have you looked at NSDateComponents? You can specify a specific day of the week.
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *pickedDateComponents = [calendar components:NSCalendarUnitWeekday fromDate:pickedDate];
NSInteger saturday = 7; // In the Gregorian calendar, 1 is Sunday, 2 is Monday, etc..
NSDateComponents *daysUntilSaturdayComponents = [NSDateComponents new];
daysUntilSaturdayComponents.weekday = saturday - pickedDateComponents.weekday;
NSDate *saturdayAfterPickedDate = [calendar dateByAddingComponents:daysUntilSaturdayComponents toDate:pickedDate options:0];

Objective-C monthly notification end of month

I have a notification system in my app that allows users to set multiple notifications and they can be:
Daily
Weekly
Monthly
The first 2 are ok, but on the third one I have a problem.
Lets say that we are on March and the user sets the notification to trigger on the 31st of the month. The notification is scheduled correctly, but if we where on April (30 days) for example the notification is scheduled on the 1st of May.
I have 2 questions:
How can I schedule notification on the last day of the month? Or set them up to handle this case gracefully in the following months.
If the notification on the 31st of March is scheduled correctly, will the next one be scheduled on the 30th of April and then on the 31st of May? My guess is no, will be 31 of march, 1st April and 31st of April.
Scheduling multiple notification is not an option for me because apple has a limit and that limit can be reached easily if the user has 6 monthly notifications (6x12) and they could have more than that.
Thanks,
Sergio
EDIT
Sorry I didn't explained myself properly.
I don't have the problem setting the notification on the correct day. But I have a problem with how the repetitions will work. If I set a notification to trigger on the 31st every month starting on the 31st of March (lets assume we are in March) the first one will come up on the right day, but what would happen on April?
Thanks again.
You can get the last day of the month of a specific date using the below methods
-(NSDate*)lastDayOfMonthOfDate:(NSDate *)date
{
NSInteger dayCount = [self numberOfDaysInMonthCountForDate:date];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone timeZoneWithName:#"GMT"]];
NSDateComponents *comp = [calendar components:
NSYearCalendarUnit |
NSMonthCalendarUnit |
NSDayCalendarUnit fromDate:date];
[comp setDay:dayCount];
return [calendar dateFromComponents:comp];
}
-(NSInteger)numberOfDaysInMonthCountForDate:(NSDate *)date
{
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone timeZoneWithName:TIMEZONE]];
NSRange dayRange = [calendar rangeOfUnit:NSDayCalendarUnit
inUnit:NSMonthCalendarUnit
forDate:date];
return dayRange.length;
}
Find the last day from these method and schedule notification for that day
Source : Getting the last day of a month
-[NSCalendar rangeOfUnit:inUnit:forDate:] will tell you the minimum and maximum values that a particular date component can take in a larger component, in the context of a particular date.
If you use NSDayCalendarUnit and NSMonthCalendarUnit, the length of the result will be the last day of the month containing the passed date.
For example:
NSCalendar * c = [NSCalendar currentCalendar];
NSRange aprilRanger = [c rangeOfUnit:NSDayCalendarUnit
inUnit:NSMonthCalendarUnit
forDate:[NSDate date]];
NSLog(#"%lu", aprilRanger.length);
NSDateComponents * minusTwoMonths = [NSDateComponents new];
[minusTwoMonths setMonth:-2];
NSDate * febDay = [c dateByAddingComponents:minusTwoMonths
toDate:[NSDate date]
options:0];
NSRange febRange = [c rangeOfUnit:NSDayCalendarUnit
inUnit:NSMonthCalendarUnit
forDate:febDay];
NSLog(#"%lu", febRange.length);
Produces:
30
28
And this will give you the correct answer when weird things like leap days happen, too.
What I had to do was:
Check if the current month has the day that the user requested (max 31).
If it doesnt then schedule a normal notification for the current month on the last day and then schedule the monthly one on the following month, the OS will handle the rest.
Note: If the current month doesn't have the requested day (31 of feb), the next one will definitely have it (31 of mar).
If it does then schedule the monthly on the current month.
Hey It's not a big deal.
In your notification object set repeatInterval to NSMonthCalendarUnit
UILocalNotification *notify = [ [UILocalNotification alloc] init ];
notify.fireDate = notificationDate;
notify.repeatInterval = NSMonthCalendarUnit;
// use NSDayCalendarUnit, NSWeekCalendarUnit for daily or weekly respectively.
Thanks
damithH

Get all NSDate of a month (and the dates in adjacent weeks)

Given a date (for example today 4 Feb 2014), I would like to get a list of NSDate instances that go from Monday 27 Jan 2014 to Sunday 2 Mar 2014.
Basically all the dates in the current month, plus the dates from the last week of the previous month, and the first week of the next month, if they share the same week with some of the dates in the current month.
How can I achieve this?
I can think of a way to obtain this (pseudo-code below), but it's way too long and complicated. Is there any simpler way, like a method that the SDK provides to short cut?
Extract the month component from [NSDate date] (today)
Construct the first day of the month
Calculate its weekday
If it's Wednesday (3rd day of the week) then add today-1, today-2 to the list and so on
Repeat from step 2, but with the last day of the month
Also to all the elitists out there, just because I'm not posting any code, doesn't mean this is not a coding question. The problem is real (construct a calendar grid of a month), and finding the right algorithm/method before coding is much better than playing around and manually do the maths with NSDate and NSCalendar (very error prone, as I will need to take into account all the weird cases). I figure many people have already encountered this same problem and if they could share some pointers, great. If you don't want to answer, no need to reply.
I'm going to post very general code. You are going to need to make it specific to your needs.
//Setup the calendar object
NSCalendar *calendar = [NSCalendar currentCalendar];
//Get the current date's month
NSUInteger month = [dateComponents month];
//Create an NSDate for the first and last day of the month
NSDateComponents *comp = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:[NSDate date]];
[comp setDay:1];
NSDate *firstOfMonth = [calendar dateFromComponents:comp];
[comp setMonth:[comp month]+1];
[comp setDay:0];
NSDate *lastOfMonth = [calendar dateFromComponents:comp];
//Now get the first and last week number from there
NSUInteger unitFlags = NSWeekCalendarUnit;
//Create a date component object from today's date.
NSDateComponents *firstDateComponents = [calendar components:unitFlags
fromDate:firstOfMonth // for current date
options:0];
NSDateComponents *lastDateComponents = [calendar components:unitFlags
fromDate:lastOfMonth // for current date
options:0];
NSUInteger firstWeek = [firstDateComponents week];
NSUInteger lastWeek = [lastDateComponents week];
Now that you have the first and last weeks you can start at the first day of the first week and go through the last day of the last week to set up your calendar. Good luck.
Swift 5:
extension Date {
var startOfMonth: Date {
let calendar = Calendar(identifier: .gregorian)
let components = calendar.dateComponents([.year, .month], from: self)
return calendar.date(from: components)!
}
var endOfMonth: Date {
var components = DateComponents()
components.month = 1
components.second = -1
return Calendar(identifier: .gregorian).date(byAdding: components, to: startOfMonth)!
}
}
let date = Date()
let firstWeek = Calendar.current.dateComponents([.weekOfYear], from: date.startOfMonth)
let lastWeek = Calendar.current.dateComponents([.weekOfYear], from: date.endOfMonth)

NSDate of the week

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.

Resources