NSCalendar first day of week
Asked Answered
D

17

44

Does anyone know if there is a way to set the first day of the week on a NSCalendar, or is there a calendar that already has Monday as the first day of the week, instead of Sunday. I'm currently working on an app that is based around a week's worth of work, and it needs to start on Monday, not Sunday. I can most likely do some work to work around this, but there will be a lot of corner cases. I'd prefer the platform do it for me.

Thanks in advance

Here's some the code that I'm using. it's saturday now, so what I would hope is that weekday would be 6, instead of 7. that would mean that Sunday would be 7 instead of rolling over to 0

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setFirstWeekday:0];
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit | NSWeekCalendarUnit | NSWeekdayCalendarUnit;
NSDateComponents *todaysDate = [gregorian components:unitFlags fromDate:[NSDate date]];
int dayOfWeek = todaysDate.weekday;
Dapsang answered 9/7, 2009 at 23:21 Comment(2)
Well did you find any solutions yet? I am struck with the same!Pure
The ISO 8601 calendar appears to have it's first weekday set to monday by default.Landwehr
S
78

Edit: This does not check the edge case where the beginning of the week starts in the prior month. Some updated code to cover this: https://mcmap.net/q/190076/-nscalendar-first-day-of-week


In case anyone is still paying attention to this, you need to use

ordinalityOfUnit:inUnit:forDate:

and set firstWeekday to 2. (1 == Sunday and 7 == Saturday)

Here's the code:

NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[gregorian setFirstWeekday:2]; // Sunday == 1, Saturday == 7
NSUInteger adjustedWeekdayOrdinal = [gregorian ordinalityOfUnit:NSWeekdayCalendarUnit inUnit:NSWeekCalendarUnit forDate:[NSDate date]];
NSLog(@"Adjusted weekday ordinal: %d", adjustedWeekdayOrdinal);

Remember, the ordinals for weekdays start at 1 for the first day of the week, not zero.

Documentation link.

Snailfish answered 11/2, 2011 at 22:31 Comment(4)
Great answer, this is the real deal!Ribald
Ditto that. Great answer, this is the way to go!Tuneberg
@Dapsang this is the correct answer, please verify and check off as answered.Cyanohydrin
For some reason, when doing: [gregorian components:NSYearForWeekOfYearCalendarUnit |NSYearCalendarUnit | NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:date]; where date is a day that lands on Sunday, comps.weekday returns 1 instead of 7... Any clues?Detail
B
18

This code constructs a date that is set to Monday of the current week:

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDate *today = [NSDate date];
NSDate *beginningOfWeek = nil;
BOOL ok = [gregorian rangeOfUnit:NSWeekCalendarUnit startDate:&beginningOfWeek
                                interval:NULL forDate: today];
Boole answered 2/12, 2009 at 20:16 Comment(3)
This one (with mmc's answer taken into account) actually works.Forespent
Would have been nice if Mr. Joshua had put the answer check here, for it does exactly what the question describes.Bathe
-1 because this actually sets beginningOfWeek to Sunday 12am.Bogan
C
12

setFirstWeekday: on the NSCalendar object. Sets the index of the first weekday for the receiver.

- (void)setFirstWeekday:(NSUInteger)weekday

Should do the trick.

Clovis answered 9/7, 2009 at 23:29 Comment(2)
I tried setting this value to various values, and it has no effect on the weekday property of the NSCalendary. I've added some code to my original post so you can see what I'm doing. thanksDapsang
sorry, I meant the weekday property on NSDateComponentsDapsang
B
11

In my opinion this settings should be dynamic according to the user locale. Therefore one should use:

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setLocale:[NSLocale currentLocale]];

This will cause the calendar to set the first week day according to the user locale automatically. Unless you are developing your app for a specific purpose/user locale (or prefer to allow the user to choose this day).

Barry answered 6/9, 2010 at 6:3 Comment(2)
+1 - This is really a matter of personal preference - although most of the world uses Monday as the start of the week (and it is in fact the ISO standard), coming from the US, I'm used to a Sunday start. Why not use this elegant answer and fall back on the user's own calendar settings? This solution worked very well for my app.Gertiegertrud
-1 - The question is explicitly asking for a way to change the start day to monday and the scope of stackoverflow encompasses mainly on code solutions, not best app practices. At any rate, this should be a comment on the main thread, not an 'answer' post.Cyanohydrin
O
9

I've done it like this.

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *today = [NSDate date];
NSDateComponents *compForWeekday = [gregorian components:(NSWeekdayCalendarUnit) fromDate:today];
NSInteger weekDayAsNumber = [compForWeekday weekday]; // The week day as number but with sunday starting as 1

weekDayAsNumber = ((weekDayAsNumber + 5) % 7) + 1; // Transforming so that monday = 1 and sunday = 7
Outcrop answered 8/4, 2013 at 11:10 Comment(0)
M
8

