Calculating hour difference iOS - ios

I have written like this to calculate hour difference.
+ (NSInteger)hoursBetweenDate:(NSDate*)fromDateTime andDate:(NSDate *)toDateTime
{
NSDate *fromDate;
NSDate *toDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[calendar rangeOfUnit:NSHourCalendarUnit startDate:&fromDate
interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSHourCalendarUnit startDate:&toDate
interval:NULL forDate:toDateTime];
NSDateComponents *difference = [calendar components:NSHourCalendarUnit
fromDate:fromDate toDate:toDate options:0];
return [difference hour];
}
Problem is that if input is like this,
2016-03-09 07:58:39 +0000 (fromDateTime)
2016-03-09 09:07:44 +0000 (toDateTime),
difference is always 2 hours. Actually, it should be 1 hour and 10 minute. As a result, I only want to show 1 hour. Is there a way to round up or down?

If you are just interested in the "unformatted" number of hours between to dates, you can use NSTimeInterval and calculate it easily.
+(int)fullHoursBetweenDate:(NSDate *)date1 andDate:(NSDate *)date2 {
NSTimeInterval interval = [date2 timeIntervalSinceDate:date1];
return (int)interval / 3600;
}

You extracted out only the hour component (NSHourCalendarUnit) before you compare, that's why it's always 2 (9 - 7). Added NSMinuteCalendarUnit the result would be 1:
+ (NSInteger)hoursBetweenDate:(NSDate*)fromDateTime andDate:(NSDate *)toDateTime
{
NSDate *fromDate;
NSDate *toDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[calendar rangeOfUnit:NSHourCalendarUnit|NSMinuteCalendarUnit startDate:&fromDate
interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSHourCalendarUnit|NSMinuteCalendarUnit startDate:&toDate
interval:NULL forDate:toDateTime];
NSDateComponents *difference = [calendar components:NSHourCalendarUnit
fromDate:fromDate toDate:toDate options:0];
return [difference hour];
}
Also, if you are targeting iOS 8+, you should use NSCalendarUnitHour and NSCalendarUnitMinute instead as NSHourCalendarUnit and NSMinuteCalendarUnit were deprecated.

This small NSCalendar extension should do a trick for you.
extension NSCalendar {
func hoursBetweenDates(startDate: NSDate, endDate: NSDate) -> Int {
let days = differenceInDates(startDate, endDate: endDate, unit: .Hour)
return days
}
func differenceInDates(startDate: NSDate, endDate: NSDate, unit: NSCalendarUnit) -> Int {
let startUnit = ordinalityOfUnit(unit, inUnit: .Era, forDate:startDate)
let endUnit = ordinalityOfUnit(unit, inUnit: .Era, forDate:endDate)
let difference = endUnit-startUnit
return difference
}
}
And Obj-C version can be written as follow:
#interface NSCalendar (Duration)
-(NSNumber *)hoursBetweenStartDate:(NSDate*)startDate andEndDate:(NSDate*) endDate;
#end
#implementation NSCalendar (Duration)
-(NSNumber *)hoursBetweenStartDate:(NSDate*)startDate andEndDate:(NSDate*) endDate {
NSUInteger startUnit = [self ordinalityOfUnit:NSCalendarUnitHour inUnit:NSCalendarUnitEra forDate:startDate];
NSUInteger endUnit = [self ordinalityOfUnit:NSCalendarUnitHour inUnit:NSCalendarUnitEra forDate:endDate];
return [NSNumber numberWithUnsignedInteger: (endUnit - startUnit)];
}
#end

