Converting any date to local date issue - objective-c

I am stuck somewhere in NSDate and NSDateFormatter due to timezone issues.
I need to send time to server only in UTC(which is converted to unix time).
Here is my few steps what I am doing:
Selecting a date from the calender should be added with the current time and converted to UTC.
Compare the selected date with the current date. Just to know whether selected date is a past or future date. (Few other operations are to be done based on past/future/current date).
I have tried this code:
In a Category on NSDate :
-(NSDate *) toLocalTime{
NSDate* sourceDate = self;
NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithName:#"UTC"];
NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate];
NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];
NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;
NSDate* destinationDate = [[NSDate alloc] initWithTimeInterval:interval sinceDate:sourceDate];
return destinationDate;
}
But the problem exists when I try to convert date to local, (at times I am not sure the current time is in local timezone or not). If they are in UTC then the above method works fine.
If the time is already in local timezone then it again adds the interval and I am getting incorrect time.
I am out of ideas, please help me.
Any idea will be highly appreciated.

NSDate represents the time since Jan 1st 1970 in UTC. Never try to pretend it is anything else. Never try to think of an NSDate as being in a particular local time.
So what you need is a date from a calendar + an offset which represents the time since midnight today.
To get today 0:00am UTC, you first need a Gregorian calendar for the UTC time zone.
NSTimeZone* utcTimeZone = [NSTimeZone timeZoneWithName:#"UTC"];
NSCalendar* gregorian = [[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar];
[gregorian setTimeZone: utcTimeZone];
Now you use date components to get the hours, minutes and seconds since midnight UTC
NSUInteger unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDate *date = [NSDate date];
NSDateComponents *comps = [gregorian components: unitFlags fromDate:date];
If you have a date that is midnight UTC on the date from your calendar, you can get midnight UTC + your hours, minutes and seconds like this:
NSDate* theDateIWant = [gregorian dateByAddingComponents: comps
toDate: midnightUTCDateFromCalendar
options: 0];
NSLog(#"The final date is %#", theDateIWant);

Related

NSDatePicker setDate doesn't work as expected

I'm trying to set time on a NSDatePicker according to a certain offset that I have in milliseconds. When I set the time, the value that I see is off and I can't figure out why.
Here is the code I use to set the picker:
- (void)setDatePicker{
int targetmillisondsFromMidnight = [self.schedule.targetHour intValue]; //Value is: 61680000 milliseconds whis is equal to 17:08 UTC (or 19:08 in my local time);
NSDate* todayMidnight = [NSCalendar.currentCalendar startOfDayForDate:[NSDate new]];
NSTimeZone* timezone = [NSTimeZone localTimeZone]; //Value is: Local Time Zone (Asia/Jerusalem (GMT‎+2‎) offset 7200)
NSInteger seconds = [timezone secondsFromGMT]; //Value is: 7200
todayMidnight = [todayMidnight dateByAddingTimeInterval:seconds]; // Value is: 2019-12-25 00:00:00 UTC
NSDate* scheduleDate = [NSDate dateWithTimeInterval:targetmillisondsFromMidnight/1000 sinceDate:todayMidnight]; //Value is: 2019-12-25 17:08:00 UTC
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"]];
NSDateComponents *components = [calendar components:(NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:scheduleDate];
[self.datePicker setDate:[calendar dateFromComponents:components] animated:YES];
}
I stop with a breakpoint at the last command in the function and I print some values, the outputs that I get are:
po [components hour] = 17
po [components minute] = 8
po [calendar dateFromComponents:components] = 001-01-01 17:08:00 +0000
So from my understanding the date is set with 17:08.
What I expect to see on the time picker is 19:08, however what I see is 19:28. I can't figure out where those 20 minutes are coming from.
Try this code. Date picker uses while setting time current timezone and uses UTC offset based on passed date (001-01-01 17:08:00 +0000) and looks for offset in timezone database at this timepoint. Because at that time (year zero) there were no timezones, it could not be found in tz database so time zone offset was calculated based on mean solar time, so you got offset 2:20 (approx.) for your area.
- (void)setDatePicker {
int targetmillisondsFromMidnight = 61680000; //Value is: 61680000 milliseconds whis is equal to 17:08 UTC (or 19:08 in my local time);
NSCalendar *calendar = NSCalendar.currentCalendar;
NSTimeZone* timezone = [NSTimeZone timeZoneWithName:#"Asia/Jerusalem"]; //Value is: Local Time Zone (Asia/Jerusalem (GMT‎+2‎) offset 7200)
calendar.timeZone = timezone;
NSDate* todayMidnight = [calendar startOfDayForDate:[NSDate new]];
NSInteger seconds = [timezone secondsFromGMT]; //Value is: 7200
todayMidnight = [todayMidnight dateByAddingTimeInterval:seconds]; // Value is: 2019-12-25 00:00:00 UTC
NSDate* scheduleDate = [NSDate dateWithTimeInterval:targetmillisondsFromMidnight/1000 sinceDate:todayMidnight]; //Value is: 2019-12-25 17:08:00 UTC
NSDate *date = [scheduleDate dateByAddingTimeInterval:seconds];
self.datePicker.timeZone = [NSTimeZone timeZoneWithAbbreviation:#"UTC"];
[self.datePicker setDate:date animated:YES];
}

NSDate is wrong when doing 'dateFromString'

Im here in the UK and when working with dates in iOS they are always out by one hour (one hour behind), what I need is the correct time from an NSDate. Ive done the following, but i get two different times:
NSDate *today = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm:ss";
[dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
NSLog(#"NSDate %#",today);
NSLog(#"Time %#", [dateFormatter stringFromDate:today]);
NSDate*stringDate = [dateFormatter dateFromString:[dateFormatter stringFromDate:today]];
NSLog(#"Time date %#",stringDate);
Here is what is logged:
NSDate 2015-07-01 16:07:22 +0000
Time 2015-07-01 17:07:22
Time date 2015-07-01 16:07:22 +0000
Why is this happening? Am i missing something obvious? Surely if the string date is correct, then doing dateFromString should yield the correct results?
The reason I need an NSDate is so I can get the correct amount of seconds using [myTime timeIntervalSince1970]
2015-07-01 17:07:22 //is the correct date
Im expecting an NSDate object that is correct to my date and time.
Update
The answers below helped me find where I was going wrong, so I changed my approach, I was able to get the current timestamp doing the following:
NSString *timeStamp = [dateFormatter stringFromDate:[NSDate date]];
NSDate *curdate = [dateFormatter dateFromString:timeStamp];
int unix_timestamp = [curdate timeIntervalSince1970];
NSDate* referenceDate = [NSDate dateWithTimeIntervalSince1970: 0];
NSTimeZone* timeZone = [NSTimeZone systemTimeZone];
int offset = (int)[timeZone secondsFromGMTForDate: referenceDate];
int currentTimestamp = unix_timestamp + offset;
NSLog(#"CUrrent time stamp %d",currentTimestamp);
NSDate is an absolute moment of time, it does not have a timezone. The date object you have is correct: it is exactly the moment that code was executed.
If you need string representation of that moment of time in a specific time zone, use stringFromDate: just like you did.
If you need to know number values of hour/minute in a specific time zone, use -[NSCalendar components:fromDate:].
[calendar setTimeZone:...];
NSDateComponents* components = [calendar components:(NSHourCalendarUnit|NSMinuteCalendarUnit) fromDate:date];
The date formatter defaults to the local timezone. If you want a different timezone specify it. NSLog of a date used the 'NSDatedescription` method that defaults to GMT (UTC).
Examining the code:
NSDate *today = [NSDate date];
// Creates today's data in GMT (UTC) All NSDates are referenced to GMT.
NSLog(#"NSDate %#",today); (moved up for explanation ordering)
// NSDate 2015-07-01 16:07:22 +0000
// Displays the date in GMT
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm:ss";
[dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
// Creates a date for matter with the system timezone
NSLog(#"Time %#", [dateFormatter stringFromDate:today]);
// Time 2015-07-01 17:07:22
// Creates a string representation in the system timezone and displays it
NSDate*stringDate = [dateFormatter dateFromString:[dateFormatter stringFromDate:today]];
// Creates a date from the string taking into the system timezone
NSLog(#"Time date %#",stringDate);
// Time date 2015-07-01 16:07:22 +0000
// Displays the date in GMT.

(iOS, Objective-C) Handling different time zones when calculating dates/times

Edited 07/08/13: Apple has an excellent set of WWDC videos that really helped me understand the various date and time classes in Objective-C, and how to correctly perform time calculations/manipulations with them.
"Solutions to Common Date and Time Challenges" (HD video, SD video, slides (PDF)) (WWDC 2013)
"Performing Calendar Calculations" (SD video, slides (PDF)) (WWDC 2011)
Note: links require a free Apple Developer membership.
In this SO question, I asked how I might go about calculating a certain date and time ("next Sunday at 5 PM"). Thanks to the answers I got, I came up with the following code:
- (NSDate *) toPacificTime
{
NSTimeZone *tz = [NSTimeZone timeZoneWithName:#"America/Los_Angeles"];
NSInteger seconds = [tz secondsFromGMTForDate: self];
return [NSDate dateWithTimeInterval: seconds sinceDate: self];
}
- (void)handleLiveShowReminders
{
NSDate *gmtNow = [NSDate date];
NSDate *now = [gmtNow toPacificTime];
NSCalendar *calendar = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[calendar setTimeZone:[NSTimeZone timeZoneWithName:#"America/Los_Angeles"]];
NSDateComponents *dateComponents = [calendar components:NSWeekdayCalendarUnit fromDate:now];
NSInteger weekday = [dateComponents weekday];
NSInteger daysTillNextSunday = 8 - weekday;
int secondsInDay = 86400; // 24 * 60 * 60
NSDate *nextSunday = [now dateByAddingTimeInterval:secondsInDay * daysTillNextSunday];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:nextSunday];
[components setHour:17];
[components setMinute:00];
[components setTimeZone:[NSTimeZone timeZoneWithName:#"America/Los_Angeles"]];
NSDate *nextSunday5PM = [calendar dateFromComponents:components];
warningInterval = -300; // we want the notification to fire 5 minutes beforehand
NSDate *alertDate = [nextSunday5PM dateByAddingTimeInterval:(NSTimeInterval)warningInterval];
UILocalNotification* notifyAlarm = [[[UILocalNotification alloc] init] autorelease];
if (notifyAlarm)
{
notifyAlarm.fireDate = alertDate;
notifyAlarm.timeZone = [NSTimeZone timeZoneWithName:#"America/Los_Angeles"];
notifyAlarm.repeatInterval = NSWeekCalendarUnit;
notifyAlarm.soundName = #"alert.aif";
notifyAlarm.alertBody = #"LIVE SHOW REMINDER: The live show is about to start!";
[[UIApplication sharedApplication] scheduleLocalNotification:notifyAlarm];
}
}
The problem is that, although this code works for me, it's not working for anyone who's not in the PST timezone, as can be seen by this debug output I received from a user in EST:
number of notifications = 1
Notification #1
===================
Body: LIVE SHOW REMINDER: The live show is about to start!
Details: <UIConcreteLocalNotification: 0x1d5f2200>
{fire date = Sunday, December 9, 2012, 4:30:00 PM Pacific Standard Time,
time zone = America/Los_Angeles (PST) offset -28800,
repeat interval = NSWeekCalendarUnit
repeat count = UILocalNotificationInfiniteRepeatCount,
next fire date = Sunday, December 9, 2012, 4:30:00 PM Eastern Standard Time,
user info = (null)}
What am I doing wrong here?
One problem with your logic, just adding 86,400 to your time interval might not be what you want. If for example, the user experiences daylight-savings, then you will be off by an hour, or even a day if it happens near midnight. A better method is to ask the NSCalendar for the result, which takes into account the users local time zone.
NSDate *start = yourDate;
NSDateComponents *oneDay = [NSDateComponents new];
[oneDay setDay:1];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *end = [cal dateByAddingComponents:oneDay toDate:start options:0];
And NSDate is relative to UTC/GMT. When you "adjust" if for Pacific time you create utter confusion.

How to get local date and time?

I am using NSDate to get current time and date but it is giving date and time based on GMT.Please can anyone know how to get local time?
Thanks in advance!
NSDate has no knowledge of timezones. It always stores dates as absolute moments in time, in GMT.
Use NSDateFormatter to get a local (or any other time zone) representation.
See the pertinent programming guide: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html
[dateFormatter setTimeZone:systemTimeZone];
set the timezone of the date using a date formatter with the above method. It will work. Convert the date to string, set the format and timezone and convert back to date.
Hi I found this from another site
NSDate* sourceDate = [NSDate date];
NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate];
NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];
NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;
NSDate* destinationDate = [[[NSDate alloc] initWithTimeInterval:interval sinceDate:sourceDate] autorelease];
Link for the website is here

Getting date from [NSDate date] off by a few hours

I am using
NSDate *date = [NSDate date];
for getting the date, but the date I get is off by 2 hours.
NSDate objects don't have time zones. They represent an absolute moment in time. However, when you ask one for its description (by printing it with NSLog(), e.g.), it has to pick a time zone. The most reasonable "default" choice is GMT. If you're not in GMT yourself, the date will seem to be incorrect, by the amount of your own offset.
You should always use an NSDateFormatter to create a string for display. The formatter's timezone should be set to yours, which is the default.
You can get your date corrected like this:
NSDate * dateGMT = [NSDate date];
NSTimeInterval secondsFromGMT = [[NSTimeZone localTimeZone] secondsFromGMT];
NSDate * correctDate = [dateGMT dateByAddingTimeInterval:secondsFromGMT];
-(NSDate *)getDateInCurrentSystemTimeZone
{
NSDate* sourceDate = [NSDate date];
NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithAbbreviation:#"GMT"];
NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate];
NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];
NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;
NSDate* destinationDate = [[NSDate alloc] initWithTimeInterval:interval sinceDate:sourceDate];
return destinationDate;
}