Under iOS 7 or 8, the stock Calendar app does something that I have been unable to figure out.
Under some locales, such as en_US
, the Calendar app shows the short (3-letter) month names.
Under other locales, such as de_DE
, the Calendar app shows the full month names. Interestingly, the locale en_DE
shows the short month names so it seems to be tied to the language more than the region format.
What I can't figure out is how to know which month format to use.
Regardless of my device's locale, NSDateFormatter standaloneShortMonthSymbols
gives me the 3-letter month names and NSDateFormatter standaloneMonthSymbols
gives me the full month names.
Is also tried:
NSString *monthformat = [NSDateFormatter dateFormatFromTemplate:@"LLL" options:0 locale:[NSLocale currentLocale]];
and that gives back the same LLL
for both en_US
and de_DE
.
Looking at NSLocale
there doesn't appear to be any value that determines whether to use short or full month names.
There doesn't appear to be anything in NSCalendar
, NSDateFormatter
, or NSLocale
to help determine which month format to use.
Does anyone have any idea how to make this determination?
Update:
I thought I found a solution but it doesn't work for all locales that I tried. I ran the following code with various locales to see if I could find anything in common between locales that show the short and long months names in the Calendar app:
NSLocale *locale = [NSLocale currentLocale];
NSString *locid = [locale localeIdentifier];
NSLog(@"Locale = %@", locid);
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSLog(@"monthSymbols = %@", [formatter monthSymbols]);
NSLog(@"shortMonthSymbols = %@", [formatter shortMonthSymbols]);
NSLog(@"veryShortMonthSymbols = %@", [formatter veryShortMonthSymbols]);
NSLog(@"monthStandaloneSymbols = %@", [formatter standaloneMonthSymbols]);
NSLog(@"shortStandaloneMonthSymbols = %@", [formatter shortStandaloneMonthSymbols]);
NSLog(@"veryShortStandaloneMonthSymbols = %@", [formatter veryShortStandaloneMonthSymbols]);
NSDate *date = [NSDate date];
[formatter setDateStyle:NSDateFormatterShortStyle];
NSLog(@"short date style: %@", [formatter stringFromDate:date]);
[formatter setDateStyle:NSDateFormatterMediumStyle];
NSLog(@"medium date style: %@", [formatter stringFromDate:date]);
[formatter setDateStyle:NSDateFormatterLongStyle];
NSLog(@"long date style: %@", [formatter stringFromDate:date]);
[formatter setDateStyle:NSDateFormatterFullStyle];
NSLog(@"full date style: %@", [formatter stringFromDate:date]);
[formatter setDateStyle:NSDateFormatterNoStyle];
[formatter setDateFormat:@"M"];
NSLog(@"M date format: %@", [formatter stringFromDate:date]);
[formatter setDateFormat:@"MM"];
NSLog(@"MM date format: %@", [formatter stringFromDate:date]);
[formatter setDateFormat:@"MMM"];
NSLog(@"MMM date format: %@", [formatter stringFromDate:date]);
[formatter setDateFormat:@"MMMM"];
NSLog(@"MMMM date format: %@", [formatter stringFromDate:date]);
[formatter setDateFormat:@"MMMMM"];
NSLog(@"MMMMM date format: %@", [formatter stringFromDate:date]);
[formatter setDateFormat:@"L"];
NSLog(@"L date format: %@", [formatter stringFromDate:date]);
[formatter setDateFormat:@"LL"];
NSLog(@"LL date format: %@", [formatter stringFromDate:date]);
[formatter setDateFormat:@"LLL"];
NSLog(@"LLL date format: %@", [formatter stringFromDate:date]);
[formatter setDateFormat:@"LLLL"];
NSLog(@"LLLL date format: %@", [formatter stringFromDate:date]);
[formatter setDateFormat:@"LLLLL"];
NSLog(@"LLLLL date format: %@", [formatter stringFromDate:date]);
I had tested with en_US
, en_GB
, es_ES
, de_DE
, fr_FR
, and it_IT
. The French and German locales show the full month name in the Calendar app while the rest show the short name.
The one thing that looked promising with the test code is that only the French and German locales have a period at the end of the shortMonthSymbols
.
So then I ran the following code to find all locales that use punctuation in the short month symbols and those that don't:
NSMutableArray *hasDot = [[NSMutableArray alloc] init];
NSMutableArray *noDot = [[NSMutableArray alloc] init];
NSCharacterSet *letters = [NSCharacterSet letterCharacterSet];
NSArray *locales = [NSLocale availableLocaleIdentifiers];
for (NSString *locid in locales) {
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:locid];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:locale];
NSArray *shortNames = [formatter shortMonthSymbols];
//NSLog(@"locale: %@, short names: %@", locid, shortNames[10]);
NSString *nov = shortNames[10];
unichar char1 = [nov characterAtIndex:0];
unichar charN = [nov characterAtIndex:nov.length - 1];
if ([letters characterIsMember:char1] && [letters characterIsMember:charN]) {
[noDot addObject:locid];
} else {
[hasDot addObject:locid];
}
}
NSLog(@"no dot: %@", [noDot sortedArrayUsingSelector:@selector(compare:)]);
NSLog(@"has dot: %@", [hasDot sortedArrayUsingSelector:@selector(compare:)]);
Scanning through the results I saw that the Dutch locales used a period in the short month symbols. But a quick test of the Calendar app revealed that the Calendar app showed short month names when the device was set to Dutch (nl_NL
). Ugh.
Update 2:
I've tested a few more locales. The following show long month names:
fr_FR, de_DE, ru_RU, sv_SE (actually all locales for each of these languages)
the following (and I'm sure many more) show the short month:
en_US, en_GB, es_ES, it_IT, nl_NL, ca_ES, uk_UA, ro_RO (actually all locales for each of these languages)
Months. Abbreviations are acceptable for Jan., Feb., Aug., Sept., Oct., Nov., and Dec. Use no punctuation if listing only the month and the year, but set the year off with commas if listing the day of the month as well. The basketball game is on Thursday, Feb.
Remember: "MA" is May/mai, and "MR" is March/mars.
aug. or kol.
Once every so often, there comes a question worth looking into. Rick, for you I debugged the Calendar app (can be done using attaching to MobileCal process). It all comes down to EventKitUI`CurrentLocaleRequiresUnabbrevatedMonthNames
which answers the desired question.
Let's look at its disassembly:
EventKitUI`CurrentLocaleRequiresUnabbrevatedMonthNames:
0x102c6bec7: pushq %rbp
0x102c6bec8: movq %rsp, %rbp
0x102c6becb: pushq %r15
0x102c6becd: pushq %r14
0x102c6becf: pushq %rbx
0x102c6bed0: subq $0xb8, %rsp
0x102c6bed7: movq 0x14a3aa(%rip), %r15 ; (void *)0x0000000104e93070: __stack_chk_guard
0x102c6bede: movq (%r15), %rax
0x102c6bee1: movq %rax, -0x20(%rbp)
0x102c6bee5: cmpq $0x0, 0x1c01fb(%rip) ; CurrentLocaleRequiresUnabbrevatedMonthNames.usesFullLengthMonthNames + 6
0x102c6beed: je 0x102c6beff ; CurrentLocaleRequiresUnabbrevatedMonthNames + 56
0x102c6beef: movb 0x1c01eb(%rip), %al ; CurrentLocaleRequiresUnabbrevatedMonthNames.hasChecked
0x102c6bef5: xorb $0x1, %al
0x102c6bef7: testb $0x1, %al
0x102c6bef9: je 0x102c6c0d6 ; CurrentLocaleRequiresUnabbrevatedMonthNames + 527
0x102c6beff: movq 0x1b583a(%rip), %rdi ; (void *)0x00000001025dae58: NSLocale
0x102c6bf06: movq 0x1aef23(%rip), %rsi ; "currentLocale"
0x102c6bf0d: movq 0x14a524(%rip), %r14 ; (void *)0x0000000104945000: objc_msgSend
0x102c6bf14: callq *%r14
0x102c6bf17: movq %rax, %rdi
0x102c6bf1a: callq 0x102d29920 ; symbol stub for: objc_retainAutoreleasedReturnValue
0x102c6bf1f: movq %rax, %rbx
0x102c6bf22: movq 0x14a227(%rip), %rax ; (void *)0x00000001025a3cd8: NSLocaleLanguageCode
0x102c6bf29: movq (%rax), %rdx
0x102c6bf2c: movq 0x1ae12d(%rip), %rsi ; "objectForKey:"
0x102c6bf33: movq %rbx, %rdi
0x102c6bf36: callq *%r14
0x102c6bf39: movq %rax, %rdi
0x102c6bf3c: callq 0x102d29920 ; symbol stub for: objc_retainAutoreleasedReturnValue
0x102c6bf41: movq %rax, %r14
0x102c6bf44: movq %rbx, %rdi
0x102c6bf47: callq *0x14a4f3(%rip) ; (void *)0x00000001049429b0: objc_release
0x102c6bf4d: movq 0x1c0194(%rip), %rdi ; __languagesRequiringUnabbreviatedMonthNames
0x102c6bf54: testq %rdi, %rdi
0x102c6bf57: jne 0x102c6c0b0 ; CurrentLocaleRequiresUnabbrevatedMonthNames + 489
0x102c6bf5d: leaq 0x15425c(%rip), %rax ; @"ru"
0x102c6bf64: movq %rax, -0xd0(%rbp)
0x102c6bf6b: leaq 0x1524ce(%rip), %rax ; @"de"
0x102c6bf72: movq %rax, -0xc8(%rbp)
0x102c6bf79: leaq 0x154260(%rip), %rax ; @"fr"
0x102c6bf80: movq %rax, -0xc0(%rbp)
0x102c6bf87: leaq 0x154272(%rip), %rax ; @"fi"
0x102c6bf8e: movq %rax, -0xb8(%rbp)
0x102c6bf95: leaq 0x154284(%rip), %rax ; @"pt"
0x102c6bf9c: movq %rax, -0xb0(%rbp)
0x102c6bfa3: leaq 0x154296(%rip), %rax ; @"no"
0x102c6bfaa: movq %rax, -0xa8(%rbp)
0x102c6bfb1: leaq 0x1542a8(%rip), %rax ; @"nb"
0x102c6bfb8: movq %rax, -0xa0(%rbp)
0x102c6bfbf: leaq 0x1542ba(%rip), %rax ; @"nn"
0x102c6bfc6: movq %rax, -0x98(%rbp)
0x102c6bfcd: leaq 0x1542cc(%rip), %rax ; @"sv"
0x102c6bfd4: movq %rax, -0x90(%rbp)
0x102c6bfdb: leaq 0x1542de(%rip), %rax ; @"he"
0x102c6bfe2: movq %rax, -0x88(%rbp)
0x102c6bfe9: leaq 0x1542f0(%rip), %rax ; @"th"
0x102c6bff0: movq %rax, -0x80(%rbp)
0x102c6bff4: leaq 0x154305(%rip), %rax ; @"hi"
0x102c6bffb: movq %rax, -0x78(%rbp)
0x102c6bfff: leaq 0x15431a(%rip), %rax ; @"bn"
0x102c6c006: movq %rax, -0x70(%rbp)
0x102c6c00a: leaq 0x15432f(%rip), %rax ; @"mr"
0x102c6c011: movq %rax, -0x68(%rbp)
0x102c6c015: leaq 0x154344(%rip), %rax ; @"ur"
0x102c6c01c: movq %rax, -0x60(%rbp)
0x102c6c020: leaq 0x154359(%rip), %rax ; @"te"
0x102c6c027: movq %rax, -0x58(%rbp)
0x102c6c02b: leaq 0x15436e(%rip), %rax ; @"ta"
0x102c6c032: movq %rax, -0x50(%rbp)
0x102c6c036: leaq 0x154383(%rip), %rax ; @"gu"
0x102c6c03d: movq %rax, -0x48(%rbp)
0x102c6c041: leaq 0x154398(%rip), %rax ; @"kn"
0x102c6c048: movq %rax, -0x40(%rbp)
0x102c6c04c: leaq 0x1543ad(%rip), %rax ; @"ml"
0x102c6c053: movq %rax, -0x38(%rbp)
0x102c6c057: leaq 0x1543c2(%rip), %rax ; @"ne"
0x102c6c05e: movq %rax, -0x30(%rbp)
0x102c6c062: leaq 0x1543d7(%rip), %rax ; @"pa"
0x102c6c069: movq %rax, -0x28(%rbp)
0x102c6c06d: movq 0x1b55ec(%rip), %rdi ; (void *)0x00000001025d9cd8: NSArray
0x102c6c074: movq 0x1ae5cd(%rip), %rsi ; "arrayWithObjects:count:"
0x102c6c07b: leaq -0xd0(%rbp), %rdx
0x102c6c082: movl $0x16, %ecx
0x102c6c087: callq *0x14a3ab(%rip) ; (void *)0x0000000104945000: objc_msgSend
0x102c6c08d: movq %rax, %rdi
0x102c6c090: callq 0x102d29920 ; symbol stub for: objc_retainAutoreleasedReturnValue
0x102c6c095: movq 0x1c004c(%rip), %rdi ; __languagesRequiringUnabbreviatedMonthNames
0x102c6c09c: movq %rax, 0x1c0045(%rip) ; __languagesRequiringUnabbreviatedMonthNames
0x102c6c0a3: callq *0x14a397(%rip) ; (void *)0x00000001049429b0: objc_release
0x102c6c0a9: movq 0x1c0038(%rip), %rdi ; __languagesRequiringUnabbreviatedMonthNames
0x102c6c0b0: movq 0x1ae6c1(%rip), %rsi ; "containsObject:"
0x102c6c0b7: movq %r14, %rdx
0x102c6c0ba: callq *0x14a378(%rip) ; (void *)0x0000000104945000: objc_msgSend
0x102c6c0c0: movb %al, 0x1c001b(%rip) ; CurrentLocaleRequiresUnabbrevatedMonthNames.usesFullLengthMonthNames
0x102c6c0c6: movb $0x1, 0x1c0013(%rip) ; __overlayCalendarGeneration + 7
0x102c6c0cd: movq %r14, %rdi
0x102c6c0d0: callq *0x14a36a(%rip) ; (void *)0x00000001049429b0: objc_release
0x102c6c0d6: movb 0x1c0005(%rip), %al ; CurrentLocaleRequiresUnabbrevatedMonthNames.usesFullLengthMonthNames
0x102c6c0dc: movq (%r15), %rcx
0x102c6c0df: cmpq -0x20(%rbp), %rcx
0x102c6c0e3: jne 0x102c6c0f3 ; CurrentLocaleRequiresUnabbrevatedMonthNames + 556
0x102c6c0e5: addq $0xb8, %rsp
0x102c6c0ec: popq %rbx
0x102c6c0ed: popq %r14
0x102c6c0ef: popq %r15
0x102c6c0f1: popq %rbp
0x102c6c0f2: retq
0x102c6c0f3: callq 0x102d29a1c ; symbol stub for: __stack_chk_fail
As you can see, it creates an array of locales that require unabbreviated month names. It then compares if the current locale is one of these locales.
Hardcoded in the code.
For abbreviated months, it uses the LLL
format (as seen in EventKitUI`CalStringForMonth
), and for unabbreviated months, it uses the LLLL
format (as seen in EventKitUI`CalLongStringForMonth
).
Cheers
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With