Time picker problem with OS4? - iphone

I have an app which I am updating to OS4. In this I use a time picker. From this I want to get an hour and minute as an integar. This is the code I used previously for this...
NSDate *selected = [timePicker date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit| NSMinuteCalendarUnit |NSSecondCalendarUnit;
NSDateComponents *compsDur = [gregorian components:unitFlags fromDate:selected];
int hDur = [compsDur hour];
int mDur = [compsDur minute];
int sDur = [compsDur second];
[gregorian release];
Now all was working fine here. My problem is that when I update to OS4 if I change my phones time settings say from the UK to the US I get a random value not what the user has entered.
To give you a specific example, on UK time I set the time for 1 minute, I get a return value for mDur as 1. If I then change my phone to New York time I get 19 hours and 6 minutes? What's going on?

I think this might be related to an undocumented change to the UIDatePicker in iOS4 where it defaults to (what I think is) GMT as the time zone.
You can set the time zone of the UIDatePicker to the system time zone so the user gets the expected experience.
yourDatePicker.timeZone = [NSTimeZone systemTimeZone];

Related

A UIDatePicker Bug?

Here's my code:
picker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0,40,0,0)];
picker.datePickerMode = UIDatePickerModeDateAndTime;
picker.minuteInterval = 5;
picker.minimumDate = [NSDate date];
Ok, It's working fine until here. (Image: http://img29.imageshack.us/img29/8277/snap1r.png)
Days that are past in the DatePicker were all grayed out. It can't be selected.
And minute Intervals are 5.
But now when I click any row that were already Grayed out. The date of DatePicker returns time of this moment.
For Example:I cliked "9" on the DatePicker (It's already past time)
And the system time now is
22:27:57
and the Date of DatePicker returns: (Image: http://img42.imageshack.us/img42/2760/nslog.png)
2012-04-08 22:27
Because my minute interval is 5 minute, so I don't hope the picker returns the value that can't be divided by 5, this will cause my program crash.
Is this a Bug? or it's just my problem?
thanks!
------To inspector g (Sorry my English isn't very good)
Because the minuteInterval of Datepicker is 5. So the return value of DatePicker's date only returns minute that can divded by 5 (etc. 0, 5 , 10 , 15 .....)
and also I have the property minimumDate set to [NSDate date], so that users can't select the date in past.
but now of user click a row that was in past (grayed out), the DatePicker's date return the time at that moment.
so the minute of date could be any value (0~60) but not I wished ( 0 , 5 , 10 , 15....)
I've tried my best to explain >"< please forgive.
To Inspector g.
Thanks for your code, I suddenly realized there's a nice way to solve my problem.
But I dunno why, there are some problems if I use your code. (I guess it's about timeZone)
But I follow your logic and re-write a code, I'll share with you:
unsigned unitFlags_ = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit;
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *comps_ = [gregorian components:unitFlags_ fromDate:[jRemindPicker date]];
NSInteger remainder = [comps_ minute] % 5;
NSLog(#"%i-%i-%i %i:%i", comps_.year, comps_.month, comps_.day, comps_.hour, comps_.minute);
if ( remainder ) {
/* My Own code /*
} else {
/* My Own Code /*
}
[gregorian release];
Your description of the problem with selecting a date/time is a little unclear, so perhaps you can clarify? Provide a short screencast?
In any event, it sounds like you cannot select a date before today, so your error is in this line:
picker.minimumDate = [NSDate date];
You are setting the minimum selectable date to the current date and time (as that's what [NSDate date] returns.
Remove that line and you should be able to select whatever date/time you wish.
EDIT
If the problem is that you can't select a date in the future, try setting:
picker.maximumDate = [NSDate distantFuture];
Using your existing minimum and this new maximum, the range of selectable dates will be set to somewhere between today and a very long time after today.
SECOND EDIT
Thanks for clarifying! I see the problem now. When you receive the callback from the user changing the date, you have to round up or down appropriately. You can then use the rounded time at that point, or manually set the picker date/time to the rounded value via setDate: animated:
For example:
-(IBAction) pickerValueChanged:(id)selector_
{
UIDatePicker* picker = (UIDatePicker*) selector_;
// get the minutes from the picker
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate:picker.date];
NSInteger minutes = [components minute];
// check if the minutes should be rounded
NSInteger remainder = minutes % 5;
if(remainder)
{
minutes += 5 - remainder;
[components setMinute:minutes];
picker.date = [calendar dateFromComponents:components];
}
// now picker.date is "safe" to use!
}

Create NSDate at specific time x days from now

I'm trying to learn Objective-C/iPhone SDK and right now I'm doing a kind of to-do app playing with local notifications.
I have a "timeOfDay" ivar stored as an NSDate from a DatePicker and a "numberOfDays" ivar stored as an NSNumber.
When I press a specific button, I would like to schedule a local notification x numberOfDays from the time the button is pressed but at the specific timeOfDay.
I seems easy to add an NSTimeInterval to the current date which would give me the a way to schedule the notification numberOfDays from current time but adding the timeOfDay feature makes it more complex.
What would be the correct way of achieving this?
Thanks
Use NSDateComponents to add time intervals to an existing date while respecting all the quirks of the user's current calendar.
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
// Get the year, month and day of the current date
NSDateComponents *dateComponents = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit| NSDayCalendarUnit) fromDate:[NSDate date]];
// Extract the hour, minute and second components from self.timeOfDay
NSDateComponents *timeComponents = [calendar components:(NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:self.timeOfDay];
// Apply the time components to the components of the current day
dateComponents.hour = timeComponents.hour;
dateComponents.minute = timeComponents.minute;
dateComponents.second = timeComponents.second;
// Create a new date with both components merged
NSDate *currentDateWithTimeOfDay = [calendar dateFromComponents:dateComponents];
// Create new components to add to the merged date
NSDateComponents *futureComponents = [[NSDateComponents alloc] init];
futureComponents.day = [self.numberOfDays integerValue];
NSDate *newDate = [calendar dateByAddingComponents:futureComponents toDate:currentDateWithTimeOfDay options:0];
There is a pretty simple method to do this that won't involve as many lines of code.
int numDays = 5;
myDate = [myDate dateByAddingTimeInterval:60*60*24*numDays];
+ (id)dateWithTimeInterval:(NSTimeInterval)seconds sinceDate:(NSDate *)date
That should give you what you're looking for.

How to store and compare time data objects

This should be really simple!
I have a shop, it opens at 8:30 and closes at 17:00. I want my app to say the shops current open or currently closed.
Whats the best way to store my open_time and close_time? Store them as seconds since the start of the day, i.e. 30600 and 63000?
This make sense, but how do I get the current time right now, in seconds since the begining of today, so I can check if current_time is between open_time and close_time, i.e. open!!
Thanks in advance!
This problem isn't quite as trivial as you may think. You have to work with dates very carefully. The best solution is to store all of your open and close times as dates. Here is some code for creating your open/close times and comparing them:
NSDate * now = [NSDate date];
NSCalendar * calendar = [NSCalendar currentCalendar];
NSDateComponents * comps = [calendar components:~(NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:now];
[comps setHour:8];
[comps setMinute:30];
NSDate * open = [calendar dateFromComponents:comps];
[comps setHour:17];
[comps setMinute:0];
NSDate * close = [calendar dateFromComponents:comps];
if ([now compare:open] == NSOrderedDescending && [now compare:close] == NSOrderedAscending) {
// The date is within the shop's hours.
}
else {
// The date is not within the shop's hours.
}
Here's what I did:
Grab the current date.
Get the components of the date, except hours, minutes, and seconds.
Set the hour and minutes.
Create an open time.
Repeat steps 3-4 for close time.
Compare open and close times to now.
If you ever need to do any modification of dates, you should always use NSCalendar and NSDateComponents. Check out this answer for why it's so important.
I think a clearer solution would be to use NSDate objects with only hour/minute components present.
Basically, somewhere in your app you need to store the shop's open/close times as such:
NSCalendar *calendar = [[NSCalendar alloc]
initWithCalendarIdentifier: NSGregorianCalendar];
NSDateComponents *openTime = [[NSDateComponents alloc] init];
[openTime setHour: 12];
[openTime setMinute: 30];
NSDate *openDate = [calendar dateFromComponents: openTime];
[calendar release];
And if you need to see whether the current time is between two such NSDate objects you could have a method like this:
- (BOOL)currentTimeIsInBetween: (NSDate *)date1 andDate: (NSDate *)date2 {
NSCalendar *calendar = [[NSCalendar alloc]
initWithCalendarIdentifier: NSGregorianCalendar];
NSDateComponents *currentComponents = [calendar components:
(NSMinuteCalendarUnit | NSHourCalendarUnit)
fromDate: [NSDate date]];
NSDate *currentAdjusted = [calendar dateFromComponents: currentComponents];
[calendar release];
if ([currentAdjusted compare: date1] == NSOrderedAscending)
return NO;
if ([currentAdjusted compare: date2] == NSOrderedDescending)
return NO;
return YES;
}
EDIT: Seems like user rbrown was a bit faster than me, we are suggesting the same approach.
You can do something like this.
NSDate *today = // code for getting today date at 0 oclock
NSDate *now = [NSDate date];
double second = [now timeIntervalSinceDate:today];
Now you got time in second since the start of the day for compare.

Iphone SDK: How to Countdown from current time to a specific day in the future

I have was wondering how it would be possible to show the specific time remaining for a date in the future.
This is what i have so far. I can get the current time, and display that, and i used the minutes to midnight tutorial to figure out how to find out what time it will be midnight. But i am suck as to finding out how i would pick a day in the future and find out how much time is left there.
code:
NSDate* now = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComponents = [gregorian components:(NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:now];
NSInteger hour = 23 - [dateComponents hour];
NSInteger minute = 59 - [dateComponents minute];
NSInteger second = 59 - [dateComponents second];
[gregorian release];
countdownLabel.text = [NSString stringWithFormat:#"%02d:%02d:%02d", hour, minute, second];
Any possibility someone could take a look at this and change it up a bit? It would be easiest for me to follow if i could understand it from using some of this code, but if thats not possible or correct I would rather know the right way to do it.
Thanks.
Try one of these, it will be number of second between the two dates in double:
NSTimeInterval timeBetweenThenAndNow = [futureDate timeIntervalSinceNow];
NSTimeInterval timeBetweenThenAndMidnight = [futureDate timeIntervalSinceDate: myMidnightDate];

dateByAddingComponents and getting difference of dates with NSDateComponents

I am having problems with adding values to dates and also getting differences between dates.
The dates and components calculated are incorrect.
So for adding, if I add 1.5 months, I only get 1 month, however if I add any whole number ie (1 or 2 or 3 and etc) it calculates correctly.
Float32 addAmount = 1.5;
NSDateComponents *components = [[[NSDateComponents alloc] init] autorelease];
[components setMonth:addAmount];
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[gregorian setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
NSDate *newDate2 = [gregorian dateByAddingComponents:components toDate:Date1 options:0];
Now for difference, if I have a date that has been added with exactly one year (almost same code as above), it adds correctly, but when the difference is calculated, I get 0 years, 11 months and 30 days.
NSDate *startDate = Date1;
NSDate *endDate = Date2;
NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents *components = [gregorian components:unitFlags
fromDate:startDate
toDate:endDate options:0];
NSInteger years = [components year];
NSInteger months = [components month];
NSInteger days = [components day];
What am I doing wrong? Also I have added the kCFCalendarComponentsWrap constanct in the options for both adding and difference functions but with no difference.
Thanks
So for adding, if I add 1.5 months, I
only get 1 month, however if I add any
whole number ie (1 or 2 or 3 and etc)
it calculates correctly.
The setMonth: method in NSDateComponents takes an NSInteger, not a floating point number. So the behaviour is correct, as it's simply truncating the 1.5 to 1.
Now for difference, if I have a date
that has been added with exactly one
year (almost same code as above), it
adds correctly, but when the
difference is calculated, I get 0
years, 11 months and 30 days.
Unless you show the code for how your Date1 and Date2 variables are created, there's no real way to tell.
(You're also leaking memory above; always match an alloc with a release/autorelease. And try not to give your variables capital letters, since as a matter of style, that should only be done for class names)
I finally found the problem with the date difference, when I was saving it to a db, I used timeintervalsince1970 with an double value, but when populating it and setting it to a datepicker, I was using a int column type.
Thanks Shaggy Frog for steering me in the right direction.