I had trouble with a lot of the answers here. . maybe it was just me. .

Here's an answer that works for me:

- (NSDate*)firstDayOfWeek
{
    NSCalendar* cal = [[NSCalendar currentCalendar] copy];
    [cal setFirstWeekday:2]; //Override locale to make week start on Monday
    NSDate* startOfTheWeek;
    NSTimeInterval interval;
    [cal rangeOfUnit:NSWeekCalendarUnit startDate:&startOfTheWeek interval:&interval forDate:self];
    return startOfTheWeek;
}

- (NSDate*)lastDayOfWeek
{
    NSCalendar* cal = [[NSCalendar currentCalendar] copy];
    [cal setFirstWeekday:2]; //Override locale to make week start on Monday
    NSDate* startOfTheWeek;
    NSTimeInterval interval;
    [cal rangeOfUnit:NSWeekCalendarUnit startDate:&startOfTheWeek interval:&interval forDate:self];
    return [startOfTheWeek dateByAddingTimeInterval:interval - 1];
}

Update:

As pointed out (elsewhere) by @vikingosegundo, in general its best to let the local determine which day is the start of the week, however in this case the OP was asking for the start of the week to occur on Monday, hence we copy the system calendar, and override the firstWeekDay.

Manipular answered 23/6, 2013 at 10:20 Comment(5)
-1 Adding multiples 86,400 to a date is never the right answer.Trinitrotoluene
Asked for clarification via a new question here: #19827452Manipular
to be pedantic: lastDayOfTheWeek will return the last second of the week.Fluorine
renaming is a smart solutionFluorine
actually your answer is quite similar to this answer: https://mcmap.net/q/190076/-nscalendar-first-day-of-week including the same flaw that OP explicitly needs the start of the week set to Monday. this isn't covered in your answer. just say, that before calling the method you expect the calendar to be altered like [[NSCalendar currentCalendar] setFirstWeekday: 2];// Sunday == 1, Saturday == 7Fluorine
N
7

The problem with Kris' answer is the edge case where the beginning of the week starts in the prior month. Here's some easier code and it also checks the edge case:

// Finds the date for the first day of the week
- (NSDate *)getFirstDayOfTheWeekFromDate:(NSDate *)givenDate
{
    NSCalendar *calendar = [NSCalendar currentCalendar];

    // Edge case where beginning of week starts in the prior month
    NSDateComponents *edgeCase = [[NSDateComponents alloc] init];
    [edgeCase setMonth:2];
    [edgeCase setDay:1];
    [edgeCase setYear:2013];
    NSDate *edgeCaseDate = [calendar dateFromComponents:edgeCase];

    NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:edgeCaseDate];
    [components setWeekday:1]; // 1 == Sunday, 7 == Saturday
    [components setWeek:[components week]];

    NSLog(@"Edge case date is %@ and beginning of that week is %@", edgeCaseDate , [calendar dateFromComponents:components]);

    // Find Sunday for the given date
    components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:givenDate];
    [components setWeekday:1]; // 1 == Sunday, 7 == Saturday
    [components setWeek:[components week]];

    NSLog(@"Original date is %@ and beginning of week is %@", givenDate , [calendar dateFromComponents:components]);

    return [calendar dateFromComponents:components];
}
Nureyev answered 4/2, 2013 at 14:26 Comment(2)
This doesn't work for one cornercase. For date 2012-12-31 it returns: 2012-01-01Lattimer
I should clarify that I set weekday is to 2 (monday).Lattimer
I
3

I see misunderstanding in the other messages. The first weekday, whichever it is, has number 1 not 0. By default Sunday=1 as in the "Introduction to Date and Time Programming Guide for Cocoa: Calendrical Calculations":

"The weekday value for Sunday in the Gregorian calendar is 1"

For the Monday as a first workday the only remedy I have is brute force condition to fix the calculation

NSCalendar *cal=[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps = [cal components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
// set to 7 if it's Sunday otherwise decrease weekday number
NSInteger weekday=[comps weekday]==1?7:[comps weekday]-1; 
Inwardly answered 11/11, 2009 at 18:32 Comment(0)
B
1

Below also covers the edge case,

- (NSDate *)getFirstDayOfTheWeekFromDate:(NSDate *)givenDate
{
    NSCalendar *calendar = [NSCalendar currentCalendar];


   NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:givenDate];
    [components setWeekday:2]; // 1 == Sunday, 7 == Saturday
    if([[calendar dateFromComponents:components] compare: curDate] == NSOrderedDescending) // if start is later in time than end
    {
        [components setWeek:[components week]-1];
    }

    return [calendar dateFromComponents:components];
}
Brent answered 19/1, 2014 at 19:1 Comment(0)
W
1

You can just change .firstWeekday of the calendar.

NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
calendar.firstWeekday = 2;

Then use rangeOfUnit:startDate:interval:forDate: to get the first day

NSDate *startOfWeek;
[calendar rangeOfUnit:NSCalendarUnitWeekOfYear startDate:&startOfWeek interval:nil forDate:[NSdate date]];
Webworm answered 14/8, 2017 at 7:21 Comment(0)
A
0

Try this:

NSCalendar *yourCal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]
[yourCal setFirstWeekday:0];
Armistead answered 10/7, 2009 at 0:9 Comment(1)
It should be setFirstWeekday:2 for mondayLandwehr
M
0

