objective-c: Conversion between TimeZones

2019-04-16 11:07发布

I'm having problems converting a time string to a different timeZone.

This is a string example as the one I retrieve from a server: @"5/14/2013 4:19am"

I know the timeZone is America/New_York and I want to convert it to my local timeZone: Europe/Rome.

Here is the code I wrote to test it:

-(void) convertTimeZone
{   

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    dateFormatter.timeZone=[NSTimeZone timeZoneWithAbbreviation:@"GMT"];
    dateFormatter.dateFormat=@"MM/dd/yyyy HH:mma";
    NSString *sourceDateString = @"5/14/2013 4:19am";

    NSTimeZone *sourceTimeZone = [[NSTimeZone alloc] initWithName:@"America/New_York"];
    NSTimeZone *destinationTimeZone = [NSTimeZone localTimeZone];
    NSInteger sourceGMToffset = sourceTimeZone.secondsFromGMT;
    NSInteger destinationGMToffset = destinationTimeZone.secondsFromGMT;
    NSInteger interval = destinationGMToffset-sourceGMToffset;


    NSLog(@"DateFormatter TimeZone: %@", dateFormatter.timeZone);
    NSLog(@"Source TimeZone: %@", sourceTimeZone);
    NSLog(@"Destination TimeZone: %@", destinationTimeZone);
    NSLog(@"sourceOffset: %u destinationOffset: %u interval: %u", sourceGMToffset, destinationGMToffset, interval);

    NSLog(@"----------------------------------------------------------------------------------------------------");

    NSLog(@"sourceDateString : %@", sourceDateString);

    //Convert from string to date

    NSDate *dateObject = [dateFormatter dateFromString:sourceDateString];
    NSLog(@"From string to date: %@", dateObject);

    //Now back from date to string
    NSLog(@"From date to string: %@", [dateFormatter stringFromDate:dateObject]);

    //Convert from sourceTimeZone to destinationTimeZone
    NSDate *newDate = [[NSDate alloc] initWithTimeInterval: interval sinceDate:dateObject];     
    NSLog(@"destinationDateString: %@", [dateFormatter stringFromDate: newDate]);

}

output:


DateFormatter TimeZone: GMT (GMT) offset 0

Source TimeZone: America/New_York (GMT-04:00) offset -14400 (Daylight)

Destination TimeZone: Local Time Zone (Europe/Rome (CEST) offset 7200 (Daylight))

sourceOffset: 4294952896 destinationOffset: 7200 interval: 21600

sourceDateString : 5/14/2013 4:19am

From string to date: 2013-05-14 00:19:00 +0000

From date to string: 05/14/2013 00:19AM

destinationDateString: 05/14/2013 06:19AM


I'm getting mad!!!!

If the original date is "5/14/2013 4:19am" WHY the date formatter changes it to "05/14/2013 00:19AM" ????????? If it had saved correctly it to "05/14/2013 04:19AM +0000" the conversion would be perfect (6 hours between the two TimeZones)!!!

Any hint?

Thanks

Nicola


EDIT

By setting these parameters as suggested by lithu-t-v:

dateFormatter.timeZone=[NSTimeZone timeZoneWithAbbreviation:@"GMT"];

NSString *sourceDateString = @"5/14/2013 4:19am -0400";

It seems to work but it doesn't.

I say "it seems" because:

sourceDateString: 5/14/2013 04:19am -0400

From string to date: 2013-05-13 04:19:00 +0000 (OK)

From date to string: 05/14/2013 04:19 (OK)

destinationDateString: 05/14/2013 10:19 (CORRECT)

is correct BUT if I try with a late pm hour:

sourceDateString: 5/14/2013 10:19pm -0400

From string to date: 2013-05-14 16:19:00 +0000 (WRONG)

From date to string: 05/14/2013 16:19

destinationDateString: 05/14/2013 22:19 (WRONG!)

The correct result should be: 05/15/2013 04:19AM

3条回答
Explosion°爆炸
2楼-- · 2019-04-16 11:14

Thank you all for all your hints! After a lot of trials I ended writing a function that uses DateComponent:

