I am setting up an NSPredicate for a fetch to Core Data which is set up like so:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(date >= %#) AND (date <= %#)", todaysDate, tomorrowsDate];
I need todaysDate to be today at 00:00:00, and I need tomorrowsDate to be today at 23:59:59.
I can't set todaysDate equal to [NSDate date] and then manipulate the hours, minutes, and seconds with NSDateComponents because [NSDate date] gives me a date which is 5 hours ahead of my local actual time (so if it's 11:00 pm here on May 6th, then [NSDate date] would give me "2014-05-07 04:00:00 +0000", but I still need it to think that it is May 6th, not 7th!).
How can I manipulate the tools I have in Xcode to consistently get my variable todaysDate to be today at midnight, and tomorrowsDate to be a second before midnight strikes tomorrow?
The rangeOfUnit:... method of NSCalendar is a convenient method to
compute the start of the current day and the start of tomorrow
in your local time zone:
NSDate *now = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *todaysDate;
NSDate *tomorrowsDate;
NSTimeInterval interval;
[cal rangeOfUnit:NSDayCalendarUnit startDate:&todaysDate interval:&interval forDate:now];
tomorrowsDate = [todaysDate dateByAddingTimeInterval:interval];
so that you can use it in the predicate with >= and <:
[NSPredicate predicateWithFormat:#"(date >= %#) AND (date < %#)", todaysDate, tomorrowsDate]
to fetch all objects of the current day.
Remark: Don't let the NSLog() output of NSDate objects confuse you.
NSDate represents an absolute point in time and knows nothing about time zones.
NSLog(#"%#", todaysDate) prints the date according to the GMT time zone and not in your local time zone.
To print the dates according to your time zone, use p todaysDate in the debugger console (instead of po),
or print
[todaysDate descriptionWithLocale:[NSLocale currentLocale]]
The following works for me:
- (NSDate *)dateWithDate:(NSDate *)date Hour:(NSInteger)hour Minute:(NSInteger)minute Second:(NSInteger)second {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
components.hour = hour;
components.minute = minute;
components.second = second;
return [calendar dateFromComponents:components];
}
Example:
NSDate *beginningOfDay = [self dateWithDate:[NSDate date] Hour:0 Minute:0 Second:0];
NSDate *endOfDay = [self dateWithDate:[NSDate date] Hour:23 Minute:59 Second:59];
Swift:
let cal = NSCalendar.currentCalendar()
//tip:NSCalendarUnit can be omitted, but with the presence of it, you can take advantage of Xcode's auto-completion
var comps = cal.components(NSCalendarUnit.YearCalendarUnit | .MonthCalendarUnit | .DayCalendarUnit | .HourCalendarUnit | .MinuteCalendarUnit | .SecondCalendarUnit, fromDate: NSDate())
comps.hour = 0
comps.minute = 0
comps.second = 0
let todaysDate = cal.dateFromComponents(comps)!
let tomorrowsDate = NSDate(timeInterval: 86399, sinceDate: todaysDate)
Objective-C:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *comps = [cal components: (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate: [NSDate date]];
[comps setHour:0];
[comps setMinute:0];
[comps setSecond:0];
NSDate *todaysDate = [cal dateFromComponents:comps];
NSDate *tomorrowsDate = [NSDate dateWithTimeInterval: 86399 sinceDate:todaysDate];
Related
What is the most efficient way to obtain an NSDate object that represents midnight of the current day?
New API in iOS 8
iOS 8 includes a new method on NSCalendar called startOfDayForDate, which is really easy to use:
let startOfToday = NSCalendar.currentCalendar().startOfDayForDate(NSDate())
Apple's description:
This API returns the first moment date of a given date.
Pass in [NSDate date], for example, if you want the start of "today".
If there were two midnights, it returns the first. If there was none, it returns the first moment that did exist.
Update, regarding time zones:
Since startOfDayForDate is a method on NSCalendar, it uses the NSCalendar's time zone. So if I wanted to see what time it was in New York, when today began in Los Angeles, I could do this:
let losAngelesCalendar = NSCalendar.currentCalendar().copy() as! NSCalendar
losAngelesCalendar.timeZone = NSTimeZone(name: "America/Los_Angeles")!
let dateTodayBeganInLosAngeles = losAngelesCalendar.startOfDayForDate(NSDate())
dateTodayBeganInLosAngeles.timeIntervalSince1970
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .MediumStyle
dateFormatter.timeStyle = .ShortStyle
dateFormatter.timeZone = NSTimeZone(name: "America/New_York")!
let timeInNewYorkWhenTodayBeganInLosAngeles = dateFormatter.stringFromDate(dateTodayBeganInLosAngeles)
print(timeInNewYorkWhenTodayBeganInLosAngeles) // prints "Jul 29, 2015, 3:00 AM"
Try this:
NSDate *const date = NSDate.date;
NSCalendar *const calendar = NSCalendar.currentCalendar;
NSCalendarUnit const preservedComponents = (NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay);
NSDateComponents *const components = [calendar components:preservedComponents fromDate:date];
NSDate *const normalizedDate = [calendar dateFromComponents:components];
NSCalendar *cal = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[cal setTimeZone:[NSTimeZone systemTimeZone]];
NSDateComponents * comp = [cal components:( NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate:[NSDate date]];
[comp setMinute:0];
[comp setHour:0];
[comp setSecond:0];
NSDate *startOfToday = [cal dateFromComponents:comp];
If you mean midnight as 23:59 then set component's hour as 23 and minutes as 59.
Swift:
let cal = NSCalendar.currentCalendar()
//tip:NSCalendarUnit can be omitted, but with the presence of it, you can take advantage of Xcode's auto-completion
var comps = cal.components(NSCalendarUnit.YearCalendarUnit | .MonthCalendarUnit | .DayCalendarUnit | .HourCalendarUnit | .MinuteCalendarUnit | .SecondCalendarUnit, fromDate: NSDate())
comps.hour = 0
comps.minute = 0
comps.second = 0
let midnightOfToday = cal.dateFromComponents(comps)!
Swift 2.2:
let cal = NSCalendar.currentCalendar()
let comps = cal.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: NSDate())
comps.hour = 0
comps.minute = 0
comps.second = 0
let midnightOfToday = cal.dateFromComponents(comps)!
Objective-C:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *comps = [cal components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:[NSDate date]];
[comps setHour:0];
[comps setMinute:0];
[comps setSecond:0];
NSDate *midnightOfToday = [cal dateFromComponents:comps];
You could use the following method to get the midnight value for an NSDate.
- (NSDate *)dateAtBeginningOfDayForDate:(NSDate *)inputDate
{
// Use the user's current calendar and time zone
NSCalendar *calendar = [NSCalendar currentCalendar];
NSTimeZone *timeZone = [NSTimeZone systemTimeZone];
[calendar setTimeZone:timeZone];
// Selectively convert the date components (year, month, day) of the input date
NSDateComponents *dateComps = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:inputDate];
// Set the time components manually
[dateComps setHour:0];
[dateComps setMinute:0];
[dateComps setSecond:0];
// Convert back
NSDate *beginningOfDay = [calendar dateFromComponents:dateComps];
return beginningOfDay;
}
Thkis is taken from here website.
From iOS8, you can use startDayForDate.
So, in order to get the start of today (Objective -C):
NSDate * midnight;
midnight = [[NSCalendar currentCalendar] startOfDayForDate: [NSDate date]];
try this:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [gregorian components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:now];
NSDateFormatter* df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"MM/dd/yyyy HH:mm:ss.SSS"];
NSLog(#"%#",[df stringFromDate:[gregorian dateFromComponents:components]]);
NSDate *now = [NSDate date];
NSDate *beginningOfToday = nil;
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&beginningOfToday interval:NULL forDate:now];
You could use NSCalendar's dateBySettingHour:minute:second:ofDate:options:
So it would be as easy as doing:
NSCalendar *calendar = [NSCalendar currentCalendar];
calendar.timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
NSDate *midnight = [calendar dateBySettingHour:0 minute:0 second:0 ofDate:[NSDate date] options:0];
let calendar = NSCalendar.currentCalendar()
let unitFlags: NSCalendarUnit = [.Year, .Month, .Day, .Minute, .Second]
let components = calendar.components(unitFlags , fromDate: NSDate())
components.hour = 0
components.minute = 0
components.second = 0
//Gives Midnight time of today
let midnightOfToday = calendar.dateFromComponents(components)!
I have start date and a duration (period). For example startDate = '2014-02-12' period = 2. I desired dates 2014-02-12, 2014-02-14, 2014-02-16, .... I need to determine the current date is flagged during.
To check if the difference between the start date and the current date is an even
number of days, use NSDateComponents:
NSDate *startDate = ...;
NSDate *currentDate = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *diff = [cal components:NSDayCalendarUnit fromDate:startDate
toDate:currentDate options:0];
NSInteger days = diff.day;
if (days % 2 == 0) {
// even number of days between start date and current date
}
You can get the desired dates using
- (id)dateByAddingTimeInterval:(NSTimeInterval)seconds
with seconds = period*3600*24
Calc the days from startDate to givenData, check the result whether it can be divisible by the period.
+ (NSInteger)daysWithinEraFromDate:(NSDate *)startDate toDate:(NSDate *)endDate {
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setTimeZone:[NSTimeZone localTimeZone]];
// for timezone issue
NSDate *newDate1 = [startDate dateByAddingTimeInterval:[[NSTimeZone localTimeZone] secondsFromGMT]];
NSDate *newDate2 = [endDate dateByAddingTimeInterval:[[NSTimeZone localTimeZone] secondsFromGMT]];
NSInteger startDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit
inUnit: NSEraCalendarUnit forDate:newDate1];
NSInteger endDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit
inUnit: NSEraCalendarUnit forDate:newDate2];
return endDay - startDay;
}
I need to get an NSDate object for 00:00(beginning of the day) from [NSDate date], let's say if currently it is 11:30am(returned by [NSDate date]), on 01/06/2012, now I need to have an NSDate for 00:00am on 01/06/2012.
I haven't tried this, but what is in my mind is:
NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:now];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
NSDate *morningStart = [calendar dateFromComponents:components];
So I first get current date(say it is 01/06/2012), and construct a NSDateComponent for the date, then I set hour/minute/second to 0 and the year/month/day should not be changed(ie. 01/06/2012) then I create an NSDate for this component setting and can I get a date of 00:00:00 01/06/2012?
What you doing is correct, but when you NSLog morningStart the date will be displayed in GMT time zone
If you wanna make sure that the date is correct, convert it to NSString
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MMM-dd HH:mm:ss"];
NSString *strFromDate = [formatter stringFromDate:morningStart]; // this will return 2012-Jun-21 00:00:00
Converting NSDate to NSString can be helpful but if you need to keep a NSDate object for further processing, here is your solution to have your real morningStart NSDate object set at 00:00:00 time, with care of the timezone as well... As you will see you were not so far from the solution :
NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:now];
NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
int timeZoneOffset = [destinationTimeZone secondsFromGMTForDate:now] / 3600;
[components setHour:timeZoneOffset];
[components setMinute:0];
[components setSecond:0];
NSDate *morningStart = [calendar dateFromComponents:components];
It's very easy to do this in iOS8 using startOfDayForDate:
let date = NSDate()
let calendar = NSCalendar.currentCalendar(calendarIdentifier: NSGregorianCalendar)
let dateAtStartOfDay = calendar.startOfDayForDate(date)
OR you may do it in the traditional way in Swift as follows:
let date = NSDate()
let calendar = NSCalendar.currentCalendar(calendarIdentifier: NSGregorianCalendar)
// Use a mask to extract the required components from today's date
let components = calendar.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: date)
let dateAtStartOfDay = calendar.dateFromComponents(components)!
print(dateAtStartOfDay)
(Note: NSDates are stored relative to GMT. So print will display the relative local time. A clear understanding of TimeZone's is essential to using NSDates properly.)
I'm writing a GTD app for the iPhone. For the due tasks, I want to display something like "Due tomorrow" or "Due yesterday" or "Due July 18th". Obviously, I need to display "Tomorrow" even if the task is less than 24 hours away (e.g. the user checks at 11pm on Saturday and sees there's a task on Sunday at 8am). So, I wrote a method to get the number of days in between two dates. Here's the code...
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd-HH-mm"];
NSDate *nowDate = [dateFormatter dateFromString:#"2010-01-01-15-00"];
NSDate *dueDate = [dateFormatter dateFromString:#"2010-01-02-14-00"];
NSLog(#"NSDate *nowDate = %#", nowDate);
NSLog(#"NSDate *dueDate = %#", dueDate);
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *differenceComponents = [calendar components:(NSDayCalendarUnit)
fromDate:nowDate
toDate:dueDate
options:0];
NSLog(#"Days between dates: %d", [differenceComponents day]);
... and here's the output:
NSDate *nowDate = 2010-01-01 15:00:00 -0700
NSDate *dueDate = 2010-01-02 14:00:00 -0700
Days between dates: 0
As you can see, the method returns incorrect results. It should have returned 1 as the number of days between the two days. What am I doing wrong here?
EDIT: I wrote another method. I haven't done extensive unit tests, but so far it seems to work:
+ (NSInteger)daysFromDate:(NSDate *)fromDate inTimeZone:(NSTimeZone *)fromTimeZone untilDate:(NSDate *)toDate inTimeZone:(NSTimeZone *)toTimeZone {
NSCalendar *calendar = [NSCalendar currentCalendar];
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
[calendar setTimeZone:fromTimeZone];
NSDateComponents *fromDateComponents = [calendar components:unitFlags fromDate:fromDate];
[calendar setTimeZone:toTimeZone];
NSDateComponents *toDateComponents = [calendar components:unitFlags fromDate:toDate];
[calendar setTimeZone:[NSTimeZone defaultTimeZone]];
NSDate *adjustedFromDate = [calendar dateFromComponents:fromDateComponents];
NSDate *adjustedToDate = [calendar dateFromComponents:toDateComponents];
NSTimeInterval timeIntervalBetweenDates = [adjustedToDate timeIntervalSinceDate:adjustedFromDate];
NSInteger daysBetweenDates = (NSInteger)(timeIntervalBetweenDates / (60.0 * 60.0 * 24.0));
NSDateComponents *midnightBeforeFromDateComponents = [[NSDateComponents alloc] init];
[midnightBeforeFromDateComponents setYear:[fromDateComponents year]];
[midnightBeforeFromDateComponents setMonth:[fromDateComponents month]];
[midnightBeforeFromDateComponents setDay:[fromDateComponents day]];
NSDate *midnightBeforeFromDate = [calendar dateFromComponents:midnightBeforeFromDateComponents];
[midnightBeforeFromDateComponents release];
NSDate *midnightAfterFromDate = [[NSDate alloc] initWithTimeInterval:(60.0 * 60.0 * 24.0)
sinceDate:midnightBeforeFromDate];
NSTimeInterval timeIntervalBetweenToDateAndMidnightBeforeFromDate = [adjustedToDate timeIntervalSinceDate:midnightBeforeFromDate];
NSTimeInterval timeIntervalBetweenToDateAndMidnightAfterFromDate = [adjustedToDate timeIntervalSinceDate:midnightAfterFromDate];
if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0) {
// toDate is before the midnight before fromDate
timeIntervalBetweenToDateAndMidnightBeforeFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;
if (timeIntervalBetweenToDateAndMidnightBeforeFromDate < 0.0)
daysBetweenDates -= 1;
}
else if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0) {
// toDate is after the midnight after fromDate
timeIntervalBetweenToDateAndMidnightAfterFromDate -= daysBetweenDates * 60.0 * 60.0 * 24.0;
if (timeIntervalBetweenToDateAndMidnightAfterFromDate >= 0.0)
daysBetweenDates += 1;
}
[midnightAfterFromDate release];
return daysBetweenDates;
}
From the docs for components:fromDate:toDate:options::
The result is lossy if there is not a small enough unit requested to hold the full precision of the difference.
Since the difference is less than a full day, it correctly returns a result of 0 days.
If all you care about is tomorrow or yesterday vs. a specific date, then you can save yourself a lot of work and just test whether the dates are only one calendar day apart.
To do that, compare the dates to find which is earlier and which is later (and if they compare equal, bail out with that result), then test whether 1 day after the earlier date produces a date with the same year, month, and day-of-month as the later date.
If you really do want to know exactly how many calendar days there are from one date to the other:
Send the calendar a components:fromDate: message to get the year, month, and day-of-the-month of the first date.
Same as #1, but for the second date.
If the two dates are in the same year and month, subtract one day-of-month from the other and pass to abs (see abs(3)) to take the absolute value.
If they are not in the same year and month, test whether they are in adjacent months (e.g., December 2010 to January 2011, or June 2010 to July 2010). If they are, add the number of days in the earlier date's month (which you can obtain by sending the calendar a rangeOfUnit:inUnit:forDate: message, passing NSDayCalendarUnit and NSMonthCalendarUnit, respectively) to the day-of-month of the later date, then compare that result to the earlier date's day-of-month.
For example, when comparing 2010-12-31 to 2011-01-01, you would first determine that these are in adjacent months, then add 31 (number of days in 2010-12) to 1 (day-of-month of 2011-01-01), then subtract 31 (day-of-month of 2010-12-31) from that sum. Since the difference is 1, the earlier date is one day before the later date.
When comparing 2010-12-30 to 2011-01-02, you would determine that they are in adjacent months, then add 31 (days in 2010-12) to 2 (day-of-month of 2011-01-02), then subtract 30 (day-of-month of 2010-12-30) from that sum. 33 minus 30 is 3, so these dates are three calendar days apart.
Either way, I strongly suggest writing unit tests at least for this code. I've found that date-handling code is among the most likely to have subtle bugs that only manifest, say, twice a year.
One thing you might try is using rangeOfUnit: to zero out hours, minutes and seconds from the start and end dates.
NSCalendar *calendar = [NSCalendar currentCalendar];
NSCalendarUnit range = NSDayCalendarUnit;
NSDateComponents *comps = [[NSDateComponents alloc] init];
NSDate *start = [NSDate date];
NSDate *end;
[comps setDay:1];
[calendar rangeOfUnit:range startDate:&start interval:nil forDate:start];
end = [calendar dateByAddingComponents:comps toDate:start options:0];
In this example start will be 2010-06-19 00:00:00 -0400, end will be 2010-06-20 00:00:00 -0400. I'd imagine this would work better with NSCalendar's comparison methods, although I haven't tested it myself.
I am using this piece of code, it is working very well:
- (NSInteger)daysToDate:(NSDate*)date
{
if(date == nil) {
return NSNotFound;
}
NSUInteger otherDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:date];
NSUInteger currentDay = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:self];
return (otherDay-currentDay);
}
Here is the function I've used in the past
its defined in a category on NSDate
- (int) daysToDate:(NSDate*) endDate
{
//dates needed to be reset to represent only yyyy-mm-dd to get correct number of days between two days.
NSDateFormatter *temp = [[NSDateFormatter alloc] init];
[temp setDateFormat:#"yyyy-MM-dd"];
NSDate *stDt = [temp dateFromString:[temp stringFromDate:self]];
NSDate *endDt = [temp dateFromString:[temp stringFromDate:endDate]];
[temp release];
unsigned int unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps = [gregorian components:unitFlags fromDate:stDt toDate:endDt options:0];
int days = [comps day];
[gregorian release];
return days;
}
-(NSInteger)daysBetweenTwoDates:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime
{
NSDate *fromDate;
NSDate *toDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate
interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate
interval:NULL forDate:toDateTime];
NSDateComponents *difference = [calendar components:NSDayCalendarUnit
fromDate:fromDate toDate:toDate options:0];
return [difference day];
}
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;
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://stackoverflow.com/a/14688780/308315
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.
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];
setFirstWeekday: on the NSCalendar object.
Sets the index of the first weekday for the receiver.
- (void)setFirstWeekday:(NSUInteger)weekday
Should do the trick.
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).
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
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.
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];
}
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;
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];
}
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]];
Try this:
NSCalendar *yourCal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]
[yourCal setFirstWeekday: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
The ISO 8601 calendar appears to have it's first weekday set to monday by default.
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
}
}
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
… is there a calendar that already has Monday as the first day of the week, instead of Sunday.
Someday, there will be.
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