Incorrect time string with NSDateFormatter with Da

2019-08-10 04:22发布

问题:

I'm using CET as my default timezone in my application. In Germany Daylight saving time will start from 29 March 2015. Now I'm doing some testing to see if my dates will be converted correctly in the future. I used following code to test it.

NSTimeZone *timeZone = [NSTimeZone timeZoneWithAbbreviation:@"CET"];
[NSTimeZone setDefaultTimeZone:timeZone];

NSDate *now = [NSDate date]; // 10 Feb, 2015
NSDate *dateAfter29March = [now dateByAddingMonths:2]; // 10 April, 2015

NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss ZZZ"];
[formatter setTimeZone:timeZone];

NSLog(@" NOW DATE -> %@",[formatter stringFromDate:now]);
NSLog(@" DATE AFTER 29 March -> %@",[formatter stringFromDate:dateAfter29March]);

The category method for adding months is

- (NSDate *)dateByAddingMonths:(NSInteger) dMonths {
    NSDateComponents *components = [[NSDateComponents alloc] init];
    components.month = dMonths;
    NSCalendar *calendar = [NSCalendar currentCalendar];
    return [calendar dateByAddingComponents:components toDate:self options:0];
}

The Output of Console is as follows:

NOW DATE -> 2015-02-10 18:28:05 +0100
DATE AFTER 29 March -> 2015-04-10 18:28:05 +0200

Where my expected time on date after 29 March was 17:28:05 as One hour in now less after daylight time. What am I doing wrong?

PS: I also tried to set the output strings in a label and no change occurs.

回答1:

I really don't see what you are complaining about.

You asked the OS to add two months to a date. And adding two months means that it uses the calendar, and returns a date that is two months later, on the same day of the month, at the same time. It should take changes in DST into account, and it does. That's what adding two months means.

Imagine we have a meeting that we always do at the first of the month, at 10am in the morning. You want that meeting at 10am in the morning, whether it is DST or not.



回答2:

Try this;

[NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

NSDate *now = [NSDate date]; // 10 Feb, 2015
NSDate *dateAfter29March = [self dateByAddingMonths:2 date:now]; // 10 April, 2015


NSLog(@"now -> %@", now);
NSLog(@"dateAfter29March -> %@", dateAfter29March);
NSLog(@"========================================");


NSTimeZone *timeZone = [NSTimeZone timeZoneWithAbbreviation:@"CET"];

NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss ZZZ"];
[formatter setTimeZone:timeZone];

NSLog(@"NOW DATE -> %@",[formatter stringFromDate:now]);
NSLog(@"DATE AFTER 29 March -> %@",[formatter stringFromDate:dateAfter29March]);

It worked fine for me. See the logs

now -> 2015-02-11 06:01:56 +0000
dateAfter29March -> 2015-04-11 06:01:56 +0000
========================================
NOW DATE -> 2015-02-11 07:01:56 +0100
DATE AFTER 29 March -> 2015-04-11 08:01:56 +0200


回答3:

You can use nextDaylightSavingTimeTransition to find out when is the next daylight saving time transition for your time zone:

NSTimeZone(abbreviation: "CET")!.nextDaylightSavingTimeTransition!   // "Mar 28, 2015, 22:00"

You can create a NSDateFormatter extension to format your time using the CET time zone as follow:

extension NSDate {
    var time:String {
        let formatter = NSDateFormatter()
        formatter.timeZone = NSTimeZone(abbreviation: "CET")
        formatter.dateFormat = "HH:mm"
        return formatter.stringFromDate(self)
    }
}



let futureDate = NSCalendar.currentCalendar().dateWithEra(1, year: 2015, month: 3, day: 27, hour: 13, minute: 0, second: 0, nanosecond: 0)!
let futureDateDLST = NSCalendar.currentCalendar().dateWithEra(1, year: 2015, month: 3, day: 29, hour: 13, minute: 0, second: 0, nanosecond: 0)!

futureDate.time      // 17:00"
futureDateDLST.time  // "18:00"