Get progress of day in Objective-C - ios

I need to get the progress of the current day (For example from 6am and 11pm). I have tried many things and the only code I can get working so far is this code:
//Get the seconds between two days
//Get todays date
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
//Set todays date in a NSCalendar object
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *date1 = [gregorian dateFromComponents:components];
//Get tomorrows date
NSDateComponents *components1 = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
components1.day++;
//Set tomorrows date in a NSCalendar object
NSDate *date2 = [gregorian dateFromComponents:components1];
NSTimeInterval secondsBetween = [date2 timeIntervalSinceDate:date1];
NSLog(#"Test: %f", secondsBetween);
This code will get the amount of seconds between two days in total (which doesn't really matter as it's a constant). How do I get the progress between two different times? As the user will be allowed to change the times it counts between I need to be able to calculate this on the fly in a NSTimer.
Edit: I want to know how to use a ProgressView to display the percentage between two times. E.g. The ProgressView would be 50% if it was 9AM and the times were 6AM and 12PM.

The difference in seconds between two dates is
[date2 timeIntervalSinceDate:date1];
That seems to be all you need for your problem.

you only set day/month/year -- set hours/minutes/seconds too.
right now the dates always point to 00:00:00 and not to any TIME

No problem. I use the following method to determine the difference between NSDate values:
- (NSInteger)secondsBetweenDate:(NSDate*)fromDateTime andDate:(NSDate*)toDateTime {
NSDate *fromDate;
NSDate *toDate;
NSCalendar *calendar = [NSCalendar currentCalendar];
//Returns by reference the starting time and duration for the given dates (with second precision).
[calendar rangeOfUnit:NSCalendarUnitSecond startDate:&fromDate
interval:NULL forDate:fromDateTime];
[calendar rangeOfUnit:NSCalendarUnitSecond startDate:&toDate
interval:NULL forDate:toDateTime];
//Returns the difference between the two dates (with second precision)
NSDateComponents *difference = [calendar components:NSCalendarUnitSecond
fromDate:fromDate toDate:toDate options:0];
//returns the number of seconds
return [difference second];
}
To test the above code, I've used an easier way to create two points in time (rather than many lines you've used above). This is simply used to test and you'll obviously use real values in your actual code
NSDate * myDate1 = [NSDate date]; // date as of right now
NSDate * myDate2 = [[NSDate alloc] initWithTimeIntervalSinceNow:1 * 24 * 60 * 60]; // exactly 24 hours into the future
NSLog(#"Second Between: %ld",(long)[self secondsBetweenDate:myDate1 andDate:myDate2]);
//OUTPUT: Seconds: 86400
You can easily tweak the values above to test certain ranges. For reference, this is what each number stands for:
1 (days) * 24 (hours) * 60 (minutes) * 60 (seconds)
OK, with that sorted you just need to figure out how much time has progressed since your starting time. You can do that with two calls to the method created at the start of this post:
//Assuming dates are sourced from some other code
NSDate * startTime // Set elsewhere in the code
NSDate * currentTime // Set elsewhere in the code
NSDate * endTime // Set elsewhere in the code
long fullDuration = [self secondsBetweenDate:startTime andDate:endTime];
long timeElapsed = [self secondsBetweenDate:startTime andDate:currentTime];
float percentComplete = (float) timeElapsed / (float) fullDuration;
Now, since we're going to be using ProgressView I'm keep this percent value as a number between 0 and 1 and the variable as a float.
Finally, assuming you already have the following done:
Added a ProgressView object to your storyboard
Added an outlet to to the object called something like progressViewOutlet
All you need to do is send the update regularly using your timer using the following code:
self.progressViewOutlet.progress = percentComplete;
You can grab my source code below. Let me know if you have any questions
https://dl.dropboxusercontent.com/u/43038596/testDates.zip

Related

How to get current seconds left to complete hour?

I want to get how many seconds are remaining to complete an hour. No matter which what time it is?
if its 05:01:00 then it should give 3540 seconds
and if its 11:58:40 then it gives 80 seconds and so on. I try to find it on google but could not able to find it.
Thanks in advance.
NSCalendar has got methods to do that kind of date math:
NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
// find next date where the minutes are zero
NSDate *nextHour = [calendar nextDateAfterDate:now matchingUnit:NSCalendarUnitMinute value:0 options:NSCalendarMatchNextTime];
// get the number of seconds between now and next hour
NSDateComponents *componentsToNextHour = [calendar components:NSCalendarUnitSecond fromDate:now toDate:nextHour options:0];
NSLog(#"%ld", componentsToNextHour.second);
#Vadian's answer is very good. (voted)
It requires iOS 8 or later however.
There are other ways you could do this using NSCalendar and NSDateComponents that would work with older OS versions.
You could use componentsFromDate to get the month, day, year, and hour from the current date, then increment the hour value and use the NSCalendar method dateFromComponents: to convert your to adjusted components back to a date.
NSDate *date1 = [NSDate dateWithString:#"2010-01-01 00:00:00 +0000"];
NSDate *date2 = [NSDate dateWithString:#"2010-02-03 00:00:00 +0000"];
NSTimeInterval secondsBetween = [date2 timeIntervalSinceDate:date1];
Fill in with the correct times and dates to get the difference in seconds
Edit: An alternative method is to work out the currenthour and return as an integer. Then add one to the NSInteger returned as below (you will have to make sure to handle the case where it is after midnight though!)
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:[NSDate date]];
NSInteger currentHour = [components hour];

Trying to determine if current date is 3 days or less from the end of the month in iOS

I am trying to determine if the current date is in fact three days or less from the end of the month. In other words, if I am in August, then I would like to be alerted if it is the 28,29,30, or 31st. If I am in February, then I would like to be notified when it is the 25,26,27, or 28 (or even 29). In the case of a leap year, I would be alerted from 26th onwards.
My problem is that I am not sure how to perform such a check so that it works for any month. Here is my code that I have thus far:
-(BOOL)monthEndCheck {
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
NSInteger day = [components day];
NSInteger month = [components month];
NSInteger year = [components year];
if (month is 3 days or less from the end of the month for any month) {
return YES;
} else {
return NO;
}
}
Because there are months with 28, 30, and 31 days, I would like a dynamic solution, rather than creating a whole series of if/else statements for each and every condition. Is there a way to do this?
This is how you get the last day of the month:
NSDate *curDate = [NSDate date];
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* comps = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:curDate]; // Get necessary date components
// set last of month
[comps setMonth:[comps month]+1];
[comps setDay:0];
NSDate *tDateMonth = [calendar dateFromComponents:comps];
NSLog(#"%#", tDateMonth);
Source: Getting the last day of a month
EDIT (another source): How to retrive Last date of month give month as parameter in iphone
Now you can simply count from the current date.
If < 3 do whatever you wanted to do.
Maybe something like this:
NSTimeInterval distanceBetweenDates = [date1 timeIntervalSinceDate:date2];
double timeInSecondsFor3Days = 280000; //Better use NSDateComponents here!
NSInteger hoursBetweenDates = distanceBetweenDates / timeInSecondsFor3Days;
However I did not test that^^
EDIT: Thanks to Aaron. Do NSDateComponents to calculate the time for three days instead!
First you have to compute the start of the current day (i.e. today at 00.00).
Otherwise, the current day will not count as a full day when computing the
difference between today and the start of the next month.
NSDate *now = [NSDate date];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *startOfToday;
[cal rangeOfUnit:NSCalendarUnitDay startDate:&startOfToday interval:NULL forDate:now];
Computing the start of the next month can be done with rangeOfUnit:...
(using a "statement expression" to be fancy :)
NSDate *startOfNextMonth = ({
NSDate *startOfThisMonth;
NSTimeInterval lengthOfThisMonth;
[cal rangeOfUnit:NSCalendarUnitMonth startDate:&startOfThisMonth interval:&lengthOfThisMonth forDate:now];
[startOfThisMonth dateByAddingTimeInterval:lengthOfThisMonth];
});
And finally the difference in days:
NSDateComponents *comp = [cal components:NSCalendarUnitDay fromDate:startOfToday toDate:startOfNextMonth options:0];
if (comp.day < 4) {
// ...
}

Store time zone independent time-only in NSDate object

I am creating an app which includes a timetable feature, allowing the user to specify periods in a particular day (e.g. Period 1 goes from 9:00 to 10:00; Period 2 goes from 10:00 to 11:00 and so on). As the app is backed by Core Data it seems sensible to store the periods as Core Data managed objects with two NSDate attributes: startTime and endTime.
Assuming that the periods cannot span multiple days, the date in the NSDate objects is irrelevant - I am only interested in the time. While one approach is to simply use NSDateComponents to extract the time (and ignore the date) if I am doing a comparison of NSDate objects, or sorting based upon those objects, then if the dates are different that will affect the comparison or sort. I was thinking that one way around this problem is to manually the startTime and endTime attributes to have the same (arbitrarily) chosen date. Is this the best way around the issue?
Additionally, the times (i.e. NSDate objects) need to be time zone independent. If the user travels from say Australia to the USA then I don't want all the times to now show they have a class in the middle of the night! Compensating for time zone changes is not relevant in this app, so I would like the times to stay static regardless of time zone. I would appreciate advice on this aspect too.
Thanks in advance for any assistance!
If you don't need the "day date" I would recommend to not use NSDate at all, otherwise you will run into various issues. As said in the comments NSDate is not timezone aware, this means you will do a lot of unnecessary conversions to make sure that your app works in all timezones.
I would as well use the approach to save seconds since midnight, or have two fields for hours and minutes since midnight. That basically is local time, always.
To get this information from a UIDatePicker you have to convert the NSDate into NSDateComponents
- (IBAction)datePickerChanged:(UIDatePicker *)sender {
NSDate *date = sender.date;
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
calendar.timeZone = sender.timeZone;
NSDateComponents *components = [calendar components:NSHourCalendarUnit|NSMinuteCalendarUnit fromDate:date];
NSInteger hours = components.hour;
NSInteger minutes = components.minute;
NSLog(#"%02ld:%02ld", (long)hours, (long)minutes);
NSInteger secondsSinceMidnight = hours * (60*60) + minutes * 60;
// save to core data
}
When you want to show your time in the datePicker you have to go the opposite way, you have to create a NSDate (that, for timezone reasons happens to be today) from the saved components. e.g.:
// get from core data
NSInteger secondsSinceMidnight = 8 * (60*60) + 30 * 60;
NSInteger minutes = (secondsSinceMidnight / 60) % 60;
NSInteger hours = (secondsSinceMidnight / 3600);
NSLog(#"%02ld:%02ld", (long)hours, (long)minutes);
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
calendar.timeZone = datePicker.timeZone;
// use today as day, to avoid timezone issues
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:[NSDate date]];
components.hour = hours;
components.minute = minutes;
datePicker.date = [calendar dateFromComponents:components];

Possible to compare NSDate objects without using calendar components?

I'm looking to compare NSDate objects based on the day only (ignoring time). Instead of converting the time to 0:00:00, or using NSDateComponent like most solutions (ex. Comparing two NSDates and ignoring the time component)
Does anyone see an issue with converting the date to an int representing the number of days since 1970 with the timeIntervalSince1970 method?
return (int)([date timeIntervalSince1970]/(SECONDS_PER_DAY));
Yes, absolutely. There are an endless number of pitfalls with date math. Use NSDateComponents; they’re not hard.
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *date1Components = [cal components:NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date1];
NSDateComponents *date2Components = [cal components:NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date2];
NSComparisonResult comparison = [[cal dateFromComponents:date1Components] compare:[cal dateFromComponents:date2Components];
Here's a way to convert an NSDate to an NSTimeInterval that represents midnight of the original date without using NSCalendar. Doing this with two NSDate objects would let you compare the two dates without regard to time.
NSDate *now = [NSDate date]; // Your original date with time
NSTimeInterval interval = [now timeIntervalSince1970]; // the full interval
NSDateFormatter *form = [[NSDateFormatter alloc] init];
[form setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; // Zulu time
[form setDateFormat:#"A"]; // milliseconds since midnight
NSString *secondsStr = [form stringFromDate:now];
NSTimeInterval seconds = [secondsStr integerValue] / 1000.0; // seconds since midnight
NSTimeInterval justDate = interval - seconds; // interval for date at midnight (Zulu time)
// For testing purposes
NSDate *nowDate = [NSDate dateWithTimeIntervalSince1970:justDate];
NSLog(#"now = %#, interval = %f", now, interval);
NSLog(#"seconds = %#", secondsStr);
NSLog(#"justDate = %f, nowDate = %#", justDate, nowDate);
This may or may not be better than using NSCalendar as shown in Noah's answer.
You must definitely not simply divide by SECONDS_PER_DAY. That will simply be wrong.
NSCalendar.currentCalendar().compareDate(date1, toDate: date2, toUnitGranularity: .Day)
Returns NSComparisonResult
Here is what apple talks about it

Get an Accurate Time Difference Between two NSDate?

Is there any way to find out an accurate difference between two NSDate?
I have found solutions but they aren't accurate enough. I need to take into account daylight saving, the fact that different months have a different number of days, etc.
A simple calculation such as /60/60/24 etc. to work out minutes, hours and days doesn't take them into account.
Lets say I need to work out the difference between the time right now ([NSDate date]) and December 25th 10:22PM (date chosen by user using date picker [datePicker date]) just as an example, how would I do this?
Knowing the exact time difference isn't the key, so long as I have an accurate difference of days, months and years, it will do.
From Apple's Date & Time Programming Guide:
Listing 12 Getting the difference between two dates
NSDate *startDate = ...;
NSDate *endDate = ...;
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
NSUInteger unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents *components = [gregorian components:unitFlags
fromDate:startDate
toDate:endDate options:0];
NSInteger months = [components month];
NSInteger days = [components day];
NSDate is completely independent of Timezone. Daylight saving doesn't even come into the picture for NSDate. Only when you convert NSDate into a human readable format (MM/DD/YY, HH:MM:SS format or the like), does Time Zone come into picture.
Make sure that you take into account correct timezone, day-light saving setting when you create NSDate(s). Subsequently, the method, [date1 timeIntervalSinceDate:date2] should always give you accurate time difference.
So, the more accurate question you meant to ask was: How can I get a nicely formatted days, months, years from a difference between two dates. First you want to get the nsTimerInterval (time difference in seconds) and then format it:
How do I break down an NSTimeInterval into year, months, days, hours, minutes and seconds on iPhone?
Small update on the code for latest iOS:
NSDate *startDate = ...;
NSDate *endDate = ...;
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSUInteger unitFlags = NSCalendarUnitMonth | NSCalendarUnitDay;
NSDateComponents *components = [gregorian components:unitFlags
fromDate: startDate
toDate:endDate options:0];
NSInteger months = [components month];
NSInteger days = [components day];

Resources