如何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]可能正是你所需要的。