How can I get all the dates that come in between 2 dates?
For example:
Start date = 2011/01/25
End date = 2011/02/03
1) Find the no of days between two dates. Then,
for(i=0;i<noofdays;i++)
{
//Find the next date
//add to the array
}
To find number of days
To find next date
NSCalendarUnit serves for defining the step between the dates & taking care of the dates being normalized.
iOS 8 API, Swift 2.0
func generateDates(calendarUnit: NSCalendarUnit, startDate: NSDate, endDate: NSDate) -> [NSDate] {
let calendar = NSCalendar.currentCalendar()
let normalizedStartDate = calendar.startOfDayForDate(startDate)
let normalizedEndDate = calendar.startOfDayForDate(endDate)
var dates = [normalizedStartDate]
var currentDate = normalizedStartDate
repeat {
currentDate = calendar.dateByAddingUnit(calendarUnit, value: 1, toDate: currentDate, options: NSCalendarOptions.MatchNextTime)!
dates.append(currentDate)
} while !calendar.isDate(currentDate, inSameDayAsDate: normalizedEndDate)
return dates
}
To find all days between two NSDates:
- (void)cerateDaysArray{
_daysArray = [NSMutableArray new];
NSCalendar *calendar = [[NSCalendaralloc]initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone systemTimeZone]];
NSDate *startDate = [_minDate copy];
NSDateComponents *deltaDays = [NSDateComponents new];
[deltaDays setDay:1];
[_daysArray addObject:startDate];
while ([startDate compare:_maxDate] == NSOrderedAscending) {
startDate = [calendar dateByAddingComponents:deltaDays toDate:startDate options:0];
[_daysArray addObject:startDate];
}
}
Related
I tried the following code, but I do not get a condition where the date is in next week. How will I know that the date which is parameter in the function falls in next week? Following code always returns 1.
- (NSInteger)thisW:(NSDate *)date
{
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *todaysComponents =
[gregorian components:NSYearCalendarUnit fromDate:[NSDate date]];
NSUInteger todaysWeek = [todaysComponents weekOfYear];
NSDateComponents *otherComponents =
[gregorian components:NSYearCalendarUnit fromDate:date];
NSUInteger datesWeek = [otherComponents weekOfYear];
//NSLog(#"Date %#",date);
if(todaysWeek==datesWeek){
//NSLog(#"Date is in this week");
return 1;
}else if(todaysWeek+1==datesWeek){
//NSLog(#"Date is in next week");
return 2;
} else {
return 0;
}
}
You need to pass NSCalendarUnitWeekOfYear when extracting date components:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *todaysComponents = [gregorian components:NSCalendarUnitWeekOfYear fromDate:[NSDate date]];
NSUInteger todaysWeek = [todaysComponents weekOfYear];
NSDateComponents *otherComponents = [gregorian NSCalendarUnitWeekOfYear fromDate:date];
NSUInteger datesWeek = [otherComponents weekOfYear];
Using datecomponents for week calculations will give you problems when the dates are close to year's end, i.e. "The first week of the year is designated to be the week containing the first Thursday of the year.(ISO 8601)"
I find it easier to compare dates with certain granularity, weekly in this case (the following code detects if a date is more than one week in the past, last week, this week, next week or further in the future)
-(EventWeekRange)numberOfWeeksFromTodayToEvent:(NSDate *)eventDate {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSComparisonResult comparison = [calendar compareDate:[NSDate date] toDate:eventDate toUnitGranularity:NSCalendarUnitWeekOfYear];
if (comparison == NSOrderedSame) {
return RangeThisWeek;
} else if (comparison == NSOrderedAscending) { // The event date is in the future
// Advance today's date one week to check if this new date is in the same week as the event
NSDate *todaysNextWeek = [[NSDate date]dateByAddingTimeInterval:60*60*24*7];
if ([calendar compareDate:todaysNextWeek toDate:eventDate toUnitGranularity:NSCalendarUnitWeekOfYear] == NSOrderedSame) {
return RangeNextWeek;
} else {
return RangeLater;
}
} else { // The event date is in the past
// Advance the event's date one week to check if this new date is in the same week as today
NSDate *eventsNextWeek = [eventDate dateByAddingTimeInterval:60*60*24*7];
if ([calendar compareDate:eventsNextWeek toDate:[NSDate date] toUnitGranularity:NSCalendarUnitWeekOfYear] == NSOrderedSame) {
return RangeLastWeek;
} else {
return RangeEarlier;
}
}
}
I new in cocoa programing and I want to know how I get the number of working days between to dates. So only from Monday to Friday. Thanks.
NSDate *startDate = ...;
NSDate *stopDate = ...;
NSDateFormatter *df = [NSDateFormatter new];
df.dateFormat = #"yyyy-MM-dd";
startDate = [df dateFromString:[df stringFromDate:startDate]]; // get start of the day
NSDateComponents *comps = [NSDateComponents new];
comps.day = 1; // one day in NSDateComponents
NSUInteger count = 0;
while ([stopDate timeIntervalSinceDate:startDate] >= 0) {
NSInteger weekday = [[NSCalendar currentCalendar] components:NSCalendarUnitWeekday fromDate:startDate].weekday;
if (weekday != 1 && weekday != 6) ++count; // filter out weekend days - 6 and 1
startDate = [[NSCalendar currentCalendar] dateByAddingComponents:comps toDate:startDate options:0]; // increment start day
}
Using today as an example, how do I determine which date it was, 230 workdays ago?
I know how to do it iteratively with a while loop checking date and subtracting 1 if it's a workday, but I'm wondering if there is a better method.
Also, let's take a Sunday 1 PM as an example, and subtract 3 work days and 2 hours from that time. First, it doesn't make sense to subtract work-time from weekends. So it would have to move the time to 23:59:59 of Friday, and then subtract those 3 days and 2 hours.
If it's a Monday at 1:30 AM, and I'm subtracting 5 days and 3 work-hours from that time, then the result should be Friday 22:30 PM of the previous week.
Code to test Kevin's method:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *dc = [[NSDateComponents new] autorelease];
dc.month = 12;
dc.day = 19;
dc.year = 2011;
dc.hour = 1;
dc.minute = 0;
dc.second = 0;
NSDate *date = [cal dateFromComponents:dc];
NSLog(#"%#", [date descriptionWithCalendarFormat:nil timeZone:nil locale:nil]);
date = dateBySubtractingWorkOffset(date, 0, 2);
NSLog(#"%#", [date descriptionWithCalendarFormat:nil timeZone:nil locale:nil]);
Output log:
2011-12-02 16:33:46.878 otest[7124:707] 2011-12-19 01:00:00 -0500
2011-12-02 16:33:47.659 otest[7124:707] 2011-12-18 23:00:00 -0500
It should never be 12-18, since that's a Sunday.
Figure out how long from the last weekend your date is, subtract that amount from both your date and your offset. Now you can divide your offset by 5 to figure out how many full weeks are in your offset, and then multiply that by 7 and subtract this new value from your date. Take your previous offset (the one you divided by 5) and mod it by 5, to get the number of remaining days. If it's greater than 0, subtract that offset + 2 (for the weekend) from your date.
Note, this assumes every single weekday is a workday. Corporate holidays tend to make that assumption invalid. If you need to handle holidays, you're in for a much tougher problem.
Update: Here's an attempt to fix David's code to actually express the idea here:
NSDate *dateBySubtractingWorkOffset(NSDate *date, NSUInteger days, NSUInteger hours) {
const int secsInHour = 60*60;
const int secsInDay = 24*secsInHour;
NSTimeInterval offset = days*secsInDay + hours*secsInHour;
NSCalendar *cal = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
// figure out distance from last weekend
{
NSUInteger units = NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSWeekdayCalendarUnit;
NSDateComponents *dc = [cal components:units fromDate:date];
if (dc.weekday == 1 || dc.weekday == 7) {
// we're in the weekend already. Let's just back up until friday
// and then we can start our calculations there
} else {
// figure out our offset from sunday 23:59:59
dc.day -= (dc.weekday - 1);
dc.weekday = 1;
dc.hour = 23;
dc.minute = 23;
dc.second = 23;
NSDate *sunday = [cal dateFromComponents:dc];
NSTimeInterval newOffset = [date timeIntervalSinceDate:sunday];
if (offset < newOffset) {
// our offset doesn't even go back to sunday, we don't need any calculations
return [date dateByAddingTimeInterval:-offset];
}
offset -= [date timeIntervalSinceDate:sunday];
// Now we can jump back to Friday with our new offset
}
// Calculate last friday at 23:59:59
dc.day -= (dc.weekday % 7 + 1);
dc.hour = 23;
dc.minute = 59;
dc.second = 59;
date = [cal dateFromComponents:dc];
}
// We're now set to Friday 23:59:59
// Lets figure out how many weeks we have
int secsInWorkWeek = 5*secsInDay;
NSInteger weeks = (NSInteger)trunc(offset / secsInWorkWeek);
offset -= weeks*secsInWorkWeek;
if (weeks > 0) {
// subtract that many weeks from the date
NSDateComponents *dc = [[NSDateComponents alloc] init];
dc.week = -weeks;
date = [cal dateByAddingComponents:dc toDate:date options:0];
[dc release];
}
// now we can just subtract our remaining offset from the date
return [date dateByAddingTimeInterval:-offset];
}
I haven't exhaustively test this, but it's based on some category methods I use regularly. To determine how many weekdays are between date1 and date2 (assumes date1 < date2), divide the return value of this function by 24*60*60 (the number of seconds in a day).
This splits the calculation into number of days before the first weekend, number of days after the last weekend and number of days in the intervening weeks. A weekend starts on Saturday at 00:00:00 hours and ends on Sunday at 23:59:59 hours. Typically you want to avoid assuming that a day has 24 hours in it, because there may be special cases associated with daylight savings time. So I recommend using NSCalendar to calculate time intervals when this is important. But that happens on weekends, so it is not significant for this case.
There are two methods here. The first returns the NSDate end date if you provide a start date and the number of working days (weekdays) you want to extend out to. (An earlier date is returned if the number of working days is negative.) The second returns the number of seconds that correspond to number of working days (including fractional days) between two given NSDate dates.
I tried to keep calculations within a timezone, but defaulted to the system timezone. (By the way, if you want to calculate with fractional days, change the weekdays parameter to a float. Or you may want to calculate using a parameter in seconds. If so, then also change the calculation of totalInterval in the first method. You won't have to convert to seconds. All subsequent calculations in that method are done in seconds.)
- (NSDate*) calculateWeekDaysEndDateFrom:(NSDate*)_date1 and:(int)weekdays {
NSTimeInterval dayInterval = 24*60*60;
NSTimeInterval totalInterval = dayInterval * (float) weekdays;
NSTimeInterval secondsBeforeWeekend;
NSTimeInterval secondsAfterWeekend;
NSTimeInterval secondsInInterveningWeeks;
int numberOfWeeks;
NSDate *dateOfFirstSaturdayMorning;
NSDate *dateOfLastSundayNight;
NSDate *finalDate;
if (weekdays >0) {
dateOfFirstSaturdayMorning = [_date1 theFollowingWeekend];
secondsBeforeWeekend = [dateOfFirstSaturdayMorning timeIntervalSinceDate:_date1];
numberOfWeeks = (int)((totalInterval - secondsBeforeWeekend)/(5.0 * dayInterval));
secondsInInterveningWeeks = 5 * (float)(numberOfWeeks * dayInterval);
secondsAfterWeekend = totalInterval - secondsBeforeWeekend - secondsInInterveningWeeks;
dateOfLastSundayNight = [[dateOfFirstSaturdayMorning dateByAddingDays:7*numberOfWeeks+2] dateByAddingTimeInterval:-1]; // move from saturday morning to monday morning, then back off 1 second
finalDate = [dateOfLastSundayNight dateByAddingTimeInterval:secondsAfterWeekend];
}
else {
dateOfLastSundayNight = [_date1 thePreviousWeekend];
secondsAfterWeekend = [date1 timeIntervalSinceDate:dateOfLastSundayNight];
numberOfWeeks = (int)((-totalInterval - secondsAfterWeekend)/(5.0 * dayInterval));
secondsInInterveningWeeks = 5 * (float)(numberOfWeeks * dayInterval);
dateOfFirstSaturdayMorning = [[dateOfLastSundayNight dateByAddingDays:-(7*numberOfWeeks+2)] dateByAddingTimeInterval:+1];
secondsBeforeWeekend = -totalInterval - secondsInInterveningWeeks - secondsAfterWeekend;
finalDate = [dateOfFirstSaturdayMorning dateByAddingTimeInterval:-secondsBeforeWeekend];
}
NSLog(#"dateOfFirstSaturdayMorning = %#", [dateOfFirstSaturdayMorning descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(#"dateOfLastSundayNight = %#",[dateOfLastSundayNight descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(#"date 1 = %#", date1);
NSLog (#"daysBeforeWeekend = %.2f", secondsBeforeWeekend/((float)dayInterval));
NSLog (#"daysBetweenWeekends = %.2f", secondsInInterveningWeeks/((float)(dayInterval)));
NSLog (#"daysAfterWeekend = %.2f", secondsAfterWeekend/((float)dayInterval));
NSLog (#"numberOfWeekdays = %.2f", (secondsBeforeWeekend + secondsInInterveningWeeks + secondsAfterWeekend)/((float)dayInterval));
NSLog(#"endDateFromWeekdays = %#", [finalDate descriptionWithLocale:[NSLocale currentLocale]]);
return finalDate;
}
- (NSTimeInterval) calculateWeekdaysFrom:(NSDate*)_date1 and:(NSDate*)_date2 {
if (_date1 && _date2) {
NSTimeInterval secondsBeforeWeekend;
NSTimeInterval secondsAfterWeekend;
NSDate *dateOfFirstSaturdayMorning;
NSDate *dateOfLastSundayNight;
NSTimeInterval dayInterval = 24*60*60; // This isn't always true, e.g., if daylight savings intervenes. (But that happens on the weekend in most places.)
// see if they are in the same week
if (([_date1 ordinality] < [_date2 ordinality]) && [_date2 timeIntervalSinceDate:_date1] <= 5*dayInterval) {
return [_date2 timeIntervalSinceDate:_date1];
}
// time interval before a first weekend
if ([_date1 ordinality] == 1 || [_date1 ordinality] == 7) {
secondsBeforeWeekend = 0;
dateOfFirstSaturdayMorning = _date1; // This is just a convenience. It's not true. But, later, rounding takes place to deal with it.
}
else {
dateOfFirstSaturdayMorning = [_date1 theFollowingWeekend];
secondsBeforeWeekend = [dateOfFirstSaturdayMorning timeIntervalSinceDate:_date1];
}
int ordDate2 = [_date2 ordinality];
int ordFirstSaturday = [dateOfFirstSaturdayMorning ordinality];
// time interval after a last weekend
if ([_date2 ordinality] == 1 || [_date2 ordinality] == 7) {
secondsAfterWeekend = 0;
dateOfLastSundayNight = _date2; // Again, this is just a convenience. It's not true.
}
else {
dateOfLastSundayNight = [_date2 thePreviousWeekend];
secondsAfterWeekend = [_date2 timeIntervalSinceDate:dateOfLastSundayNight];
}
NSTimeInterval intervalBetweenWeekends = [dateOfLastSundayNight timeIntervalSinceDate:dateOfFirstSaturdayMorning];
int numberOfWeeks = (int) (intervalBetweenWeekends/(7*dayInterval));
int secondsInInterveningWeeks = (float) (5*dayInterval*numberOfWeeks);
NSLog(#"date 1 = %#", [_date1 descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(#"date 2 = %#", [_date2 descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(#"dateOfFirstSaturdayMorning = %#", [dateOfFirstSaturdayMorning descriptionWithLocale:[NSLocale currentLocale]]);
NSLog(#"dateOfLastSundayNight = %#",[dateOfLastSundayNight descriptionWithLocale:[NSLocale currentLocale]]);
NSLog (#"daysBeforeWeekend = %.2f", secondsBeforeWeekend/((float)dayInterval));
NSLog (#"daysBetweenWeekends = %.2f", secondsInInterveningWeeks/((float)(dayInterval)));
NSLog (#"daysAfterWeekend = %.2f", secondsAfterWeekend/((float)dayInterval));
NSLog (#"numberOfWeekdays = %.2f", (secondsBeforeWeekend + secondsInInterveningWeeks + secondsAfterWeekend)/((float)dayInterval));
return secondsBeforeWeekend + secondsInInterveningWeeks + secondsAfterWeekend;
}
else
return 0;
}
The files for category methods on NSDate are NSDate+help.h
#interface NSDate (help)
+ (NSDate *) LSExtendedDateWithNaturalLanguageString:(NSString *)dateString WithFormatter:(NSDateFormatter*)dateFormatter;
- (NSUInteger)ordinality;
- (NSDate*) theFollowingWeekend;
- (NSDate *) thePreviousWeekend;
- (NSDate *) dateByAddingDays:(NSInteger) numberOfDays;
- (NSDate *) dateByMovingToBeginningOfDayInTimeZone:(NSTimeZone*)tz;
- (NSDate *) dateByMovingToEndOfDayInTimeZone:(NSTimeZone*)tz;
#end
and NSDate+help.m
#import "NSDate+help.h"
#implementation NSDate (help)
// thrown in for testing
+ (NSDate *) LSExtendedDateWithNaturalLanguageString:(NSString *)dateString WithFormatter:(NSDateFormatter*)dateFormatter{
[dateFormatter setDateFormat:#"yyyy-MM-dd HHmm"];
[dateFormatter setLocale:[NSLocale currentLocale]];
//NSDate *formattedDate = [dateFormatter dateFromString:#"2008-12-3T22-11-30-123"];
return [dateFormatter dateFromString:dateString];
}
- (NSUInteger)ordinality {
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:[NSTimeZone systemTimeZone]];
return [calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSWeekCalendarUnit forDate:self];
}
- (NSDate*) theFollowingWeekend {
NSUInteger myOrdinality = [self ordinality];
NSDate *dateOfFollowingWeekend = [self dateByAddingDays:(7-myOrdinality)%7];
return [dateOfFollowingWeekend dateByMovingToBeginningOfDayInTimeZone:(NSTimeZone*)nil];
}
- (NSDate *) thePreviousWeekend {
NSUInteger myOrdinality = [self ordinality];
NSDate *dateOfPreviousWeekend = [self dateByAddingDays:(1-myOrdinality)];
return [dateOfPreviousWeekend dateByMovingToEndOfDayInTimeZone:(NSTimeZone*)nil];
}
- (NSDate *) dateByAddingDays:(NSInteger) numberOfDays {
NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
dayComponent.day = numberOfDays;
NSCalendar *theCalendar = [NSCalendar currentCalendar];
return [theCalendar dateByAddingComponents:dayComponent toDate:self options:0];
}
- (NSDate *) dateByMovingToBeginningOfDayInTimeZone:(NSTimeZone*)tz {
NSTimeZone *timezone;
if (tz)
timezone = tz;
else
timezone = [NSTimeZone systemTimeZone];
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents* parts = [[NSCalendar currentCalendar] components:flags fromDate:self];
[parts setHour:0];
[parts setMinute:0];
[parts setSecond:0];
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timezone];
return [calendar dateFromComponents:parts];
}
- (NSDate *)dateByMovingToEndOfDayInTimeZone:(NSTimeZone*)tz {
NSTimeZone *timezone;
if (tz)
timezone = tz;
else
timezone = [NSTimeZone systemTimeZone];
unsigned int flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents* parts = [[NSCalendar currentCalendar] components:flags fromDate:self];
[parts setHour:23];
[parts setMinute:59];
[parts setSecond:59];
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timezone];
return [calendar dateFromComponents:parts];
}
#end
The category method ordinality returns the number for the day of the week of the receiver. Sunday = 1, Saturday = 7. This is used to find out how many days there are before the end of the first week and how many days there are after the beginning the last week. (Calculations are actually carried out in seconds.)
The category methods theFollowingWeekend and thePreviousWeekend return the NSDate at midnight on the Saturday morning that follows the receiver date and the NSDate one second before midnight on the Sunday that follows the receiver date. These methods assume you have already validated that the receiver date is not on the weekend. I handled that in the main methods. Look for the checks of ordinality == 1 or 7.
dateByMovingToBeginningOfDayInTimeZone: and dateByMovingToEndOfDayInTimeZone: set the hours, minutes, and seconds of the receiver date to 00:00:00 and 23:59:59 respectively. This is for delimiting weekends, which run from midnight Saturday morning to midnight Sunday night in the timezone.
Hope this helps. This was an exercise for me to become more familiar with the time and date functionality.
I'll credit Keith Lazuka and his calendar component for iPhone for the germination of this code.
Here's a screen shot of a test program user interface that uses these functions:
Here is your example, run through the first method. The items of interest are highlighted.
. For this, I made the simple modification to accept fractional days (which i mentioned above, but did not include in the code shown above)
Using info from above I have made a simple method to work out weekdays between two dates. Could not find this anywhere so I thought I'd post.
- (NSInteger)endDate:(NSDate *)eDate minusStartDate:(NSDate *)sDate{
int weekDaysCount;
weekDaysCount = 0;
//A method that calculates how many weekdays between two dates
//firstcompare dates to make sure end date is not in the past
//using the NScomparisonresult and the NSDate compare: method
NSComparisonResult result = [sDate compare:eDate];
if (result == NSOrderedDescending) {
eDate = sDate;
//NSLog(#"invalid date so set to end date to start date");
}
//Work out the number of days btween the twodates passed in
//first set up a gregorian calander
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger unitFlags = NSDayCalendarUnit;
NSDateComponents *components = [gregorian components:unitFlags
fromDate:sDate
toDate:eDate options:0];
//get the number of days
NSInteger days = [components day];
//now loop through the days and only count the weekdays
while (days > 0) {//while days are greater than 0
// NSLog(#"days = %i", days);
//get the weekday number of the start date
NSDateComponents *comps = [gregorian components:NSWeekdayCalendarUnit fromDate:sDate];
// NSLog(#"sDate %#", sDate);
int weekday = [comps weekday];
// NSLog(#"Comps Weekday = %i", weekday);
//Test for a weekday - if its not a Saturday or Sunday
if ((weekday!=7) && (weekday !=1)){
//increase weekDays count
weekDaysCount ++;
// NSLog(#"weekDaysCount is %i", weekDaysCount);
// NSLog(#"-------------------------");
}
//decrement the days
days -=1;
//increase the date so the next day can be tested
sDate = [sDate dateByAddingTimeInterval:(60 * 60 * 24)];
}
return weekDaysCount;
}
I have this category added to NSDate:
- (bool)isWeekend
{
NSString* s = [self asString:#"e"];
if ([s isEqual:#"6"])
return YES;
else if ([s isEqual:#"7"])
return YES;
else
return NO;
}
Helper function:
- (NSString*)asString:(NSString*)format
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:format];
NSString *formattedDateString = [dateFormatter stringFromDate:self];
[dateFormatter release];
return formattedDateString;
}
isWeekend should return YES if it is a saturday or a sunday. But it does not work if the locale has a week start on a sunday, in which case friday will be day 6 and saturday will be day 7.
How can I solve this?
You want to use NSCalendar and NSDateComponents:
NSDate *aDate = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSRange weekdayRange = [calendar maximumRangeOfUnit:NSWeekdayCalendarUnit];
NSDateComponents *components = [calendar components:NSWeekdayCalendarUnit fromDate:aDate];
NSUInteger weekdayOfDate = [components weekday];
if (weekdayOfDate == weekdayRange.location || weekdayOfDate == weekdayRange.length) {
//the date falls somewhere on the first or last days of the week
NSLog(#"weekend!");
}
This is operating under the assumption that the first and last days of the week comprise the "week ends" (which is true for the Gregorian calendar. It may not be true in other calendars).
As of iOS 8, you can use isDateOnWeekend: on NSCalendar.
In Swift 3+:
extension Date {
var isWeekend: Bool {
return NSCalendar(calendarIdentifier: NSCalendar.Identifier.gregorian)!.isDateInWeekend(self)
}
}
In Swift:
func isWeekend(date: NSDate) -> Bool {
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
return calendar.isDateInWeekend(date)
}
I'm trying to get the number of days in a current year.
When I try the solution on Number of days in the current month using iPhone SDK?, and replace NSMonthCalendarUnit by NSYearCalendarUnit, I still get the number of days for that month.
Does anyone know how I should do this?
Thanks in advance for your help.
Here's a super accurate NSCalendar extension in Swift 2:
extension NSCalendar {
func daysInYear(date: NSDate = NSDate()) -> Int? {
let year = components([NSCalendarUnit.Year], fromDate: date).year
return daysInYear(year)
}
func daysInYear(year: Int) -> Int? {
guard let begin = lastDayOfYear(year - 1), end = lastDayOfYear(year) else { return nil }
return components([NSCalendarUnit.Day], fromDate: begin, toDate: end, options: []).day
}
func lastDayOfYear(year: Int) -> NSDate? {
let components = NSDateComponents()
components.year = year
guard let years = dateFromComponents(components) else { return nil }
components.month = rangeOfUnit(NSCalendarUnit.Month, inUnit: NSCalendarUnit.Year, forDate: years).length
guard let months = dateFromComponents(components) else { return nil }
components.day = rangeOfUnit(NSCalendarUnit.Day, inUnit: NSCalendarUnit.Month, forDate: months).length
return dateFromComponents(components)
}
}
You can use it like this:
let calendar = NSCalendar.currentCalendar() // I'm using the Gregorian calendar
calendar.daysInYear() // 365 (since it's currently 2015)
calendar.daysInYear(2016) // 366 (leap year!)
This is super flexible since we don't assume anything about the length of the calendar:
let hebrew = NSCalendar(calendarIdentifier: NSCalendarIdentifierHebrew)
hebrew?.daysInYear(-7) // 354
hebrew?.daysInYear(-100) // 384
Enjoy.
If you're only going to use the Gregorian Calender, you can calculate it manually.
http://en.wikipedia.org/wiki/Leap_year#Algorithm
if year modulo 400 is 0 then leap
else if year modulo 100 is 0 then no_leap
else if year modulo 4 is 0 then leap
else no_leap
I finally came up with a solution that works. What I do is first calculate the number of months in the year and then for each month calculate the number of days for that month.
The code looks like this:
NSUInteger days = 0;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *today = [NSDate date];
NSDateComponents *components = [calendar components:NSYearCalendarUnit fromDate:today];
NSUInteger months = [calendar rangeOfUnit:NSMonthCalendarUnit
inUnit:NSYearCalendarUnit
forDate:today].length;
for (int i = 1; i <= months; i++) {
components.month = i;
NSDate *month = [calendar dateFromComponents:components];
days += [calendar rangeOfUnit:NSDayCalendarUnit
inUnit:NSMonthCalendarUnit
forDate:month].length;
}
return days;
It is not as neat as I would have hoped for but it will work for any calendar such as the ordinary gregorian one or the islamic one.
Use the NSCalendar and NSDateComponent classes, like this:
long GetDaysInYear(int year) {
NSDateComponents* c = [[NSDateComponents alloc] init];
c.year = year;
NSCalendar* cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate* startDate = [cal dateFromComponents:c];
c.year += 1;
NSDate* endDate = [cal dateFromComponents:c];
return [cal components:NSDayCalendarUnit fromDate:startDate toDate:endDate options:0].day;
}
As example:
func daysInYear(year: Int) -> Int {
var calendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)
var b = NSDate.dateWithNaturalLanguageString("01.01.\(year)", locale: NSLocale.currentLocale()) as! NSDate
var e = NSDate.dateWithNaturalLanguageString("12.31.\(year)", locale: NSLocale.currentLocale()) as! NSDate
return calendar!.components(NSCalendarUnit.CalendarUnitDay, fromDate: b, toDate: e, options: nil).day + 1
}
But default days return 355 and 354 this caused (may be) that counting begin from zero :)