NSDateComponents组件:从日期和时区获取

11

我有一个方法,它通过将NSDate分解为其NSDateComponents来提取小时和秒组件。我的代码如下...

unsigned hourAndMinuteFlags = NSHourCalendarUnit | NSMinuteCalendarUnit;
NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
NSDateComponents* travelDateTimeComponents = [calendar components:hourAndMinuteFlags fromDate:travelDate];
NSString* hours = [NSString stringWithFormat:@"%02i", [travelDateTimeComponents hour]];
NSString* minutes = [NSString stringWithFormat:@"%02i", [travelDateTimeComponents minute]];

我的问题是转换时少了一个小时。我怀疑这是由于时区导致的,但就我所看到的,传入的日期和使用的日历都是GMT。

例如,如果我传入以下NSDate对象(这是[NSDate description]的日志)...

2010-08-02 08:00:00 +0100

我希望小时数为8,分钟数为0,但实际上我的小时数返回了7。

我的系统时间是GMT,因此上面的NSDate目前是英国夏令时的+0100。

3个回答

15

请记住,NSDate对象始终按照GMT显示时间,而您从该日期中提取的组件会受到timeZoneWithName部分的影响。

例如:

NSDate *today = [NSDate date];
NSLog(@"Today's date: %@",today);

unsigned hourAndMinuteFlags = NSHourCalendarUnit | NSMinuteCalendarUnit;
NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
NSDateComponents* travelDateTimeComponents = [calendar components:hourAndMinuteFlags fromDate:today];
NSString* hours = [NSString stringWithFormat:@"%02i", [travelDateTimeComponents hour]];
NSString* minutes = [NSString stringWithFormat:@"%02i", [travelDateTimeComponents minute]];

NSLog(@"Calendar: %@",calendar);
NSLog(@"Travel Components: %@",travelDateTimeComponents);
NSLog(@"Hours: %@",hours);
NSLog(@"Minutes: %@",minutes);

控制台输出:

[Session started at 2011-01-23 12:59:17 -0700.]
2011-01-23 12:59:19.755 testttt[5697:207] Today's date: 2011-01-23 19:59:19 +0000
2011-01-23 12:59:19.756 testttt[5697:207] Calendar: <__NSCFCalendar: 0x4b2ca70>
2011-01-23 12:59:19.757 testttt[5697:207] Travel Components: <NSDateComponents: 0x4b2de20>
2011-01-23 12:59:19.757 testttt[5697:207] Hours: 19
2011-01-23 12:59:19.758 testttt[5697:207] Minutes: 59

如果我们改变时区...

NSDate *today = [NSDate date];
NSLog(@"Today's date: %@",today);

unsigned hourAndMinuteFlags = NSHourCalendarUnit | NSMinuteCalendarUnit;
NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone timeZoneWithName:@"MST"]];
NSDateComponents* travelDateTimeComponents = [calendar components:hourAndMinuteFlags fromDate:today];
NSString* hours = [NSString stringWithFormat:@"%02i", [travelDateTimeComponents hour]];
NSString* minutes = [NSString stringWithFormat:@"%02i", [travelDateTimeComponents minute]];

NSLog(@"Calendar: %@",calendar);
NSLog(@"Travel Components: %@",travelDateTimeComponents);
NSLog(@"Hours: %@",hours);
NSLog(@"Minutes: %@",minutes);

输出:

2011-01-23 13:05:29.896 testttt[5723:207] Today's date: 2011-01-23 20:05:29 +0000
2011-01-23 13:05:29.897 testttt[5723:207] Calendar: <__NSCFCalendar: 0x4e10020>
2011-01-23 13:05:29.897 testttt[5723:207] Travel Components: <NSDateComponents: 0x4e0f6d0>
2011-01-23 13:05:29.898 testttt[5723:207] Hours: 13
2011-01-23 13:05:29.898 testttt[5723:207] Minutes: 05

注意本地的小时和分钟变化了,但是“今天的日期:”部分仍然反映着GMT。如果你问我,这有点误导程序员。


5
我同意aqua的观点。这个问题非常误导人,在我对着屏幕苦思冥想的几天里浪费了很多时间。它的工作方式是这样的:[NSDate date]始终返回当前日期。因此,在上面的例子中,它将是2010-08-02 08:00:00。当我们转换为NSDateComponents时,实际时间会丢失并转换成一组整数(小时、年等),不再保留时区信息。
当转换回NSDate对象时,它会获取年、月、日等值,并以GMT为基准给出相对值,因此会丢失小时部分。这只是框架中决定的方式。我的做法如下:
+(NSDateComponents *)getComponentFromDate:(NSDate *)date {

    NSCalendar * gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSWeekdayCalendarUnit | NSDayCalendarUnit;
    NSDateComponents* components = [gregorian components:unitFlags fromDate:date];

    NSTimeZone* timeZone = [NSTimeZone localTimeZone];
    if(timeZone.isDaylightSavingTime) components.hour = ((int)timeZone.daylightSavingTimeOffset/3600);

    [gregorian release];

    return components;
}

这将为我提供一个“纠正过”的组件版本,其舍入到当前日期,当传递给日历以检索日期时,将始终设置为参数日期的00:00:00。


如果我只想将现在的时间与两个没有开放和关闭日期的小时进行比较,测试会失败,因为现在的时间也是由现在的日期前导的,而另外两个时间将使用2000年作为基准年份创建。那么在这种情况下如何进行比较呢? - marciokoko
我认为转换组件->日期会考虑到日历的时区。只要使用相同的日历,您应该能够进行无损的双向转换。 - Steven Kramer

5

事实上,GMT并不等同于英国夏令时。日期2010-08-02 08:00:00 +0100等同于2010-08-02 07:00:00 GMT,所以你应该期望得到7小时的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接