我们的应用在日期方面的工作相当多,但目前我们只支持格里高利历,一个应用范围内的NSCalendar实例的初始化如下。
NSCalendar *appCalendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
上述方法的文档中写道 "返回的日历默认为当前地区和默认时区" 然而,当在区域设置为 "United Kingdom "的设备上运行应用时,调用 [appCalendar firstWeekday]
返回值为1(周日),而不是预期的2(周一)。如果我运行 [[NSCalendar currentCalendar] firstWeekday]
在 "appCalendar "上,返回了正确的值2。起初我以为 "appCalendar "上可能没有设置locale,但记录发现它有,尽管它缺少国家代码等,而 "currentCalendar "实例有,这使得它可以返回正确的第一周日。
是否应该明确地在返回的对象上设置locale?calendarWithIdentifier
如果有,这样做有什么注意事项吗?
最新情况
根据下面zrzka的回答,我建议在初始化一个带有标识符的日历时,显式地设置一个locale,例如:。
NSCalendar *appCalendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
appCalendar.locale = [NSLocale currentLocale];
该 文件 是错误的。
返回的日历默认为当前地区和默认时区。
应该是。
返回的日历默认为系统所在地区和默认时区。
CFCalendarRef CFCalendarCreateWithIdentifier(CFAllocatorRef allocator, CFStringRef identifier) {
if (allocator == NULL) allocator = __CFGetDefaultAllocator();
__CFGenericValidateType(allocator, CFAllocatorGetTypeID());
__CFGenericValidateType(identifier, CFStringGetTypeID());
// return NULL until Chinese calendar is available
if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar) {
// if (identifier != kCFGregorianCalendar && identifier != kCFBuddhistCalendar && identifier != kCFJapaneseCalendar && identifier != kCFIslamicCalendar && identifier != kCFIslamicCivilCalendar && identifier != kCFHebrewCalendar && identifier != kCFChineseCalendar) {
if (CFEqual(kCFGregorianCalendar, identifier)) identifier = kCFGregorianCalendar;
else if (CFEqual(kCFBuddhistCalendar, identifier)) identifier = kCFBuddhistCalendar;
else if (CFEqual(kCFJapaneseCalendar, identifier)) identifier = kCFJapaneseCalendar;
else if (CFEqual(kCFIslamicCalendar, identifier)) identifier = kCFIslamicCalendar;
else if (CFEqual(kCFIslamicCivilCalendar, identifier)) identifier = kCFIslamicCivilCalendar;
else if (CFEqual(kCFHebrewCalendar, identifier)) identifier = kCFHebrewCalendar;
// else if (CFEqual(kCFChineseCalendar, identifier)) identifier = kCFChineseCalendar;
else return NULL;
}
struct __CFCalendar *calendar = NULL;
uint32_t size = sizeof(struct __CFCalendar) - sizeof(CFRuntimeBase);
calendar = (struct __CFCalendar *)_CFRuntimeCreateInstance(allocator, CFCalendarGetTypeID(), size, NULL);
if (NULL == calendar) {
return NULL;
}
calendar->_identifier = (CFStringRef)CFRetain(identifier);
calendar->_locale = NULL;
calendar->_localeID = CFLocaleGetIdentifier(CFLocaleGetSystem());
calendar->_tz = CFTimeZoneCopyDefault();
calendar->_cal = NULL;
return (CFCalendarRef)calendar;
}
_locale
被初始化为 NULL
和 _localeID
被初始化为系统的locale标识符(在iPhone & simulator上是一个空字符串)。_cal
被设置为 NULL
.
CFIndex CFCalendarGetFirstWeekday(CFCalendarRef calendar) {
CF_OBJC_FUNCDISPATCHV(CFCalendarGetTypeID(), CFIndex, calendar, firstWeekday);
__CFGenericValidateType(calendar, CFCalendarGetTypeID());
if (!calendar->_cal) __CFCalendarSetupCal(calendar);
if (calendar->_cal) {
return ucal_getAttribute(calendar->_cal, UCAL_FIRST_DAY_OF_WEEK);
}
return -1;
}
所以,因为 _cal
是 NULL
, __CFCalendarSetupCal
是叫。
static void __CFCalendarSetupCal(CFCalendarRef calendar) {
calendar->_cal = __CFCalendarCreateUCalendar(calendar->_identifier, calendar->_localeID, calendar->_tz);
}
哪些调用 __CFCalendarCreateUCalendar
与 _localeID
是一个空字符串。
我可以在iOS 11, 12 & 13上确认这个行为。 源代码是一个叫做 CF-Lite但我进一步拆解了实际的CoreFoundation框架,它做了同样的事情......
call _CFLocaleGetSystem ; _CFLocaleGetSystem
mov rdi, rax ; argument "cf" for method _CFRetain
call _CFRetain ; _CFRetain
mov qword [r15+0x18], rax
call _CFTimeZoneCopyDefault ; _CFTimeZoneCopyDefault
mov qword [r15+0x20], rax
mov rbx, qword [r15+0x10]
mov rdi, qword [r15+0x18] ; argument "locale" for method _CFLocaleGetIdentifier
call _CFLocaleGetIdentifier ; _CFLocaleGetIdentifier
mov rdx, qword [r15+0x20] ; argument #3 for method ___CFCalendarCreateUCalendar
mov rdi, rbx ; argument #1 for method ___CFCalendarCreateUCalendar
mov rsi, rax ; argument #2 for method ___CFCalendarCreateUCalendar
call ___CFCalendarCreateUCalendar ; ___CFCalendarCreateUCalendar
...使用一个空的标识符从 CFLocaleGetIdentifier
从 CFLocaleGetSystem
.
当你检查 CFCalendarCreateWithIdentifier
文档中,对当前的地域、时区......只字未提。
更有趣的是这两个方法的区别(讨论部分)。
+calendarWithIdentifier:
-initWithCalendarIdentifier:
但是没有什么区别。calendarWithIdentifier:
只是调用 alloc
& initWithCalendarIdentifier:
.
push rbp
mov rbp, rsp
push r14
push rbx
mov rbx, rdx
mov rsi, qword [0x3cb478] ; argument "selector" for method _objc_msgSend, @selector(alloc)
mov r14, qword [_objc_msgSend_390220] ; _objc_msgSend_390220
call r14 ; Jumps to 0x553ae0 (_objc_msgSend), _objc_msgSend
mov rsi, qword [0x3cc768] ; argument "selector" for method _objc_msgSend, @selector(initWithCalendarIdentifier:)
...
我相信这是一个文档问题,应该向苹果公司报告(做到了。FB7740798).