#objective-c #cocoa-touch #nsdate
#цель-c #прикосновение какао #nsdate #objective-c #cocoa-touch
Вопрос:
У меня есть фрагмент кода, который действительно работал в прошлом (по крайней мере, я думаю, что работал, потому что в противном случае проблема была бы обнаружена раньше). Через несколько недель, возможно, с переходом на летнее время, он начал выдавать неправильные результаты.
Предполагается, что этот фрагмент должен генерировать объект NSDate, относящийся к первому дню недели с заданными датами в 00:00h.
NSCalendar *gregorianCalender = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDate *firstDayDate;
unsigned yearAndWeek = NSTimeZoneCalendarUnit | NSYearCalendarUnit | NSWeekCalendarUnit;
// retrieve the components from the current date
NSDateComponents *compsCurrentDate = [gregorianCalender components:yearAndWeek fromDate:date];
[compsCurrentDate setWeekday:2]; // Monday
[compsCurrentDate setHour:0];
[compsCurrentDate setMinute:0];
[compsCurrentDate setSecond:0];
// make a date from the modfied components
firstDayDate = [gregorianCalender dateFromComponents:compsCurrentDate];
NSLog(@"MONDAY: %@", firstDayDate);
Сегодня 24 мая, поэтому в последней строке должно быть напечатано что-то вроде
MONDAY: 2011-05-23 00:00:00 0000
но на самом деле выводится следующее
MONDAY: 2011-05-22 22:00:00 0000
что, очевидно, неверно. 22 мая было в прошлое воскресенье. Теперь, чтобы сделать вещи странными, результат снова будет правильным, когда я изменю setHour:0
на
[compsCurrentDate setHour:2];
Я предполагаю, что с часовыми поясами что-то не так. Но, потратив полдня на изучение документации Apple, Google и stackoverflow, я не смог понять, в чем проблема. Возможно, я использую неправильные ключевые слова или я единственный, у кого есть такая проблема.
Мы высоко ценим каждую информацию! Пожалуйста, дайте мне знать, что вы думаете! Спасибо!
Ответ №1:
Поскольку никто не смог ответить, я продолжил свое исследование и обнаружил простую, но фатальную деталь в документации NSData
: description
метод, который вызывается при попытке напечатать NSDate
как NSString
по умолчанию, использует «default» NSLocale
для определения формата и часового пояса. Итак, все в моем коде было правильно, реальная проблема, которую я пытался исправить, была где-то в другом месте, и я уже сделал это в понедельник.. Я должен RTFM более интенсивным
Следующая строка кода выводит правильное время для моего часового пояса:
NSLog(@"MONDAY: %@", [firstDayDate descriptionWithLocale:[NSLocale systemLocale]]);
Тем не менее, во время моего исследования я обнаружил несколько интересных фрагментов, делающих в основном то же самое (хотя выходные данные также были «неправильными», поэтому я просто не заменил свои). Принимая во внимание все это, моя попытка работает, но немного неровная. Самый простой и надежный способ, который я нашел, взят из самой документации Apple, а также может быть найден на различных веб-сайтах:
/* VERSION FROM APPLE DOKU */
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
// Get the weekday component of the current date
NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit fromDate:date];
/*
Create a date components to represent the number of days to subtract from the current date.
The weekday value for Monday in the Gregorian calendar is 2, so subtract 2 from the number of days to subtract from the date in question. (If today's Sunday, subtract 0 days.)
*/
NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
[componentsToSubtract setDay: 0 - ([weekdayComponents weekday] - 2)];
NSDate *beginningOfWeek = [gregorian dateByAddingComponents:componentsToSubtract toDate:date options:0];
/*
Optional step:
beginningOfWeek now has the same hour, minute, and second as the original date (today).
To normalize to midnight, extract the year, month, and day components and create a new date from those components.
*/
NSDateComponents *components =
[gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
fromDate: beginningOfWeek];
beginningOfWeek = [gregorian dateFromComponents:components];
NSLog(@"MONDAY: %@", [beginningOfWeek descriptionWithLocale:[NSLocale systemLocale]]);
Наконец, чтобы завершить это: фрагмент, как вычислить последний день текущей недели.
// reuse the "first day" to calculate the last day
NSDate *endOfLastWeek = [whateveryourobject andmethodiscalled];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
/*
endOfLastWeek now is monday in the current week so create date components to add one week to the current date
*/
NSDateComponents *componentsToAdd = [[NSDateComponents alloc] init];
[componentsToAdd setWeek:1];
NSDate *endOfWeek = [gregorian dateByAddingComponents:componentsToAdd toDate:endOfLastWeek options:0];
NSLog(@"SUNDAY: %@", [endOfWeek descriptionWithLocale:[NSLocale systemLocale]]);