-(NSString*) convertToLocalTimeZoneDate: (NSString*) sourceDate
                                   Time: (NSString*) sourceTime
{
    //Date and time parsing

    //Expected: NSString *sourceDate = @"5/13/2013";
    //Expected: NSString *sourceTime = @"04:19am";

    NSArray *dateArray = [sourceDate componentsSeparatedByString:@"/"];
    NSDateComponents *dateComps = [[NSDateComponents alloc] init];
    dateComps.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    dateComps.year = [[dateArray objectAtIndex:2] integerValue];
    dateComps.month = [[dateArray objectAtIndex:0] integerValue];
    dateComps.day = [[dateArray objectAtIndex:1] integerValue];

    NSString *timeInDay = [sourceTime substringFromIndex:5];

    if ([[timeInDay uppercaseString] isEqualToString:@"PM"]){
        dateComps.hour = 12 + [[sourceTime substringWithRange:NSMakeRange(0, 2)] integerValue];
    } else {
        dateComps.hour = [[sourceTime substringWithRange:NSMakeRange(0, 2)] integerValue];
    }

    dateComps.minute = [[sourceTime substringWithRange:NSMakeRange(3, 2)] integerValue];
    dateComps.timeZone = [[NSTimeZone alloc] initWithName:@"America/New_York"];

    NSLog(@"Original Date Time: %@ %@", sourceDate, sourceTime);
    NSLog(@"GMT Date Time: %@", dateComps.date);

    //Return a string with the local date and time

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    dateFormatter.locale = [NSLocale currentLocale];
    dateFormatter.dateStyle = NSDateFormatterShortStyle;
    dateFormatter.timeStyle = NSDateFormatterLongStyle;

    return[dateFormatter stringFromDate:dateComps.date];
}

It returns the right output:

Original Date Time: 5/13/2013 04:19am (I know it is America/New_York)

GMT Date Time: 2013-05-13 08:19:00 +0000 (OK)

Output: 13/05/13 10:19:00 CEST

查看更多
迷人小祖宗
3楼-- · 2019-04-16 11:20

This is what you want:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat=@"dd/MM/yyyy h:mma"; // Note:  lower case h
NSString *sourceDateString = @"14/5/2013 5:19am"; // in New Yourk

NSTimeZone *sourceTimeZone = [[NSTimeZone alloc] initWithName:@"America/New_York"];
NSTimeZone *destinationTimeZone = [NSTimeZone localTimeZone];
dateFormatter.timeZone = sourceTimeZone;

NSLog(@"DateFormatter TimeZone: %@", dateFormatter.timeZone);
NSLog(@"Source TimeZone: %@", sourceTimeZone);
NSLog(@"Destination TimeZone: %@", destinationTimeZone);

NSLog(@"---------------------------------------");

NSLog(@"sourceDateString : %@", sourceDateString);

 //Convert from string to date

NSDate *dateObject = [dateFormatter dateFromString:sourceDateString];
NSLog(@"From string to date should be in GMT: %@", dateObject);

//Convert from sourceTimeZone to destinationTimeZone
dateFormatter.timeZone = destinationTimeZone;
NSLog(@"Hopefully works: %@", [dateFormatter stringFromDate: dateObject]);

Output:

Test Case '-[TestOrderedList testFoo]' started.
2013-05-15 11:44:04.263 LogicSim[43238:303] DateFormatter TimeZone: America/New_York (EDT) offset -14400 (Daylight)
2013-05-15 11:44:04.264 LogicSim[43238:303] Source TimeZone: America/New_York (EDT) offset -14400 (Daylight)
2013-05-15 11:44:04.264 LogicSim[43238:303] Destination TimeZone: Local Time Zone (Europe/London (BST) offset 3600 (Daylight))
2013-05-15 11:44:04.264 LogicSim[43238:303] ---------------------------------------
2013-05-15 11:44:04.264 LogicSim[43238:303] sourceDateString : 14/5/2013 5:19am
2013-05-15 11:44:04.265 LogicSim[43238:303] From string to date should be in GMT: 2013-05-14 09:19:00 +0000
2013-05-15 11:44:04.265 LogicSim[43238:303] Hopefully works: 14/05/2013 10:19AM
Test Case '-[TestOrderedList testFoo]' passed (0.002 seconds).

The first mistake you made was in the date format specification HH is the time in 24 hour format. When used with the am/pm specifier, this seems to always parse the hour as 0. Your test data coincidentally had an hour of 4 which is the same as the offset between GMT and New York, leading you to assume it was just getting the offset wrong. I changed the hour specifier to h to get 12 hour time. I also changed the test time, to make it less confusing.

The second mistake was to mess about with the NSDate object, that way lies insanity. NSDates are always in GMT. Do not add or subtract time intervals to correct for time zones, just set the time zone in the date formatter appropriately. So my code sets the time zone to New York and parses the date. It then sets the time zone to local time (I'm in the UK on BST) and stringifies the date. Job done.

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-04-16 11:33

The +0000 component defines the date with the time zone and provide with actual time .A NSDate object include this part also.since that part is missing the time difference comes,with respect to local time zone

Here you ceated the timezone but is not set to the datefromatter.Set timezone to the dateformatter and try it

Append +0000 to the string and do the remaining.It will work

查看更多
登录 后发表回答