Modify this function as per your requirement
+(NSString*)timeDifference:(NSDate*)startDate endDate:(NSDate*)endDate{
NSDateComponents *components;
NSInteger days;
NSInteger hour;
NSInteger minutes;
NSString *strDuration;
components = [[NSCalendar currentCalendar] components: NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute
fromDate: startDate toDate: endDate options: 0];
days = [components day];
hour=[components hour];
minutes=[components minute];
if(days>0){
if(days>1){
strDuration=[NSString stringWithFormat:#"%d days",days];
}
else{
strDuration=[NSString stringWithFormat:#"%d day",days];
}
return strDuration;
}
if(hour>0){
if(hour>1){
strDuration=[NSString stringWithFormat:#"%d hours",hour];
}
else{
strDuration=[NSString stringWithFormat:#"%d hour",hour];
}
return strDuration;
}
if(minutes>0){
if(minutes>1){
strDuration=[NSString stringWithFormat:#"%d minutes",minutes];
}
else{
strDuration=[NSString stringWithFormat:#"%d minute",minutes];
}
return strDuration;
}
return #"";
}

Related

Calculate total number of weeks from two NSDate

I want to check if the current NSDate is 2 Weeks ahead of the another NSDate. I have can do somethings as below code but was wondering if there is any proper way to do.
[calendar rangeOfUnit:NSCalendarUnitDay startDate:&currentDate interval:NULL forDate:currentDate];
[calendar rangeOfUnit:NSCalendarUnitDay startDate:&previousDate interval:NULL forDate:previousDate];
NSDateComponents *difference = [calendar components:NSCalendarUnitDay fromDate:previousDate toDate:currentDate options:0];
if(diiference >= 14) {
}
You can use one trick here. First calculate number of days between 2 dates and then calculate number of weeks between them. You can do it like
-(NSInteger)weeksBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime {
NSDate *fromDate;
NSDate *toDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar rangeOfUnit:NSCalendarUnitDay startDate:&fromDate
interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSCalendarUnitDay startDate:&toDate
interval:NULL forDate:toDateTime];
NSDateComponents *difference = [calendar components:NSCalendarUnitDay
fromDate:fromDate toDate:toDate options:0];
return [difference day] / 7;
}
You can use CGFloat if you want to get week value like 1.5 and keep as it is if you want week count like 1,2,3,etc
Try this
objective C
+ (NSInteger)daysBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime
{
NSDate *fromDate;
NSDate *toDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar rangeOfUnit:NSCalendarUnitDay startDate:&fromDate
interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSCalendarUnitDay startDate:&toDate
interval:NULL forDate:toDateTime];
NSDateComponents *difference = [calendar components:NSCalendarUnitDay
fromDate:fromDate toDate:toDate options:0];
return [difference day];
}
Swift
extension NSDate {
func numberOfDaysUntilDateTime(toDateTime: NSDate, inTimeZone timeZone: NSTimeZone? = nil) -> Int {
let calendar = NSCalendar.currentCalendar()
if let timeZone = timeZone {
calendar.timeZone = timeZone
}
var fromDate: NSDate?, toDate: NSDate?
calendar.rangeOfUnit(.Day, startDate: &fromDate, interval: nil, forDate: self)
calendar.rangeOfUnit(.Day, startDate: &toDate, interval: nil, forDate: toDateTime)
let difference = calendar.components(.Day, fromDate: fromDate!, toDate: toDate!, options: [])
return difference.day
}
}

Get days between days

Before giving downvote, comment the reason
I have created a UIDatePicker with minimum & maximum date values (i.e. 6 months of date picker) Now, i need to get 7 days from selected date. There i need to check the conditions,
If date is today date i need to get 7 days from today onwards
If date is last date (i.e. last date of picker) need to get last 7 days including last day
If date is middle of today's date & last date i need to get last 3 days, next 3 days including today date. And, also while getting last & next 3 days it shouldn't get exceed with picker's date limit.
Here's my code snippet:
- (void)addDays:(NSInteger)range {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
NSDate *startDate = self.selectedDate;
for (int x = 0; x <= range; x++) {
NSLog(#"%#", [dateFormat stringFromDate:startDate]);
startDate = [startDate dateByAddingTimeInterval:(60 * 60 * 24)];
}
}
- (void)minusDays:(NSInteger)range {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MM-yyyy"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [NSDateComponents new];
for (NSInteger i=0; i<range; i++) {
comps.day += -1;
NSDate *date = [calendar dateByAddingComponents:comps toDate:self.selectedDate options:0];
NSDateComponents *components = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:date];
NSLog(#"%#", [dateFormat stringFromDate:[calendar dateFromComponents:components]]);
}
}
- (void)calculateDateRange {
if ([dateArray count] > 0) {
[dateArray removeAllObjects];
}
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *lastcomponents = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:[NSDate date]];
lastcomponents.month += 6;
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:#"yyyy/MM/dd"];
NSDate *currentDate = [NSDate date];
NSDate *selectedD = self.selectedDate;
NSDate *endDate = [calendar dateFromComponents:lastcomponents];
NSDate *fromDate;
NSDate *toDate;
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:currentDate];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:selectedD];
NSDateComponents *difference = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
NSInteger first = [difference day];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:selectedD];
[calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:endDate];
NSDateComponents *difference2 = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
NSInteger second = [difference2 day];
if ((first == 0 || first < 3) && second > 7) {
[self addDays:7];
} else if (first >= 3 && second > 7) {
[self minusDays:3];
[self addDays:3];
}else if (second == 7 || second < 7) {
[self minusDays:7];
}
}
This is working fine. But, can't get exact last & previous days.
Anyone has idea on this?
My interpretation of your needs
You have a date picker. When a date is picked you need to create a 7 day range around that selected date.
So, if the selected date is 15/11/2014 then you want 3 days either side so...
12/11/2014 - 18/11/2014.
However, the date range cannot exceed the limits of the date picker. So if the minimum date on the date picker is set to 14/11/2014 then (in the above example) the date range would be...
14/11/2014 - 21/11/2014
Even after your additional explanation this is still my interpretation. And my code does exactly this.
Solution
You CANNOT use 60*60*24 to mean one day. This is just wrong. When dealing with dates you should always be using NSDateComponents and NSCalendar.
Also, break down your problem into small steps. There is no reason to do everything in one giant function.
OK I guess you have a datePicker action somewhere so I'd code it like this...
- (void)datePickerDateChanged
{
NSDate *minimumDate = self.datePicker.minimumDate;
NSDate *maximumDate = self.datePicker.maximumDate;
NSDate *selectedDate = self.datePicker.date;
NSDate *startDate;
NSDate *endDate;
if ([self numberOfDaysFromDate:minimumDate toDate:selectedDate] < 3) {
// get 7 days after minimumDate
startDate = minimumDate;
endDate = [self dateByAddingDays:6 toDate:minimumDate];
} else if ([self numberOfDaysFromDate:selectedDate toDate:maximumDate] < 3) {
// get 7 days before maximumDate
startDate = [self dateByAddingDays:-6 toDate:maximumDate];
endDate = maximumDate;
} else {
// get 3 days before and 3 days after selectedDate
startDate = [self dateByAddingDays:-3 toDate:selectedDate];
endDate = [self dateByAddingDays:3 toDate:selectedDate];
}
// Here startDate and endDate define your date range.
}
- (NSDate *)dateByAddingDays:(NSInteger)days toDate:(NSDate *)date
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [NSDateComponents new];
components.day = days;
return [calendar dateByAddingComponents:components toDate:date options:0];
}
- (NSInteger)numberOfDaysFromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0];
// always return positive. We just want magnitude of days.
return components.day > 0 ? components.day : -components.day;
}
This is untested and just a first attempt.