Iv found out the way to display any weekday name using nscalender..using the following code.. Just open your console from xcode menu bar to see the results.Copy Paste the following code in your viewDidLoad method to get the first day of the week

NSDate *today = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"MM/dd/yyyy :EEEE"];
NSString *dateString = [dateFormat stringFromDate:today];
NSLog(@"date: %@", dateString);
[dateFormat release];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents *components = [gregorian components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
[components setDay:([components day]-([components weekday]-1))];

NSDate *beginningOfWeek = [gregorian dateFromComponents:components];
NSDateFormatter *dateFormat_first = [[NSDateFormatter alloc] init];
[dateFormat_first setDateFormat:@"MM/dd/yyyy :EEEE"];
NSString *dateString_first = [dateFormat_first stringFromDate:beginningOfWeek];
NSLog(@"First_date: %@", dateString_first);

The Output will be:

 date: 02/11/2010 :Thursday
 First_date: 02/07/2010 :Sunday

since i had run this program on 2/11/2010 u will get the desired output depending on the current date.

Similarly if u want to get the first working day of the week i.e Monday's date then just modify the code a bit:

CHANGE :[components setDay:([components day]-([components weekday]-1))];

TO [components setDay:([components day]-([components weekday]-2))];

to get Mondays date for that week..

Similarly u can try to find the date of any of seven workdays by changing the integer -1,-2 and so on...

Hope u r question is answered..

Thanks, Bonson Dias

Milford answered 11/2, 2010 at 7:29 Comment(1)
I thought about this solution but it doesn't work properly. For Sunday 2001-01-02 it will return Monday 2011-01-03 but it should return Monday 2010-12-27 instead.Forespent
L
0

The ISO 8601 calendar appears to have it's first weekday set to monday by default.

Landwehr answered 25/7, 2014 at 13:53 Comment(4)
doesn't seem to be true - the value for sunday is still 1Radiotelephone
Sunday obviously is 1. What I mean is that when instantiating a ISO 8601 calendar the firstWeekday should be set to monday aka 2 by default.Landwehr
Sorry, @Christian Schnorr, can you explain - how it is 2 - Monday? Sould it be 1 - Monday.Autohypnosis
These indices start at 1, not 0.Landwehr
M
0

Using the Calendar nextWeekend (iOS 10 or later) and ordinality (thanks @kris-markel). I've gotten Monday as first of the week for the en_US calendar.

Here is an example of it with fallback to firstWeekday:

extension Calendar {
    var firstWorkWeekday: Int {
        guard #available(iOS 10.0, *) else{
            return self.firstWeekday
        }
        guard let endOfWeekend = self.nextWeekend(startingAfter: Date())?.end else {
            return self.firstWeekday
        }
        return self.ordinality(of: .weekday, in: .weekOfYear, for: endOfWeekend) ?? self.firstWeekday
    }
}
Manley answered 25/7, 2017 at 21:11 Comment(0)
E
0

The Swift solution (note, use .yearForWeekOfYear, not .year):

let now = Date()
let cal = Calendar.current
var weekComponents = cal.dateComponents([.yearForWeekOfYear, .weekOfYear,
                                         .weekday], from: now)
//weekComponents.weekday = 1  // if your week starts on Sunday
weekComponents.weekday = 2  // if your week starts on Monday
cal.date(from: weekComponents) // returns date with first day of the week
Election answered 10/8, 2019 at 12:0 Comment(2)
It can be even simpler. Just leave out the .weekday component and you get the start date of the current week by ISO 8601 (the Monday).Rhizotomy
Note, also there is a bug in the API (macOS 10.14.6) converning weekOfYear if you use weekday 1 (2 or nil or okay), you have to substract 1 from the weekOfYear. Otherwise you get the following week start, when creating the date.Rhizotomy
R
-1

… is there a calendar that already has Monday as the first day of the week, instead of Sunday.

Someday, there will be.

Rhiannonrhianon answered 12/10, 2009 at 10:22 Comment(0)
T
-1

My simple way of doing this is to get Monday = 0, Sunday = 6:

    NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
    NSInteger dayNumStartingFromMonday = ([dateComponents weekday] - 2 + 7) % 7;    //normal: Sunday is 1, Monday is 2
Trinity answered 5/9, 2013 at 9:26 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.