2010-09-15 3 views
4

Привет,Проблема NSCalendar с эпохой BC.

Недавно я столкнулся с большой проблемой (как мне кажется) с классом NSCalendar.

В моей задаче мне нужно работать с большими периодами времени, начиная с 4000BC до 2000AD (по григорианскому календарю). В некотором месте я был вынужден увеличить NSDate на 100-летний интервал. При возрастании лет в шкале 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: fromDate: toDate: options:] не позволяет рассчитать разницу между годами эры BC ... дополнительная «ПОЧЕМУ?» в этом ящике Пандоры.

P.S .: Я копал официальную документацию и другие ресурсы, но ничего не нашел по этой проблеме (или, может быть, она предназначена для работы, и я идиот?).

+0

3000AD + 100AD = 3100AD. Это основная арифметика. Из вашего описания математика верна. Я думаю, что внутри вашего кода вы хотите добавить от -1000 до -100, чтобы получить 1100BC, но поскольку вы не показали нам выход из вашего кода, мы не знаем, как это плохо. –

+0

«... когда вы пытаетесь добавить 100 лет к 3000AD году, вы получите 3100AD независимо от того, что ... Лично я нашел это странным и нелогичным. Правильный результат должен быть 2900BC «Вы имели в виду сказать« BC »во всем этом пункте? В противном случае это не имеет смысла: AD 3000 + 100 = AD 3100; это правильный результат. –

+0

Джонатан: Извините, я ошибся, это не AD, это BC. – GregoryM

ответ

3

Я нашел простой обходной путь для этой ошибки. Вот оно:

@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 

Если вам интересно, почему я инвертировать только года, ответ прост, любой другой компонент, за исключением лет увеличение и уменьшение в правильном направлении (я не проверял их все, но месяцев и дни, похоже, работают нормально).

EDIT: удален ошибочно добавленный autorelease, спасибо John.

+1

Я рад, что вы это обнаружили, я провел день, отслеживая проблемы смещения даты BC, и в конце концов моя проблема была вызвана этой самой ошибкой/аномальностью. Кстати, результат компонентов: fromDate: не должен быть автореализован. –

0

Представьте, что у вас есть дата с 1-го момента нашей эры - 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, а затем поиска diff.

+3

Собственно, вторым до этого является 0001-12-31 23:59:59. Только номера года бегут назад. –

+0

Основная арифметика отлично работает в NSCalendar (за исключением случая, который я описал). Когда вы вычитаете 100 лет из 50AD, вы получаете 51BC. Но кажется, что вы перевернуты, когда имеете дело с датами BC. – GregoryM

0

Это ошибка или функция. В документе Apple никогда не говорится, что они означают по , добавляя компоненты к календарной дате. Для них совершенно бесплатно определить «добавление компонента» на дату BCE как дополнение к компоненту года.

Да, я согласен с вами в том, что это противоречиво, и я думаю, что это ошибка.

Вам необходимо конвертировать ваши NSDate либо

  • второй из эпохи UNIX (1.1.1970) с использованием -timeIntervalSince1970
  • второе из OS X эпохи (1.1.2001) с помощью -timeIntervalSinceReferenceDate

Затем вы можете выполнить расчет и преобразовать его обратно в NSDate. Я думаю, что это плохая идея работать в григорианском календаре все время ...Лучше было бы преобразовать в григорианский календарь непосредственно перед его отображением в графическом интерфейсе.

+0

Ваша идея на самом деле правильная, но есть проблема. Когда вы пытаетесь добавить 100 лет к определенному NSDate, вы используете [NSCalendar dateByAddingComponents: ...], почему? Потому что вы не знаете, сколько секунд прошло это 100 лет, верно? И, как я указывал ранее, метод [компоненты NSCalendar: fromDate: toDate: options:] вообще не работает в эпоху BC, поэтому нет возможности получить точную подсчет секунд в течение 100 лет, есть ли? – GregoryM

+0

Если вам действительно нужна эта конкретная операция, я думаю, вам нужно закодировать их самостоятельно. Честно говоря, я не понимаю, что вы подразумеваете под «добавлением 100 лет точно». Что такое 100 лет плюс «400AD 29 февраля»? В любом случае вам нужно сделать какое-то правило ad-hoc, чтобы справляться с високосными годами. – Yuji

+0

Я думаю, что добавление 100 лет к какой-то дате не является «конкретной» операцией :) и что я должен написать свои собственные методы для этого. Мне просто интересно, как непроверены классы Apple. Модульные тесты Afaik были изобретены слишком давно ... – GregoryM

Смежные вопросы