Trying to determine if current day of the month is in the beginning of the month in iOS

I have the following method which helps me determine if the current day is 3 days or less from the end of the month:
- (BOOL) checkMonthEnd {
NSDate *now = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *startOfToday;
[cal rangeOfUnit:NSCalendarUnitDay startDate:&startOfToday interval:NULL forDate:now];
NSDate *startOfNextMonth = ({
NSDate *startOfThisMonth;
NSTimeInterval lengthOfThisMonth;
[cal rangeOfUnit:NSCalendarUnitMonth startDate:&startOfThisMonth interval:&lengthOfThisMonth forDate:now];
[startOfThisMonth dateByAddingTimeInterval:lengthOfThisMonth];
});
NSDateComponents *comp = [cal components:NSCalendarUnitDay fromDate:startOfToday toDate:startOfNextMonth options:0];
if (comp.day < 4) {
return YES;
} else {
return NO;
}
}
What I would like to do is now modify it so that it shows me if the current date is within the first three days of the BEGINNING of the current month. How can I do this?
To determine if it's one of the first three days of the month, just retrieve NSCalendarUnitDay and see if it's less than 4:
- (BOOL) checkMonthStart {
NSDate *now = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:NSCalendarUnitDay fromDate:now];
return components.day < 4;
}
By the way, if checking whether you're within three days of the end of the month, I might suggest:
- (BOOL) checkMonthEnd {
NSDate *now = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:NSCalendarUnitDay fromDate:now];
NSRange range = [cal rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:now];
return (range.length - components.day) < 3;
}

