Cocoa - Calculate working days between two dates - objective-c

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
}

Related

Objective-C: find date given day of the year and year

I'm writing a GUI for a legacy data object where a date is saved in the integer properties day of the year and year (real "genius" design...)
How can I convert this data into day and month given the year?
I'm currently trying:
int theYear = 2016;
int theDayOfTheYear = 222;
NSCalendar* gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setYear:theYear];
[components setDay:theDayOfTheYear];
NSDate* theDate = [gregorian dateFromComponents:components];
This seems a bit too hacky to me. It could be just coincidence that it works with days >31 - haven't found any mention of this in the NSDateComponents class reference.
When I debug, po theDate gives me 2016-08-08 22:00:00 +0000
--> close, but it should be 2016-08-09 (!)
To perform the reverse operation, i.e. to convert a normal DD-MM-YYYY date into day of the year, I'm using this code:
NSDate *fullDate = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger dayOfYear = [gregorian ordinalityOfUnit:NSDayCalendarUnit
inUnit:NSYearCalendarUnit
forDate:fullDate];
But I can't seem to find the proper methods to do the opposite...
Sometimes a test is all takes :)
Don't forget that NSDate are GMT, so you might want to add [[NSTimeZone localTimeZone] secondsFromGMT] seconds to it.
It seems that you have it working :)
- (void) testDate
{
int startYear = 2016;
int startMonth = 1;
int startDay = 1;
// Create the startDate
NSDateComponents * startDateComp = [NSDateComponents new];
startDateComp.day = startDay;
startDateComp.month = startMonth;
startDateComp.year = startYear;
NSDate * startDate = [[NSCalendar currentCalendar] dateFromComponents:startDateComp];
for(int i=0 ; i < 365; i++)
{
// add i day to startDate
NSDate * testDate = [startDate dateByAddingTimeInterval:i * 24 * 60 * 60];
NSUInteger dayOfYear = [[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit
inUnit:NSYearCalendarUnit
forDate:testDate];
XCTAssert(dayOfYear == (i + 1), #"the day of the year should be i + 1");
// Create a date using day of year
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setYear:startYear];
[components setDay:dayOfYear];
NSDate* resultDate = [[NSCalendar currentCalendar] dateFromComponents:components];
// testing result date against test date
NSInteger test, result;
test = [[NSCalendar currentCalendar] component:NSCalendarUnitDay fromDate:testDate];
result = [[NSCalendar currentCalendar] component:NSCalendarUnitDay fromDate:resultDate];
XCTAssert(test == result);
test = [[NSCalendar currentCalendar] component:NSCalendarUnitMonth fromDate:testDate];
result = [[NSCalendar currentCalendar] component:NSCalendarUnitMonth fromDate:resultDate];
XCTAssert(test == result);
test = [[NSCalendar currentCalendar] component:NSCalendarUnitYear fromDate:testDate];
result = [[NSCalendar currentCalendar] component:NSCalendarUnitYear fromDate:resultDate];
XCTAssert(test == result);
}
Please try this. Its the larger calendar unit NSCalendarUnitEra :)
NSDate *fullDate = [NSDate date];
NSCalendar *gregorian = [NSCalendar currentCalendar];
gregorian.timeZone = [NSTimeZone systemTimeZone];
NSUInteger dayOfYear = [gregorian ordinalityOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitEra
forDate:fullDate];
NSDateComponents *dateCom = [[NSDateComponents alloc] init];
dateCom.day = dayOfYear;
NSDate *expectedDate = [gregorian dateFromComponents:dateCom];
NSLog(#"%#",expectedDate);

24 hours divided by 30 minutes apart - NSDate

I'm trying to get an NSMutableArray of NSDates each NSDate object targets the nearest rounded either HH:00 or HH:30 for the next 24 hours from now.
lets say right now its 02:31 I want an output of #[03:00,03:30,04:00,04:30...03:00(of the next day)]
if its 02:29 then I want #[02:30,03:00 and so on till the next day at 02:30].
right now I have the code below and it works almost all the time, yet rarely I get an output of all the NSDate objects that I want with a remainder of an extra minute(#[03:01,03:31:04:01...so on]).
any ideas ?
-(NSMutableArray*)TwentyFourHoursFromNowDivicedBy30Mins
{
NSMutableArray * dateArray = [NSMutableArray array];
NSCalendar * cal = [NSCalendar currentCalendar];
NSDateComponents * plusDays = [NSDateComponents new];
NSDate * now = [self changeTimeValue:[NSDate date]];
for( NSUInteger day = 0; day < 2; day++ )
{
[plusDays setDay:day];
[dateArray addObject:[cal dateByAddingComponents:plusDays toDate:now options:0]];
}
NSDate *startDate = [dateArray objectAtIndex:0];
NSDate *endDate = [dateArray objectAtIndex:1];
NSDateComponents *diff = [[NSDateComponents alloc] init];
[diff setMinute:0];
NSCalendar *calz = [NSCalendar currentCalendar];
NSDate *tmp = startDate;
NSMutableArray *dates = [NSMutableArray arrayWithObject:tmp];
while ([tmp laterDate:endDate] == endDate) {
[diff setMinute:[diff minute] + 30];
tmp = [calz dateByAddingComponents:diff toDate:startDate options:0];
[dates addObject:tmp];
}
return dates;
}
- (NSDate *)changeTimeValue:(NSDate *)dateValue{
NSDateComponents *time = [[NSCalendar currentCalendar]
components:NSHourCalendarUnit | NSMinuteCalendarUnit
fromDate:dateValue];
long val = 0;
NSDate *newDate = [[NSDate alloc] init];
NSInteger minutes = [time minute];
if(minutes > 0 && minutes < 30) {
val = 30 - minutes; NSTimeInterval aTimeInterval = [dateValue
timeIntervalSinceReferenceDate] + 60 * val + minutes;
newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
return newDate;
} else if(minutes > 30 && minutes < 60) {
val = 60 - minutes;
NSTimeInterval aTimeInterval = [dateValue timeIntervalSinceReferenceDate]
+ 60 * val;
newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
return newDate;
} else {
return newDate;
}
}
You're not zeroing out the seconds in your changeTimeValue, and in any case I think you've overcomplicated it. So I suspect you're subsequently seeing rounding issues. Just use NSDateComponents to do the whole job:
NSCalendar *currentCalendar = [NSCalendar currentCalendar];
NSDateComponents *dateComponents = [currentCalendar
components:NSHourCalendarUnit |
NSMinuteCalendarUnit |
NSYearCalendarUnit |
NSMonthCalendarUnit |
NSDayCalendarUnit
fromDate:dateValue];
dateComponents.minute += 15;
dateComponents.minute -= dateComponents.minute%30;
return [currentCalendar dateFromComponents:dateComponents];

Converting a Gregorian date to Julian Day Count in Objective C

I need Objective C method for converting Gregorian date to Julian days same as this PHP method (GregorianToJD).
Precision: Incorporating time of day in Julian Date conversions
These Julian date conversion methods yield results identical to the U.S. Naval Observatory Online Julian Date Converter, which is more precise than NSDateFormatter's Julian Date conversion. Specifically, the functions below incorporate time-of-day (e.g. hour, minute and seconds), whereas NSDateFormatter rounds to noon GMT.
Swift examples:
func jdFromDate(date : NSDate) -> Double {
let JD_JAN_1_1970_0000GMT = 2440587.5
return JD_JAN_1_1970_0000GMT + date.timeIntervalSince1970 / 86400
}
func dateFromJd(jd : Double) -> NSDate {
let JD_JAN_1_1970_0000GMT = 2440587.5
return NSDate(timeIntervalSince1970: (jd - JD_JAN_1_1970_0000GMT) * 86400)
}
Objective-C examples:
double jdFromDate(NSDate *date) {
double JD_JAN_1_1970_0000GMT = 2440587.5;
return JD_JAN_1_1970_0000GMT + date.timeIntervalSince1970 / 86400;
}
NSDate dataFromJd(double jd) {
double JD_JAN_1_1970_0000GMT = 2440587.5;
return [[NSDate alloc] initWithTimeIntervalSince1970: (jd - JD_JAN_1_1970_0000GMT) * 86400)];
}
Note: Research confirms that the accepted answer rounds the date to a 24-hour interval because it uses the g format-specifier of NSDateFormatter, which returns the Modified Julian Day, according to the UNICODE standard's Date Format Patterns that Apple's date formatting APIs adhere to (according to the Date Formatting Guide).
According to http://en.wikipedia.org/wiki/Julian_day, the Julian day number for January 1, 2000, was 2,451,545. So you can compute the number of days between your date and this
reference date. For example (Jan 1, 2014):
NSUInteger julianDayFor01012000 = 2451545;
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[cal setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSDateComponents *comp = [[NSDateComponents alloc] init];
comp.year = 2014;
comp.month = 1;
comp.day = 1;
NSDate *date = [cal dateFromComponents:comp];
comp.year = 2000;
comp.month = 1;
comp.day = 1;
NSDate *ref = [cal dateFromComponents:comp];
NSDateComponents *diff = [cal components:NSDayCalendarUnit fromDate:ref toDate:date options:0];
NSInteger julianDays = diff.day + julianDayFor01012000;
NSLog(#"%ld", (long)julianDays);
// Output: 2456659
This gives the same result as http://www.php.net/manual/en/function.gregoriantojd.php:
<?php
$jd = GregorianToJD(1, 1, 2014);
echo "$jd\n";
?>
Inverse direction (Julian days to Gregorian year/month/day):
NSInteger julianDays = 2456659; // From above example
NSUInteger julianDayFor01012000 = 2451545;
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[cal setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSDateComponents *comp = [[NSDateComponents alloc] init];
comp.year = 2000;
comp.month = 1;
comp.day = 1;
NSDate *ref = [cal dateFromComponents:comp];
NSDateComponents *diff = [[NSDateComponents alloc] init];
diff.day = julianDays - julianDayFor01012000;
NSDate *date = [cal dateByAddingComponents:diff toDate:ref options:0];
comp = [cal components:NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit fromDate:date];
NSLog(#"%04ld-%02ld-%02ld", (long)comp.year, (long)comp.month, (long)comp.day);
// Output: 2014-01-01
UPDATE: As Hot Licks correctly stated in a comment, it is easier to use a date
formatter with the "g" format:
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comp = [[NSDateComponents alloc] init];
comp.year = 2014;
comp.month = 1;
comp.day = 1;
NSDate *date = [cal dateFromComponents:comp];
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:#"g"];
NSInteger julianDays = [[fmt stringFromDate:date] integerValue];
NSLog(#"%ld", (long)julianDays);
// Output: 2456659
And for the inverse direction:
NSInteger julianDays = 2456659;
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:#"g"];
NSDate *date = [fmt dateFromString:[#(julianDays) stringValue]];
NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comp = [cal components:NSDayCalendarUnit|NSMonthCalendarUnit|NSYearCalendarUnit fromDate:date];
NSLog(#"%04ld-%02ld-%02ld", (long)comp.year, (long)comp.month, (long)comp.day);
// Output: 2014-01-01
let date = Date() // now
let cal = Calendar.current
var day = 0
day = cal.ordinality(of: .day, in: .year, for: date) ?? 0

iOS: Compare two NSDate-s without time portion

I want to compare two dates: date1 and date2
2011-06-06 12:59:48.994 Project[419:707] firstDate:2011-06-06 10:59:21 +0000
2011-06-06 12:59:49.004 Project[419:707] selectedData:2011-06-06 10:59:17 +0000
but these dates have different time and when I use NSOrderedSame it don't work fine, how can I solve?
my code:
NSDate *firstDate = [[appDelegate.project objectAtIndex:i]objectAtIndex:3];
NSDate *secondDate = [[appDelegate.project objectAtIndex:i]objectAtIndex:4];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger comps = (NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit);
NSDateComponents *date1Components = [calendar components:comps
fromDate:firstDate];
NSDateComponents *date2Components = [calendar components:comps
fromDate:secondDate];
NSDateComponents *date3Components = [calendar components:comps fromDate:appDelegate.selectedDate];
NSLog(#"firstDate:%#", [date1Components date]);
NSLog(#"secondDate:%#", [date2Components date]);
NSLog(#"selectedData:%#", [date3Components date]);
NSComparisonResult compareStart = [[date1Components date] compare: [date3Components date]];
NSComparisonResult compareEnd = [[date2Components date] compare: [date3Components date]];
if ((compareStart == NSOrderedAscending || compareStart == NSOrderedSame)
&& (compareEnd == NSOrderedDescending || compareEnd == NSOrderedSame))
{
NSLog(#"inside");
Then I want to compare my dates and entry inside the "if" when date1 <= selectedDate <= date2; now I understand because I have a warning: I should add this "[date1Components date]" and it work; the problem is that I have in the NSLog null values, why??
NSCalendar *calendar = [NSCalendar currentCalendar];
NSInteger comps = (NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear);
NSDateComponents *date1Components = [calendar components:comps
fromDate: date1];
NSDateComponents *date2Components = [calendar components:comps
fromDate: date2];
date1 = [calendar dateFromComponents:date1Components];
date2 = [calendar dateFromComponents:date2Components];
NSComparisonResult result = [date1 compare:date2];
if (result == NSOrderedAscending) {
} else if (result == NSOrderedDescending) {
} else {
//the same
}
There is another handy method to create for a given date the date that represents the start of a given unit: [aCalendar rangeOfUnit:startDate:interval:forDate:]
To illustrate how this method works, see this code, that easily creates the date for the beginning of the day, week, month and year for a given date (here: now).
NSDate *now = [NSDate date];
NSDate *startOfToday = nil;
NSDate *startOfThisWeek = nil;
NSDate *startOfThisMonth = nil;
NSDate *startOfThisYear = nil;
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&startOfToday interval:NULL forDate:now];
[[NSCalendar currentCalendar] rangeOfUnit:NSWeekCalendarUnit startDate:&startOfThisWeek interval:NULL forDate:now];
[[NSCalendar currentCalendar] rangeOfUnit:NSMonthCalendarUnit startDate:&startOfThisMonth interval:NULL forDate:now];
[[NSCalendar currentCalendar] rangeOfUnit:NSYearCalendarUnit startDate:&startOfThisYear interval:NULL forDate:now];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterFullStyle];
[formatter setTimeStyle:NSDateFormatterFullStyle];
NSLog(#"%#", [formatter stringFromDate:now]);
NSLog(#"%#", [formatter stringFromDate:startOfToday]);
NSLog(#"%#", [formatter stringFromDate:startOfThisWeek]);
NSLog(#"%#", [formatter stringFromDate:startOfThisMonth]);
NSLog(#"%#", [formatter stringFromDate:startOfThisYear]);
result:
Thursday, July 12, 2012, 4:36:07 PM Central European Summer Time
Thursday, July 12, 2012, 12:00:00 AM Central European Summer Time
Sunday, July 8, 2012, 12:00:00 AM Central European Summer Time
Sunday, July 1, 2012, 12:00:00 AM Central European Summer Time
Sunday, January 1, 2012, 12:00:00 AM Central European Standard Time
this allows us to shorten the first code to:
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&date1 interval:NULL forDate:date1];
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&date2 interval:NULL forDate:date2];
NSComparisonResult result = [date1 compare:date2];
if (result == NSOrderedAscending) {
} else if (result == NSOrderedDescending) {
} else {
//the same
}
Note, that in this code, date1 and date2 will be overwritten. Alternatively you can pass in a reference to another NSDate pointer for startDate as shown in the code above, where now stays untouched.
I have used another method with NSDateFormatter and a string comparison, less smarter than
NSDate compare method but faster to write and flexible enough to do variety of comparison :
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd"];
if ([[dateFormat stringFromDate:date1] isEqualToString:[dateFormat stringFromDate:date2]])
{
//It's the same day
}
Okay, so it's a few years after the original question was asked, but it's probably worth mentioning that NSCalendar now has a number of methods that make certain date comparison questions much more straight-forward:
NSCalendar *currentCalendar = [NSCalendar currentCalendar];
Bool sameDay = [currentCalendar isDate:dateA inSameDayAsDate:dateB];
Swift Version , Comparing Dates and ignoring their time.
let dateExam1:NSDate = NSDate.init(timeIntervalSinceNow: 300)
let dateExam2:NSDate = NSDate.init(timeIntervalSinceNow: 10000)
let currCalendar = NSCalendar.currentCalendar()
let dateCompanent1:NSDateComponents = currCalendar.components([.Year,.Month,.Day], fromDate: dateExam1)
let dateCompanent2:NSDateComponents = currCalendar.components([.Year,.Month,.Day], fromDate: dateExam2)
let date1WithoutTime:NSDate? = currCalendar .dateFromComponents(dateCompanent1)
let date2WithoutTime:NSDate? = currCalendar .dateFromComponents(dateCompanent2)
if (date1WithoutTime != nil) && (date2WithoutTime != nil)
{
let dateCompResult:NSComparisonResult = date1WithoutTime!.compare(date2WithoutTime!)
if (dateCompResult == NSComparisonResult.OrderedSame)
{
print("Same Dates")
}
else
{
print("Different Dates")
}
}
Updating #LuAndre answer swift 5
let dateExam1 = Date(timeIntervalSinceNow: 300)
let dateExam2 = Date(timeIntervalSinceNow: 10000)
let currCalendar = Calendar.current
let dateCompanent1 = currCalendar.dateComponents([.year,.month,.day], from: dateExam1)
let dateCompanent2 = currCalendar.dateComponents([.year,.month,.day], from: dateExam2)
if let date1WithoutTime = currCalendar.date(from:dateCompanent1), let dateCompanent2 = currCalendar.date(from:dateCompanent2) {
let dateCompResult = date1WithoutTime.compare(dateCompanent2)
if (dateCompResult == ComparisonResult.orderedSame)
{
print("Same Dates")
}
else
{
print("Different Dates")
}
}

iPhone NSDate eg. next Friday

I want to create a function which results the date of next Friday but I have no plan how to do it. Has anyone a good hint to me ?
E.g. Get current date using NSDate, then use 'components>fromDate:' from NSCalendar to get the NSDateComponents, then add the time difference to next Friday and create a new NSDate and Bob's is your uncle.
Here is my working solution for getting the next 5 Sundays in Gregorian calendar:
self.nextBeginDates = [NSMutableArray array];
NSDateComponents *weekdayComponents = [[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:[NSDate date]];
int currentWeekday = [weekdayComponents weekday]; //[1;7] ... 1 is sunday, 7 is saturday in gregorian calendar
NSDateComponents *comp = [[NSDateComponents alloc] init];
[comp setDay:8 - currentWeekday]; // add some days so it will become sunday
// build weeks array
int weeksCount = 5;
for (int i = 0; i < weeksCount; i++) {
[comp setWeek:i]; // add weeks
[nextBeginDates addObject:[[NSCalendar currentCalendar] dateByAddingComponents:comp toDate:[NSDate date] options:0]];
}
[comp release];
This should work
+ (NSDate *) dateForNextWeekday: (NSInteger)weekday {
NSDate *today = [[NSDate alloc] init];
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
// Get the weekday component of the current date
NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit
fromDate:today];
/*
Add components to get to the weekday we want
*/
NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
NSInteger dif = weekday-weekdayComponents.weekday;
if (dif<=0) dif += 7;
[componentsToSubtract setDay:dif];
NSDate *beginningOfWeek = [gregorian dateByAddingComponents:componentsToSubtract
toDate:today options:0];
return beginningOfWeek;
}
Keep it simple, safe, and readable! (....KISSAR?)
#define FRIDAY 6
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
dayComponent.day = 1;
NSDate *nextFriday = [NSDate date];
NSInteger iWeekday = [[gregorian components:NSWeekdayCalendarUnit fromDate:nextFriday] weekday];
while (iWeekday != FRIDAY) {
nextFriday = [gregorian dateByAddingComponents:dayComponent toDate:nextFriday options:0];
iWeekday = [[gregorian components:NSWeekdayCalendarUnit fromDate:nextFriday] weekday];
}
Now nextFriday has your date.
Hope this helps!
EDIT
Note that if the current date was already a Friday, it would return that instead of the next Friday. If that's undesirable just init your nextFriday to a day later (so if current date was a Friday, it would start on Saturday, forcing the next Friday. And if current date was a Thursday you'd automatically have your next Friday without needing the loop).
Here is my solution, and just to warn you, on a saturday is the friday before shown.
cheers to all
NSDate *today = [[NSDate alloc] init];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit fromDate:today];
int weekday = [weekdayComponents weekday];
int iOffsetToFryday = -weekday + 6;
weekdayComponents.weekday = iOffsetToFryday;
NSDate *nextFriday = [[NSCalendar currentCalendar] dateByAddingComponents:weekdayComponents toDate:today options:0];