BC 时代的 NSCalendar 问题

发布于 2024-09-18 22:51:40 字数 1221 浏览 14 评论 0原文

您好,

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

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

问题是,当你尝试将 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。

顺便说一句:方法 [NSCalendar Components:fromDate:toDate:options:] 不允许我们计算 BC 时代年份之间的差异...额外的“为什么?”在这个潘多拉魔盒里。

PS:我正在挖掘官方文档和其他资源,但没有发现任何有关此问题的信息(或者也许它的目的是这样,而我是个白痴?)。

Greetings,

Recently I faced a big problem (as it seems to me) with NSCalendar class.

In my task I need to work with a large time periods starting from 4000BC to 2000AD (Gregorian calendar). In some place I was forced to increment some NSDate by 100 year interval. When incrementing the years in AD timeline (0->...) everything worked fine, but when I tried the same thing with BC i was a little confused.

The problem is, when you try to add 100 years to 3000BC [edited] year, you get 3100BC [edited] no matter what... Personally i found it strange and illogical. The right result should be 2900BC.

Here is the code sample for you to see this "not right" behavior:

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]);

What can you say about this behavior? Is this how it should work or is this a bug? I'm confused :S.

BTW.: the method [NSCalendar components:fromDate:toDate:options:] doesn't allow us to calculate the difference between years in BC era... additional 'WHY?' in this Pandora's box.

P.S.: I was digging through official documentation and other resources but found nothing regarding this problem (or maybe it's intended to work so and I'm an idiot?).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

却一份温柔 2024-09-25 22:51:40

我找到了解决此错误的简单方法。
就是这样:

@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

如果您想知道为什么我只反转年份,答案很简单,除了年份之外的所有其他组件都以正确的方式递增和递减(我没有对它们进行全部测试,但月份和日期似乎工作正常)。

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

I found a simple workaround for this bug.
Here it is:

@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

If you wonder why I invert only years, the answer is simple, every other component except years is incrementing and decrementing in the right way (I haven't tested them all, but months and days seem to work fine).

EDIT: removed mistakenly added autorelease, thanks John.

忆悲凉 2024-09-25 22:51:40

这是一个错误和/或一个功能。 Apple 文档从未说明向日历日期添加组件的含义。他们可以完全自由地将“添加组件”定义为 BCE 日期,就像添加到年份组件一样。

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

您需要使用 -timeIntervalSince1970NSDate 转换

  • 为 UNIX 纪元 (1.1.1970) 的秒数,
  • 使用 -timeIntervalSince1970 将您的 NSDate 转换为 OS X 纪元 (1.1.2001) 的秒数code>-timeIntervalSinceReferenceDate

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

It's a bug and or a feature. The Apple doc never says what they mean by adding components to the calendrical date. It's perfectly free for them to define "adding a component" to the BCE date as just the addition to the year component.

Yes I agree with you that it's counterintuitive and I think it's a bug.

You need to convert your NSDate to either

  • the second from the UNIX epoch (1.1.1970) using -timeIntervalSince1970
  • the second from the OS X epoch (1.1.2001) using -timeIntervalSinceReferenceDate

You can then perform the calculation, and convert it back to an NSDate. I think it's a bad idea to work in the Gregorian calendar all the time... It would be better to convert to the Gregorian calendar just before you show it on the GUI.

不回头走下去 2024-09-25 22:51:40

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

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

Imagine that you have Date with 1st moment of our era - AD 0001-01-01 00:00:00. What was the moment before? BC 0001-01-01 00:00:01. If Cocoa developers used basic arithmetic's for this task, you would get AD 0000-12-31 23:59:59. Is that reasonable for Gregorian calendar? I guess not. So, it seems to me that the most convenient way to implement calendar was to use Era flag, and change "time direction" when dealing with BC era to get human-readable dates in every case.

BTW.: [NSCalendar dateByAddingComponents:toDate:options:] really behaves strange and is unable to count time interval between BC dates, I checked too. So, for BC dates you may use workaround, e.g. by translating dates to AD and then finding diff.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文