Get list of days from selected month

I want to get list of days in selected month.
For example, If user select feb then it show 1 to 28 0r 29 days. How can i do that?
Thanks
You can try the following code, I have given the today's date to find the list of dates in this month. You can give some other date to find the list of that given month.
NSDate *today = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSMutableArray *datesThisMonth = [NSMutableArray array];
NSRange rangeOfDaysThisMonth = [cal rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:today];
NSDateComponents *components = [cal components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit | NSEraCalendarUnit) fromDate:today];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
for (NSInteger i = rangeOfDaysThisMonth.location; i < NSMaxRange(rangeOfDaysThisMonth); ++i) {
[components setDay:i];
NSDate *dayInMonth = [cal dateFromComponents:components];
[datesThisMonth addObject:dayInMonth];
}
NSLog(#"datesThisMonth: %#", datesThisMonth);
You can retrieve the number of days in specified month
NSCalendar* cal = [NSCalendar currentCalendar];
NSDateComponents* comps = [[NSDateComponents alloc] init];
// Set month here
[comps setMonth:2]; // 2 = February
NSRange range = [cal rangeOfUnit:NSDayCalendarUnit
inUnit:NSMonthCalendarUnit
forDate:[cal dateFromComponents:comps]];
NSLog(#"current day count in this month: %lu", (unsigned long)range.length);
I have an open source calendar event project. Maybe you can check it out if you need anything.
DPCalendar
I have simplied it to
- (NSInterger) numberOfDaysForMonth:(NSDate *)date {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate: date];
[components setDay:1];
//Get the first day of the month
NSDate *firstDay = [calendar dateFromComponents:components];
[components setDay:0];
[components setMonth:components.month + 1];
//Get the last day of the month
NSDate *lastDay = [calendar dateFromComponents:components];
components = [self.calendar components:NSDayCalendarUnit fromDate:firstDay toDate:lastDay options:0];
return components.day + 1;
}
Cheers
Generally, we only need to handle Feb depend on the year, here's my method implemented:
+ (NSUInteger)numberOfDaysForYear:(NSInteger)year
month:(NSInteger)month
{
if (2 == month) {
NSCalendar * gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents * dateComponents = [[NSDateComponents alloc] init];
[dateComponents setYear:year];
[dateComponents setMonth:month];
[dateComponents setDay:1];
NSDate * firstDayDate = [gregorianCalendar dateFromComponents:dateComponents];
NSRange days = [gregorianCalendar rangeOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitMonth
forDate:firstDayDate];
return days.length;
} else if (4 == month || 6 == month || 9 == month || 11 == month) {
return 30;
} else {
return 31;
}
}

NSDateComponents / NSDate : get the last sunday

I know there are some questions similar to this one, but I just can't find what's wrong with my code.
Basically what I want is if today is not sunday, currentDate is set to the last sunday.
If today is sunday I currentDate is set to the sunday before.
Here's how I'm trying to do it.
NSDateComponents *components = [[NSCalendar currentCalendar] components: NSWeekCalendarUnit | NSYearCalendarUnit | NSWeekdayCalendarUnit fromDate:currentDate];
if (components.weekday == 1) { //Its sunday. We just need to subtract a week
[components setWeek: -1];
currentDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:currentDate options:0];
}else{ //If its not sunday we just go to sunday
[components setWeekday: 1];
currentDate = [[NSCalendar currentCalendar] dateFromComponents:components];
}
The first time this part of the code is executed I get the right answer. After the first time I get weird dates, like 02 Dec. 4026, and the year keeps going up.
Here's the code that made it work:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components: ( NSWeekCalendarUnit | NSWeekdayCalendarUnit | NSYearCalendarUnit) fromDate:currentDate];
int weekday = components.weekday;
if (weekday == 1) {
[components setWeek:components.week -1];
}else{
[components setWeekday:1];
}
currentDate = [calendar dateFromComponents:components];
Use nextDateAfterDate with the .SearchBackwards option if you are using iOS 8.0+
let calendar = NSCalendar.currentCalendar()
let options: NSCalendarOptions = [.MatchPreviousTimePreservingSmallerUnits, .SearchBackwards]
calendar.nextDateAfterDate(NSDate(), matchingUnit: .Weekday, value: 1, options: options)
Try this category on NSCalendar
#interface NSCalendar (LastSunday)
-(NSDate *)previousSundayForDate:(NSDate *)date;
#end
#implementation NSCalendar (LastSunday)
-(NSDate *)previousSundayForDate:(NSDate *)date
{
static NSUInteger SUNDAY = 1;
static NSUInteger MONDAY = 2;
NSDate *startOfWeek;
[self rangeOfUnit:NSWeekCalendarUnit
startDate:&startOfWeek
interval:NULL
forDate:date];
if(self.firstWeekday == SUNDAY){
NSDate *beginningOfDate;
[self rangeOfUnit:NSDayCalendarUnit
startDate:&beginningOfDate
interval:NULL forDate:date];
if ([startOfWeek isEqualToDate:beginningOfDate]) {
startOfWeek = [self dateByAddingComponents:(
{
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = -7;
comps;
})
toDate:startOfWeek
options:0];
}
return startOfWeek;
}
if(self.firstWeekday == MONDAY)
return [self dateByAddingComponents:(
{
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = -1;
comps;
})
toDate:startOfWeek
options:0];
return nil;
}
#end
use it as:
NSDate *lastSunday = [calendar previousSundayForDate:[NSDate date]];
PS:
a version for "one-method-one-return-statement"- fetishists:
#implementation NSCalendar (LastSunday)
-(NSDate *)previousSundayForDate:(NSDate *)date
{
NSDate *resultDate = nil;
static NSUInteger SUNDAY = 1;
static NSUInteger MONDAY = 2;
NSDate *startOfWeek;
[self rangeOfUnit:NSWeekCalendarUnit
startDate:&startOfWeek
interval:NULL
forDate:date];
if(self.firstWeekday == SUNDAY){
NSDate *beginningOfDate;
[self rangeOfUnit:NSDayCalendarUnit
startDate:&beginningOfDate
interval:NULL forDate:date];
if ([startOfWeek isEqualToDate:beginningOfDate]) {
startOfWeek = [self dateByAddingComponents:(
{
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = -7;
comps;
})
toDate:startOfWeek
options:0];
}
resultDate = startOfWeek;
}
if(self.firstWeekday == MONDAY)
resultDate = [self dateByAddingComponents:(
{
NSDateComponents *comps = [[NSDateComponents alloc] init];
comps.day = -1;
comps;
})
toDate:startOfWeek
options:0];
return resultDate;
}
#end
The reason you got 4026 is because of this line:
NSDateComponents *components = [[NSCalendar currentCalendar] components: NSWeekCalendarUnit | NSYearCalendarUnit | NSWeekdayCalendarUnit fromDate:currentDate];
It sets the year component to the current year, 2013. It also initializes the week and weekday components. Then the components are added to the current date, which doubles the year to 4026, here:
[components setWeek: -1];
currentDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:currentDate options:0];
If you want to subtract only one week, do it like this:
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setWeek: -1];
currentDate = [[NSCalendar currentCalendar] dateByAddingComponents:components toDate:currentDate options:0];
[components release];

Resources