S.J S.J - 4 months ago 24
Objective-C Question

NSDate for the same local time as another NSDate in different time zones

I have a date time:

Friday, July 8, 2016 at 6:30:00 PM India Standard Time


I want a date time like this:

Friday, July 8, 2016 at 6:30:00 PM Central Daylight Time


I do not want to convert the dates using
NSDateFormatter
from one timezone to another, because this conversion will give me

Friday, July 8, 2016 at 8:00:00 AM Central Daylight Time


I want the same date time, but in a different timezone. How can I do that?

Answer

dasblinkenlight's answer is absolutely correct, but I'll explain a little more fully, and show the steps in ObjC instead of Swift. You don't want the same date in a different time zone; you want the same local time (or wall time): what a person would see when they look at the clock.

The same date in a different zone has a different local time: Friday, July 8, 2016 at 6:30:00 PM India Standard Time is Friday, July 8, 2016 at 8:00:00 AM Central Daylight Time. At the moment that a person in Kolkata looks at the clock and sees 6:30 PM, a person in Omaha looking at her clock would see 8:00 AM.

First, solely for the purpose of the demonstration, we'll create the original date from the string you've given, using a date parser. You would just get the date from your notification object.

NSString * originalDateString = @"Friday, July 8, 2016 at 6:30:00 PM India Standard Time";

NSDateFormatter * parser = [NSDateFormatter new];
[parser setDateStyle:NSDateFormatterFullStyle];
[parser setTimeStyle:NSDateFormatterFullStyle];
NSDate * originalDate = [parser dateFromString:originalDateString];

Although the original string contained time zone information, the date does not. The wall time, however, is dependent on the zone, so we will need a time zone object for the next step. You would get this too from your notification object. Here, we'll just create one from its identifier: NSTimeZone * originalTZ = [NSTimeZone timeZoneWithAbbreviation:@"IST"];

We need an NSDateComponents to represent the local time values for the date in the zone. NSCalendar is responsible for breaking the date into those components:

NSCalendar * cal = [NSCalendar currentCalendar];
NSDateComponents * localComps;
localComps = [cal componentsInTimeZone:originalTZ
                              fromDate:originalDate];

If you inspect localComps now, you'll see the values encapsulate the wall time you expect: 2016-07-08 18:30:00. Now that we have these values broken out, we can essentially put them into another zone without translation. That is, without reinterpreting the values as an absolute time, which would give 2016-07-08 08:00:00, as discussed above. Simply change the time zone (again, you would get the zone from your notification rather than from its abbreviation):

NSTimeZone * destinationTZ = [NSTimeZone timeZoneWithAbbreviation:@"CDT"];
[localComps setTimeZone:destinationTZ];

Now the calendar can construct a new date object from those components. NSDate * destinationDate = [cal dateFromComponents:localComps];*

This date is the time-zone-independent moment in time that represents Friday, July 8, 2016 at 6:30:00 PM Central Daylight Time. As with any NSDate, if you simply print it out, it will be formatted in GMT. If you want to see the wall time values you expect, you need to use a formatter set to the destination time zone.


*As dasblinkenlight's answer shows, localComps has an associated calendar, so it's capable of creating a date itself, too. I thought this would be a bit clearer.