Skip to content

Commit 2ec1aab

Browse files
committed
Merge pull request dotnet#4 from eerhardt/icu-normalization-casing-calendar
Implement CalendarData on Linux.
2 parents 4dfa389 + 06fd251 commit 2ec1aab

File tree

10 files changed

+604
-89
lines changed

10 files changed

+604
-89
lines changed

src/corefx/System.Globalization.Native/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ endif()
1515
add_compile_options(-fPIC)
1616

1717
set(NATIVEGLOBALIZATION_SOURCES
18+
calendarData.cpp
1819
casing.cpp
1920
idna.cpp
2021
locale.cpp
Lines changed: 382 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
#include <assert.h>
7+
8+
#include "locale.hpp"
9+
10+
#include <unicode/dtfmtsym.h>
11+
#include <unicode/dtptngen.h>
12+
#include <unicode/locdspnm.h>
13+
14+
/*
15+
* These values should be kept in sync with System.Globalization.CalendarId
16+
*/
17+
enum CalendarId : int32_t
18+
{
19+
UNINITIALIZED_VALUE = 0,
20+
GREGORIAN = 1, // Gregorian (localized) calendar
21+
GREGORIAN_US = 2, // Gregorian (U.S.) calendar
22+
JAPAN = 3, // Japanese Emperor Era calendar
23+
/* SSS_WARNINGS_OFF */
24+
TAIWAN = 4, // Taiwan Era calendar /* SSS_WARNINGS_ON */
25+
KOREA = 5, // Korean Tangun Era calendar
26+
HIJRI = 6, // Hijri (Arabic Lunar) calendar
27+
THAI = 7, // Thai calendar
28+
HEBREW = 8, // Hebrew (Lunar) calendar
29+
GREGORIAN_ME_FRENCH = 9, // Gregorian Middle East French calendar
30+
GREGORIAN_ARABIC = 10, // Gregorian Arabic calendar
31+
GREGORIAN_XLIT_ENGLISH = 11, // Gregorian Transliterated English calendar
32+
GREGORIAN_XLIT_FRENCH = 12,
33+
// Note that all calendars after this point are MANAGED ONLY for now.
34+
JULIAN = 13,
35+
JAPANESELUNISOLAR = 14,
36+
CHINESELUNISOLAR = 15,
37+
SAKA = 16, // reserved to match Office but not implemented in our code
38+
LUNAR_ETO_CHN = 17, // reserved to match Office but not implemented in our code
39+
LUNAR_ETO_KOR = 18, // reserved to match Office but not implemented in our code
40+
LUNAR_ETO_ROKUYOU = 19, // reserved to match Office but not implemented in our code
41+
KOREANLUNISOLAR = 20,
42+
TAIWANLUNISOLAR = 21,
43+
PERSIAN = 22,
44+
UMALQURA = 23,
45+
LAST_CALENDAR = 23 // Last calendar ID
46+
};
47+
48+
/*
49+
* These values should be kept in sync with System.Globalization.CalendarDataType
50+
*/
51+
enum CalendarDataType : int32_t
52+
{
53+
Uninitialized = 0,
54+
NativeName = 1,
55+
MonthDay = 2,
56+
ShortDates = 3,
57+
LongDates = 4,
58+
YearMonths = 5,
59+
DayNames = 6,
60+
AbbrevDayNames = 7,
61+
MonthNames = 8,
62+
AbbrevMonthNames = 9,
63+
SuperShortDayNames = 10,
64+
MonthGenitiveNames = 11,
65+
AbbrevMonthGenitiveNames = 12,
66+
EraNames = 13,
67+
AbbrevEraNames = 14,
68+
};
69+
70+
/*
71+
* These values should be kept in sync with System.Globalization.CalendarDataResult
72+
*/
73+
enum CalendarDataResult : int32_t
74+
{
75+
Success = 0,
76+
UnknownError = 1,
77+
InsufficentBuffer = 2,
78+
};
79+
80+
// the function pointer definition for the callback used in EnumCalendarInfo
81+
typedef void(*EnumCalendarInfoCallback)(const UChar*, const void*);
82+
83+
/*
84+
Function:
85+
GetCalendarDataResult
86+
87+
Converts a UErrorCode to a CalendarDataResult.
88+
*/
89+
CalendarDataResult GetCalendarDataResult(UErrorCode err)
90+
{
91+
if (U_SUCCESS(err))
92+
{
93+
return Success;
94+
}
95+
96+
if (err == U_BUFFER_OVERFLOW_ERROR)
97+
{
98+
return InsufficentBuffer;
99+
}
100+
101+
return UnknownError;
102+
}
103+
104+
/*
105+
Function:
106+
GetCalendarName
107+
108+
Gets the associated ICU calendar name for the CalendarId.
109+
*/
110+
const char* GetCalendarName(CalendarId calendarId)
111+
{
112+
switch (calendarId)
113+
{
114+
case JAPAN:
115+
return "japanese";
116+
case THAI:
117+
return "buddhist";
118+
case HEBREW:
119+
return "hebrew";
120+
case CHINESELUNISOLAR:
121+
case KOREANLUNISOLAR:
122+
case JAPANESELUNISOLAR:
123+
case TAIWANLUNISOLAR:
124+
return "chinese";
125+
case PERSIAN:
126+
return "persian";
127+
case HIJRI:
128+
case UMALQURA:
129+
return "islamic";
130+
case GREGORIAN:
131+
case GREGORIAN_US:
132+
case GREGORIAN_ARABIC:
133+
case GREGORIAN_ME_FRENCH:
134+
case GREGORIAN_XLIT_ENGLISH:
135+
case GREGORIAN_XLIT_FRENCH:
136+
case KOREA:
137+
case JULIAN:
138+
case LUNAR_ETO_CHN:
139+
case LUNAR_ETO_KOR:
140+
case LUNAR_ETO_ROKUYOU:
141+
case SAKA:
142+
case TAIWAN:
143+
default:
144+
return "gregorian";
145+
}
146+
}
147+
148+
/*
149+
Function:
150+
GetMonthDayPattern
151+
152+
Gets the Month-Day DateTime pattern for the specified locale.
153+
*/
154+
CalendarDataResult GetMonthDayPattern(Locale& locale, UChar* sMonthDay, int32_t stringCapacity)
155+
{
156+
UErrorCode err = U_ZERO_ERROR;
157+
LocalPointer<DateTimePatternGenerator> generator(DateTimePatternGenerator::createInstance(locale, err));
158+
if (U_FAILURE(err))
159+
return GetCalendarDataResult(err);
160+
161+
UnicodeString monthDayPattern = generator->getBestPattern(UnicodeString("MMMMd"), err);
162+
if (U_FAILURE(err))
163+
return GetCalendarDataResult(err);
164+
165+
monthDayPattern.extract(sMonthDay, stringCapacity, err);
166+
167+
return GetCalendarDataResult(err);
168+
}
169+
170+
/*
171+
Function:
172+
GetNativeCalendarName
173+
174+
Gets the native calendar name.
175+
*/
176+
CalendarDataResult GetNativeCalendarName(Locale& locale, CalendarId calendarId, UChar* nativeName, int32_t stringCapacity)
177+
{
178+
LocalPointer<LocaleDisplayNames> displayNames(LocaleDisplayNames::createInstance(locale));
179+
180+
UnicodeString calendarName;
181+
displayNames->keyValueDisplayName("calendar", GetCalendarName(calendarId), calendarName);
182+
183+
UErrorCode err = U_ZERO_ERROR;
184+
calendarName.extract(nativeName, stringCapacity, err);
185+
186+
return GetCalendarDataResult(err);
187+
}
188+
189+
/*
190+
Function:
191+
GetCalendarInfo
192+
193+
Gets a single string of calendar information by filling the result parameter with the requested value.
194+
*/
195+
extern "C" CalendarDataResult GetCalendarInfo(const UChar* localeName, CalendarId calendarId, CalendarDataType dataType, UChar* result, int32_t resultCapacity)
196+
{
197+
Locale locale = GetLocale(localeName);
198+
if (locale.isBogus())
199+
return UnknownError;
200+
201+
switch (dataType)
202+
{
203+
case NativeName:
204+
return GetNativeCalendarName(locale, calendarId, result, resultCapacity);
205+
case MonthDay:
206+
return GetMonthDayPattern(locale, result, resultCapacity);
207+
default:
208+
assert(false);
209+
return UnknownError;
210+
}
211+
}
212+
213+
/*
214+
Function:
215+
InvokeCallbackForDateTimePattern
216+
217+
Gets the DateTime pattern for the specified skeleton and invokes the callback with the retrieved value.
218+
*/
219+
bool InvokeCallbackForDateTimePattern(Locale& locale, const char* patternSkeleton, EnumCalendarInfoCallback callback, const void* context)
220+
{
221+
UErrorCode err = U_ZERO_ERROR;
222+
LocalPointer<DateTimePatternGenerator> generator(DateTimePatternGenerator::createInstance(locale, err));
223+
if (U_FAILURE(err))
224+
return false;
225+
226+
UnicodeString pattern = generator->getBestPattern(UnicodeString(patternSkeleton), err);
227+
if (U_SUCCESS(err))
228+
{
229+
callback(pattern.getTerminatedBuffer(), context);
230+
return true;
231+
}
232+
233+
return false;
234+
}
235+
236+
/*
237+
Function:
238+
EnumCalendarArray
239+
240+
Enumerates an array of strings and invokes the callback for each value.
241+
*/
242+
bool EnumCalendarArray(const UnicodeString* srcArray, int32_t srcArrayCount, EnumCalendarInfoCallback callback, const void* context)
243+
{
244+
for (int i = 0; i < srcArrayCount; i++)
245+
{
246+
UnicodeString src = srcArray[i];
247+
callback(src.getTerminatedBuffer(), context);
248+
}
249+
250+
return true;
251+
}
252+
253+
/*
254+
Function:
255+
EnumWeekdays
256+
257+
Enumerates all the weekday names of the specified context and width, invoking the callback function
258+
for each weekday name.
259+
*/
260+
bool EnumWeekdays(
261+
Locale& locale,
262+
CalendarId calendarId,
263+
DateFormatSymbols::DtContextType dtContext,
264+
DateFormatSymbols::DtWidthType dtWidth,
265+
EnumCalendarInfoCallback callback,
266+
const void* context)
267+
{
268+
UErrorCode err = U_ZERO_ERROR;
269+
DateFormatSymbols dateFormatSymbols(locale, GetCalendarName(calendarId), err);
270+
if (U_FAILURE(err))
271+
return false;
272+
273+
int32_t daysCount;
274+
const UnicodeString* dayNames = dateFormatSymbols.getWeekdays(daysCount, dtContext, dtWidth);
275+
276+
// ICU returns an empty string for the first/zeroth element in the weekdays array.
277+
// So skip the first element.
278+
dayNames++;
279+
daysCount--;
280+
281+
return EnumCalendarArray(dayNames, daysCount, callback, context);
282+
}
283+
284+
/*
285+
Function:
286+
EnumMonths
287+
288+
Enumerates all the month names of the specified context and width, invoking the callback function
289+
for each month name.
290+
*/
291+
bool EnumMonths(
292+
Locale& locale,
293+
CalendarId calendarId,
294+
DateFormatSymbols::DtContextType dtContext,
295+
DateFormatSymbols::DtWidthType dtWidth,
296+
EnumCalendarInfoCallback callback,
297+
const void* context)
298+
{
299+
UErrorCode err = U_ZERO_ERROR;
300+
DateFormatSymbols dateFormatSymbols(locale, GetCalendarName(calendarId), err);
301+
if (U_FAILURE(err))
302+
return false;
303+
304+
int32_t monthsCount;
305+
const UnicodeString* monthNames = dateFormatSymbols.getMonths(monthsCount, dtContext, dtWidth);
306+
return EnumCalendarArray(monthNames, monthsCount, callback, context);
307+
}
308+
309+
/*
310+
Function:
311+
EnumEraNames
312+
313+
Enumerates all the era names of the specified locale and calendar, invoking the callback function
314+
for each era name.
315+
*/
316+
bool EnumEraNames(Locale& locale, CalendarId calendarId, EnumCalendarInfoCallback callback, const void* context)
317+
{
318+
UErrorCode err = U_ZERO_ERROR;
319+
DateFormatSymbols dateFormatSymbols(locale, GetCalendarName(calendarId), err);
320+
if (U_FAILURE(err))
321+
return false;
322+
323+
int32_t eraNameCount;
324+
const UnicodeString* eraNames = dateFormatSymbols.getEras(eraNameCount);
325+
return EnumCalendarArray(eraNames, eraNameCount, callback, context);
326+
}
327+
328+
/*
329+
Function:
330+
EnumCalendarInfo
331+
332+
Retrieves a collection of calendar string data specified by the locale, calendar, and data type.
333+
Allows for a collection of calendar string data to be retrieved by invoking
334+
the callback for each value in the collection.
335+
The context parameter is passed through to the callback along with each string.
336+
*/
337+
extern "C" int32_t EnumCalendarInfo(
338+
EnumCalendarInfoCallback callback,
339+
const UChar* localeName,
340+
CalendarId calendarId,
341+
CalendarDataType dataType,
342+
const void* context)
343+
{
344+
Locale locale = GetLocale(localeName);
345+
if (locale.isBogus())
346+
return false;
347+
348+
switch (dataType)
349+
{
350+
case ShortDates:
351+
return InvokeCallbackForDateTimePattern(locale, "Mdyyyy", callback, context);
352+
case LongDates:
353+
// TODO: need to replace the "EEEE"s with "dddd"s for .net
354+
// Also, "LLLL"s to "MMMM"s
355+
return InvokeCallbackForDateTimePattern(locale, "eeeeMMMMddyyyy", callback, context);
356+
case YearMonths:
357+
return InvokeCallbackForDateTimePattern(locale, "yyyyMMMM", callback, context);
358+
case DayNames:
359+
return EnumWeekdays(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE, callback, context);
360+
case AbbrevDayNames:
361+
return EnumWeekdays(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED, callback, context);
362+
case MonthNames:
363+
return EnumMonths(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE, callback, context);
364+
case AbbrevMonthNames:
365+
return EnumMonths(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED, callback, context);
366+
case SuperShortDayNames:
367+
return EnumWeekdays(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::SHORT, callback, context);
368+
case MonthGenitiveNames:
369+
return EnumMonths(locale, calendarId, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE, callback, context);
370+
case AbbrevMonthGenitiveNames:
371+
return EnumMonths(locale, calendarId, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED, callback, context);
372+
case EraNames:
373+
case AbbrevEraNames:
374+
// NOTE: On Windows, the EraName is "A.D." and AbbrevEraName is "AD".
375+
// But ICU/CLDR only supports "Anno Domini", "AD", and "A".
376+
// So returning getEras (i.e. "AD") for both EraNames and AbbrevEraNames.
377+
return EnumEraNames(locale, calendarId, callback, context);
378+
default:
379+
assert(false);
380+
return false;
381+
}
382+
}

0 commit comments

Comments
 (0)