如何pipe理不同时区的NSDate

我已经在我的应用程序中创build了一个日历,用这种方式使用date对象:

NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSDateComponents *weekdayComponents = [gregorian components:(NSDayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSMinuteCalendarUnit)fromDate:[NSDate date]]; NSInteger day = [weekdayComponents day]; NSInteger month = [weekdayComponents month]; NSInteger year = [weekdayComponents year]; m_dateFormatter.dateFormat = @"dd/MM/yyyy"; [gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; NSDateComponents *timeZoneComps=[[NSDateComponents alloc] init]; [timeZoneComps setDay:day]; [timeZoneComps setMonth:month]; [timeZoneComps setYear:year]; [timeZoneComps setHour:00]; [timeZoneComps setMinute:00]; [timeZoneComps setSecond:01]; m_currentDate = [gregorian dateFromComponents:timeZoneComps]; 

当用户下个月想去的时候,我会突出显示那个月的第一个date。 那么在这种情况下,date将是1-06-2014,00:00:01。

这里是代码:

  - (void)showNextMonth { // Move the date context to the next month NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSDateComponents *dateComps = [[NSDateComponents alloc] init]; [dateComps setMonth:1]; m_currentMonthContext =[gregorian dateByAddingComponents:dateComps toDate:m_currentMonthContext options:0]; NSDateComponents *weekdayComponents1 = [gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit) fromDate:m_currentMonthContext]; NSInteger nextMonth = [weekdayComponents1 month]; NSInteger nextyear = [weekdayComponents1 year]; NSDateComponents *weekdayComponents2 = [gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit) fromDate:m_currentDate]; NSInteger currentDay = [weekdayComponents2 day]; NSInteger currentMonth = [weekdayComponents2 month]; NSInteger currentYear = [weekdayComponents2 year]; NSInteger selectedDay = 1; if(nextMonth == currentMonth && nextyear == currentYear) { selectedDay = currentDay; } NSInteger month = nextMonth; [gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; NSDateComponents *timeZoneComps=[[NSDateComponents alloc] init]; [timeZoneComps setDay:selectedDay]; [timeZoneComps setMonth:month]; [timeZoneComps setYear:nextyear]; [timeZoneComps setHour:00]; [timeZoneComps setMinute:00]; [timeZoneComps setSecond:01]; m_currentMonthContext =[gregorian dateFromComponents:timeZoneComps]; [self createCalendar]; } 

在上述方法的倒数第二行计算m_currentMonthContext ,其值为1-06-2014,00:00:01。

createCalendar实现:

 -(void)createCalendar { NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSDateComponents *weekdayComponents = [gregorian components:(NSDayCalendarUnit | NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit)fromDate:m_currentMonthContext]; NSInteger month = [weekdayComponents month]; NSInteger year = [weekdayComponents year]; } 

在这里,我得到的2014年的month为5年,但是date是2014年1月6日。 这只发生在美国时区,在所有其他时区工作正常。

所以我想知道如何有效地处理时区,或者从其他的angular度来看,如何确保即使时区改变,NSDate也不会改变。

最接近的原因是在计算date和date组件时,日历中的时区不一致。 有时您将时区设置为UTC,有时候不会,这会导致不一致,因为有时会使用当地时间的偏移量,有时不会。

详细地说,在你的情况下, m_currentMonthContext是一个NSDate ,表示2014年6月1日午夜之后的一秒钟的UTC时间。在你的createCalendar方法中,你创build一个用户本地时间的日历,并计算这样的组件一个约会。 在美国的所有时区,UTC仍然是2014年6月1日午夜之后的一个月。 代码示例,可以单独运行:

  NSCalendar *utcCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; [utcCalendar setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; NSCalendar *localCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSDate *june = [NSDate dateWithTimeIntervalSince1970:1401580801]; NSDateComponents *utcComponents = [utcCalendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:june]; NSDateComponents *localComponents = [localCalendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:june]; NSLog(@"utc : %@", utcComponents); NSLog(@"local: %@", localComponents); 

在MDT时区,这里logging:

utc:日历年份:2014月份:6闰月:无date:1

本地:历年:2014月:5闰月:无日:31

回顾一下,在内存中保留的date是以UTC时间表示的某个日历date,然后计算用户当地时间的日历date,但似乎您错误地期望不同时区的日历将以相同的方式解释相同的date。

那么该怎么办? 你的例子是相当复杂的,但似乎没有必要在UTC时区有时存储date组件,有时不一致。 现在,在我看来,如果你只是想find下个月的第一天,你的代码可能会简单得多:

  NSCalendar *cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; NSDateComponents *comps = [cal components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:[NSDate date]]; [comps setMonth:[comps month] + 1]; [comps setDay:1]; 

我于2014年12月15日进行了testing,并于当地时间于2015年1月1日创build。 希望这是一贯的行为。

综上所述 – 这很可能是一个错误,为date组件计算不使用一致的日历。 有时有UTC和有时本地会导致你的噩梦。 看来你应该总是在当地时间计算,但我不知道你的申请的整个背景,所以不能为此做一个总括的陈述。 另外,不要依赖date和date组件之间的不断转换,而是要让date组件成为您的真实来源。 也就是说,我认为将date组件转换为总是存储在实例variables中的date似乎有些复杂,但是随后在每次使用date组件时立即将date转换回date组件 – 似乎最好只使用date组件尽可能。

从评论,我希望我正确地理解你的问题。 你可以试试这个代码:

 NSDate * nowDate = [NSDate date]; NSLog(@"nowDate: %@",nowDate); NSDateFormatter *df = [NSDateFormatter new]; [df setDateFormat:@"dd/MM/yyyy HH:mm"]; df.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:[NSTimeZone localTimeZone].secondsFromGMT]; NSString *localDate = [df stringFromDate:nowDate]; NSLog(@"localDate: %@", localDate); 

输出:

2014-05-24 23:03:06.205 TestTimeZone [10214:60b] nowDate:2014-05-24 15:03:06 +0000

2014-05-24 23:03:06.209 TestTimeZone [10214:60b] localDate:24/05/2014 23:03

[NSDate date]总是返回GMT + 0的date,不pipe你的时区在哪里。 可能只是用这个? 在同一时间我使用NSDateFormatter设置为我的本地date基于我的笔记本电脑。 在模拟器上运行上述代码时,您可以尝试在Mac上更改为几个不同的时区。 [NSDatedate]可能正是你所需要的。