Проблема NSCalendar с эпохой Британской Колумбии
-
03-10-2019 - |
Вопрос
Привет,
Недавно я столкнулся с большой проблемой (как мне кажется) с классом NSCalendar.
В моей задаче мне нужно работать с большими периодами времени, начиная с 4000 г. до н.э. по 2000 г. н.э. (по григорианскому календарю).Где-то меня заставили увеличить какой-то 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]);
Что вы можете сказать о таком поведении?Так должно работать или это баг?Я в замешательстве :С.
КСТАТИ.:метод [NSCalendarComponents:fromDate:toDate:options:] не позволяет нам вычислить разницу между годами в эпоху до нашей эры...Дополнительный "почему?" В этой коробке Пандоры.
P.S.:Я копался в официальной документации и других ресурсах, но ничего не нашел по этой проблеме (или, может быть, так и задумано, а я идиот?).
Решение
Я нашел простое решение этой ошибки.Вот:
@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
Если вам интересно, почему я инвертирую только годы, ответ прост: все остальные компоненты, кроме лет, увеличиваются и уменьшаются правильным образом (я не проверял их все, но месяцы и дни, похоже, работают нормально).
РЕДАКТИРОВАТЬ:удалено по ошибке добавлено автовыпуск, спасибо Джон.
Другие советы
Это ошибка и или функция. Apple Doc никогда не говорит, что они подразумевают под Добавление компонентов в календальную дату. Отказ Для них идеально свободно определить «добавление компонента» на дату BCE в качестве просто дополнения к компоненту года.
Да, я согласен с тобой, что это противоречит, и я думаю, что это ошибка.
Вам нужно преобразовать свой NSDate
либо
- второй из эпохи Unix (1.1.1970) с использованием
-timeIntervalSince1970
- второй из ОС X EPOCH (1.1.2001), используя
-timeIntervalSinceReferenceDate
Затем вы можете выполнить расчет и преобразовать его обратно в NSDate
. Отказ Я думаю, что это плохое представление о работе в Грегорьеском календаре все время ... Было бы лучше преобразовать в Грегорианский календарь незадолго до того, как покажешь его на графическом интерфейсе.
Представьте, что у вас есть дата с 1-м моментом нашей эры - AD 0001-01-01 00:00:00. Какой был момент раньше? BC 0001-01-01 00:00:01.. Отказ Если разработчики какао использовали базовые арифметики для этой задачи, вы получите AD 0000-12-31 23:59:59.. Отказ Это разумно для григорианского календаря? Я думаю, нет. Итак, мне кажется, что самый удобный способ реализации календаря должен был использовать флаг эпохи и изменения »направление времени«При работе с эпохи BC, чтобы получить читаемые человеком даты в каждом случае.
КСТАТИ.: [NSCalendar dateByAddingComponents:toDate:options:]
Действительно ведет себя странно и не может рассчитывать интервал времени между датами BC, я тоже проверил. Итак, для даты BC вы можете использовать обходной путь, например, путем перевода датов на AD, а затем найти diff.