BC时代的NSCalendar问题

时间:2010-09-15 15:33:36

标签: objective-c cocoa nsdate nscalendar

问候,

最近我遇到了NSCalendar类的一个大问题(在我看来)。

在我的任务中,我需要处理从公元前4000年到公元前2000年(格里高利历)的大量时间段。在某些地方,我被迫以100年的间隔增加一些NSDate。当在AD时间轴(0-> ...)中递增年份时,一切正常,但是当我尝试与BC相同的事情时,我有点困惑。

问题是,当你尝试将100年添加到3000BC [编辑]年时,你得到3100BC [编辑]无论如何...我个人发现它很奇怪而且不合逻辑。正确的结果应该是2900BC。

以下是您看到此“不正确”行为的代码示例:

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

// initing
NSDateComponents *comps = [[[NSDateComponents alloc] init] autorelease];
[comps setYear:-1000];
NSDate *date = [gregorian dateFromComponents:comps];

// math
NSDateComponents *deltaComps = [[[NSDateComponents alloc] init] autorelease];
[deltaComps setYear:100];

date = [gregorian dateByAddingComponents:deltaComps toDate:date options:0];

// output
NSString *dateFormat = @"yyyy GG";

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:dateFormat];
NSLog(@"%@", [formatter stringFromDate:date]);

你对这种行为有什么看法?这是它应该如何工作或这是一个错误?我很困惑:S。

BTW:方法[NSCalendar components:fromDate:toDate:options:]不允许我们计算不列颠哥伦比亚省时代之间的差异...另外'为什么?'在潘多拉的盒子里。

P.S。:我正在挖掘官方文档和其他资源,但没有发现任何关于这个问题(或者它可能是这样的,我是个白痴?)。

3 个答案:

答案 0 :(得分:3)

我找到了这个bug的简单解决方法。 这是:

@interface NSCalendar (EraFixes)

- (NSDate *)dateByAddingComponentsRegardingEra:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSUInteger)opts;

@end

@implementation NSCalendar (EraFixes)

- (NSDate *)dateByAddingComponentsRegardingEra:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSUInteger)opts
{
    NSDateComponents *toDateComps = [self components:NSEraCalendarUnit fromDate:date];
    NSDateComponents *compsCopy = [[comps copy] autorelease];

    if ([toDateComps era] == 0) //B.C. era
    {
        if ([comps year] != NSUndefinedDateComponent) [compsCopy setYear:-[comps year]];
    }

    return [self dateByAddingComponents:compsCopy toDate:date options:opts];
}

@end

如果你想知道为什么我只反转几年,答案很简单,除了几年之外的所有其他组件都以正确的方式递增和递减(我没有全部测试过,但是几个月和几天似乎工作正常)。 / p>

编辑:删除了错误添加的自动释放,感谢John。

答案 1 :(得分:0)

想象一下,你有我们这个时代的第一时刻的日期 - AD 0001-01-01 00:00:00。之前的那一刻? BC 0001-01-01 00:00:01 。如果Cocoa开发人员使用基本算术来完成此任务,那么你将获得 AD 0000-12-31 23:59:59 。格里高利历是否合理?我猜不会。因此,在我看来,实现日历最方便的方法是使用Era标志,并在处理BC时代时更改“时间方向”,以便在每种情况下获得人类可读日期。

BTW:[NSCalendar dateByAddingComponents:toDate:options:]确实表现得很奇怪,无法计算BC日期之间的时间间隔,我也查了一下。因此,对于BC日期,您可以使用解决方法,例如通过将日期转换为AD然后找到差异。

答案 2 :(得分:0)

这是一个错误和/或功能。 Apple文档从未说明将组件添加到日历日期的含义。他们完全免费为BCE日期定义“添加组件”,只是添加到年份组件。

是的我同意你的观点,这是违反直觉的,我认为这是一个错误。

您需要将NSDate转换为

  • 来自UNIX纪元(1.1.1970)的第二个使用-timeIntervalSince1970
  • 来自OS X纪元(1.1.2001)的第二个使用-timeIntervalSinceReferenceDate

然后,您可以执行计算,并将其转换回NSDate。我认为一直在格里高利历中工作是个坏主意......最好在你在GUI上显示之前转换为公历。