diff --git a/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs b/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs
index d1ab917229f21c..faa7e4f3a90738 100644
--- a/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs
+++ b/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs
@@ -14,5 +14,11 @@ internal static extern unsafe ResultCode GetTimeZoneDisplayName(
TimeZoneDisplayNameType type,
char* result,
int resultLength);
+
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_WindowsIdToIanaId")]
+ internal static extern unsafe int WindowsIdToIanaId(string windowsId, char* ianaId, int ianaIdLength);
+
+ [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IanaIdToWindowsId")]
+ internal static extern unsafe int IanaIdToWindowsId(string ianaId, char* windowsId, int windowsIdLength);
}
}
diff --git a/src/libraries/Native/Unix/System.Globalization.Native/entrypoints.c b/src/libraries/Native/Unix/System.Globalization.Native/entrypoints.c
index 85a39085270b46..fced139e21958e 100644
--- a/src/libraries/Native/Unix/System.Globalization.Native/entrypoints.c
+++ b/src/libraries/Native/Unix/System.Globalization.Native/entrypoints.c
@@ -42,6 +42,7 @@ static const Entry s_globalizationNative[] =
DllImportEntry(GlobalizationNative_GetSortKey)
DllImportEntry(GlobalizationNative_GetSortVersion)
DllImportEntry(GlobalizationNative_GetTimeZoneDisplayName)
+ DllImportEntry(GlobalizationNative_IanaIdToWindowsId)
DllImportEntry(GlobalizationNative_IndexOf)
DllImportEntry(GlobalizationNative_InitICUFunctions)
DllImportEntry(GlobalizationNative_InitOrdinalCasingPage)
@@ -53,6 +54,7 @@ static const Entry s_globalizationNative[] =
DllImportEntry(GlobalizationNative_StartsWith)
DllImportEntry(GlobalizationNative_ToAscii)
DllImportEntry(GlobalizationNative_ToUnicode)
+ DllImportEntry(GlobalizationNative_WindowsIdToIanaId)
};
EXTERN_C const void* GlobalizationResolveDllImport(const char* name);
diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c
index 99a7899b848358..4f71b66c2678f8 100644
--- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c
+++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c
@@ -20,7 +20,7 @@
#include "pal_icushim.h"
// Define pointers to all the used ICU functions
-#define PER_FUNCTION_BLOCK(fn, lib) TYPEOF(fn)* fn##_ptr;
+#define PER_FUNCTION_BLOCK(fn, lib, required) TYPEOF(fn)* fn##_ptr;
FOR_ALL_ICU_FUNCTIONS
#undef PER_FUNCTION_BLOCK
@@ -41,14 +41,14 @@ static void* libicui18n = NULL;
#if defined (TARGET_UNIX)
-#define PER_FUNCTION_BLOCK(fn, lib) \
+#define PER_FUNCTION_BLOCK(fn, lib, required) \
c_static_assert_msg((sizeof(#fn) + MaxICUVersionStringWithSuffixLength + 1) <= sizeof(symbolName), "The symbolName is too small for symbol " #fn); \
sprintf(symbolName, #fn "%s", symbolVersion); \
fn##_ptr = (TYPEOF(fn)*)dlsym(lib, symbolName); \
- if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from " #lib "\nError: %s\n", symbolName, dlerror()); abort(); }
+ if (fn##_ptr == NULL && required) { fprintf(stderr, "Cannot get symbol %s from " #lib "\nError: %s\n", symbolName, dlerror()); abort(); }
static int FindSymbolVersion(int majorVer, int minorVer, int subVer, char* symbolName, char* symbolVersion, char* suffix)
-{
+{
// Find out the format of the version string added to each symbol
// First try just the unversioned symbol
if (dlsym(libicuuc, "u_strlen") == NULL)
@@ -89,10 +89,10 @@ static int FindSymbolVersion(int majorVer, int minorVer, int subVer, char* symbo
#define sscanf sscanf_s
-#define PER_FUNCTION_BLOCK(fn, lib) \
+#define PER_FUNCTION_BLOCK(fn, lib, required) \
sprintf_s(symbolName, SYMBOL_NAME_SIZE, #fn "%s", symbolVersion); \
fn##_ptr = (TYPEOF(fn)*)GetProcAddress((HMODULE)lib, symbolName); \
- if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol %s from " #lib "\nError: %u\n", symbolName, GetLastError()); abort(); }
+ if (fn##_ptr == NULL && required) { fprintf(stderr, "Cannot get symbol %s from " #lib "\nError: %u\n", symbolName, GetLastError()); abort(); }
static int FindICULibs()
{
@@ -421,7 +421,7 @@ void GlobalizationNative_InitICUFunctions(void* icuuc, void* icuin, const char*
assert(icuuc != NULL);
assert(icuin != NULL);
assert(version != NULL);
-
+
libicuuc = icuuc;
libicui18n = icuin;
int major = -1;
@@ -476,7 +476,7 @@ int32_t GlobalizationNative_GetICUVersion()
{
if (u_getVersion_ptr == NULL)
return 0;
-
+
UVersionInfo versionInfo;
u_getVersion(versionInfo);
diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h
index 081518b753deb5..836ba44ec182fd 100644
--- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h
+++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal.h
@@ -53,132 +53,148 @@
#include "pal_compiler.h"
#if !defined(STATIC_ICU)
+
+#if !defined(TARGET_ANDROID)
+// (U_ICU_VERSION_MAJOR_NUM < 52)
+// The following APIs are not supported in the ICU versions less than 52. We need to define them manually.
+// We have to do runtime check before using the pointers to these APIs. That is why these are listed in the FOR_ALL_OPTIONAL_ICU_FUNCTIONS list.
+U_CAPI int32_t U_EXPORT2 ucal_getWindowsTimeZoneID(const UChar* id, int32_t len,UChar* winid, int32_t winidCapacity, UErrorCode* status);
+U_CAPI int32_t U_EXPORT2 ucal_getTimeZoneIDForWindowsID(const UChar* winid, int32_t len, const char* region, UChar* id, int32_t idCapacity, UErrorCode* status);
+#endif
+
// List of all functions from the ICU libraries that are used in the System.Globalization.Native.so
#define FOR_ALL_UNCONDITIONAL_ICU_FUNCTIONS \
- PER_FUNCTION_BLOCK(u_charsToUChars, libicuuc) \
- PER_FUNCTION_BLOCK(u_getVersion, libicuuc) \
- PER_FUNCTION_BLOCK(u_strlen, libicuuc) \
- PER_FUNCTION_BLOCK(u_strncpy, libicuuc) \
- PER_FUNCTION_BLOCK(u_tolower, libicuuc) \
- PER_FUNCTION_BLOCK(u_toupper, libicuuc) \
- PER_FUNCTION_BLOCK(ucal_add, libicui18n) \
- PER_FUNCTION_BLOCK(ucal_close, libicui18n) \
- PER_FUNCTION_BLOCK(ucal_get, libicui18n) \
- PER_FUNCTION_BLOCK(ucal_getAttribute, libicui18n) \
- PER_FUNCTION_BLOCK(ucal_getKeywordValuesForLocale, libicui18n) \
- PER_FUNCTION_BLOCK(ucal_getLimit, libicui18n) \
- PER_FUNCTION_BLOCK(ucal_getTimeZoneDisplayName, libicui18n) \
- PER_FUNCTION_BLOCK(ucal_open, libicui18n) \
- PER_FUNCTION_BLOCK(ucal_set, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_close, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_closeElements, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_getOffset, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_getRules, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_getSortKey, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_getStrength, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_getVersion, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_next, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_previous, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_open, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_openElements, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_openRules, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_safeClone, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_setAttribute, libicui18n) \
- PER_FUNCTION_BLOCK(ucol_strcoll, libicui18n) \
- PER_FUNCTION_BLOCK(udat_close, libicui18n) \
- PER_FUNCTION_BLOCK(udat_countSymbols, libicui18n) \
- PER_FUNCTION_BLOCK(udat_getSymbols, libicui18n) \
- PER_FUNCTION_BLOCK(udat_open, libicui18n) \
- PER_FUNCTION_BLOCK(udat_setCalendar, libicui18n) \
- PER_FUNCTION_BLOCK(udat_toPattern, libicui18n) \
- PER_FUNCTION_BLOCK(udatpg_close, libicui18n) \
- PER_FUNCTION_BLOCK(udatpg_getBestPattern, libicui18n) \
- PER_FUNCTION_BLOCK(udatpg_open, libicui18n) \
- PER_FUNCTION_BLOCK(uenum_close, libicuuc) \
- PER_FUNCTION_BLOCK(uenum_count, libicuuc) \
- PER_FUNCTION_BLOCK(uenum_next, libicuuc) \
- PER_FUNCTION_BLOCK(uidna_close, libicuuc) \
- PER_FUNCTION_BLOCK(uidna_nameToASCII, libicuuc) \
- PER_FUNCTION_BLOCK(uidna_nameToUnicode, libicuuc) \
- PER_FUNCTION_BLOCK(uidna_openUTS46, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_canonicalize, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_countAvailable, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getAvailable, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getBaseName, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getCharacterOrientation, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getCountry, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getDefault, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getDisplayCountry, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getDisplayLanguage, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getDisplayName, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getISO3Country, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getISO3Language, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getKeywordValue, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getLanguage, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getLCID, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getName, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_getParent, libicuuc) \
- PER_FUNCTION_BLOCK(uloc_setKeywordValue, libicuuc) \
- PER_FUNCTION_BLOCK(ulocdata_getCLDRVersion, libicui18n) \
- PER_FUNCTION_BLOCK(ulocdata_getMeasurementSystem, libicui18n) \
- PER_FUNCTION_BLOCK(unorm2_getNFCInstance, libicuuc) \
- PER_FUNCTION_BLOCK(unorm2_getNFDInstance, libicuuc) \
- PER_FUNCTION_BLOCK(unorm2_getNFKCInstance, libicuuc) \
- PER_FUNCTION_BLOCK(unorm2_getNFKDInstance, libicuuc) \
- PER_FUNCTION_BLOCK(unorm2_isNormalized, libicuuc) \
- PER_FUNCTION_BLOCK(unorm2_normalize, libicuuc) \
- PER_FUNCTION_BLOCK(unum_close, libicui18n) \
- PER_FUNCTION_BLOCK(unum_getAttribute, libicui18n) \
- PER_FUNCTION_BLOCK(unum_getSymbol, libicui18n) \
- PER_FUNCTION_BLOCK(unum_open, libicui18n) \
- PER_FUNCTION_BLOCK(unum_toPattern, libicui18n) \
- PER_FUNCTION_BLOCK(ures_close, libicuuc) \
- PER_FUNCTION_BLOCK(ures_getByKey, libicuuc) \
- PER_FUNCTION_BLOCK(ures_getSize, libicuuc) \
- PER_FUNCTION_BLOCK(ures_getStringByIndex, libicuuc) \
- PER_FUNCTION_BLOCK(ures_open, libicuuc) \
- PER_FUNCTION_BLOCK(usearch_close, libicui18n) \
- PER_FUNCTION_BLOCK(usearch_first, libicui18n) \
- PER_FUNCTION_BLOCK(usearch_getMatchedLength, libicui18n) \
- PER_FUNCTION_BLOCK(usearch_last, libicui18n) \
- PER_FUNCTION_BLOCK(usearch_openFromCollator, libicui18n) \
- PER_FUNCTION_BLOCK(usearch_setPattern, libicui18n) \
- PER_FUNCTION_BLOCK(usearch_setText, libicui18n)
+ PER_FUNCTION_BLOCK(u_charsToUChars, libicuuc, true) \
+ PER_FUNCTION_BLOCK(u_getVersion, libicuuc, true) \
+ PER_FUNCTION_BLOCK(u_strlen, libicuuc, true) \
+ PER_FUNCTION_BLOCK(u_strncpy, libicuuc, true) \
+ PER_FUNCTION_BLOCK(u_tolower, libicuuc, true) \
+ PER_FUNCTION_BLOCK(u_toupper, libicuuc, true) \
+ PER_FUNCTION_BLOCK(ucal_add, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucal_close, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucal_get, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucal_getAttribute, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucal_getKeywordValuesForLocale, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucal_getLimit, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucal_getTimeZoneDisplayName, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucal_open, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucal_set, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_close, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_closeElements, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_getOffset, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_getRules, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_getSortKey, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_getStrength, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_getVersion, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_next, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_previous, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_open, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_openElements, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_openRules, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_safeClone, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_setAttribute, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucol_strcoll, libicui18n, true) \
+ PER_FUNCTION_BLOCK(udat_close, libicui18n, true) \
+ PER_FUNCTION_BLOCK(udat_countSymbols, libicui18n, true) \
+ PER_FUNCTION_BLOCK(udat_getSymbols, libicui18n, true) \
+ PER_FUNCTION_BLOCK(udat_open, libicui18n, true) \
+ PER_FUNCTION_BLOCK(udat_setCalendar, libicui18n, true) \
+ PER_FUNCTION_BLOCK(udat_toPattern, libicui18n, true) \
+ PER_FUNCTION_BLOCK(udatpg_close, libicui18n, true) \
+ PER_FUNCTION_BLOCK(udatpg_getBestPattern, libicui18n, true) \
+ PER_FUNCTION_BLOCK(udatpg_open, libicui18n, true) \
+ PER_FUNCTION_BLOCK(uenum_close, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uenum_count, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uenum_next, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uidna_close, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uidna_nameToASCII, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uidna_nameToUnicode, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uidna_openUTS46, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_canonicalize, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_countAvailable, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getAvailable, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getBaseName, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getCharacterOrientation, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getCountry, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getDefault, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getDisplayCountry, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getDisplayLanguage, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getDisplayName, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getISO3Country, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getISO3Language, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getKeywordValue, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getLanguage, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getLCID, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getName, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_getParent, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uloc_setKeywordValue, libicuuc, true) \
+ PER_FUNCTION_BLOCK(ulocdata_getCLDRVersion, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ulocdata_getMeasurementSystem, libicui18n, true) \
+ PER_FUNCTION_BLOCK(unorm2_getNFCInstance, libicuuc, true) \
+ PER_FUNCTION_BLOCK(unorm2_getNFDInstance, libicuuc, true) \
+ PER_FUNCTION_BLOCK(unorm2_getNFKCInstance, libicuuc, true) \
+ PER_FUNCTION_BLOCK(unorm2_getNFKDInstance, libicuuc, true) \
+ PER_FUNCTION_BLOCK(unorm2_isNormalized, libicuuc, true) \
+ PER_FUNCTION_BLOCK(unorm2_normalize, libicuuc, true) \
+ PER_FUNCTION_BLOCK(unum_close, libicui18n, true) \
+ PER_FUNCTION_BLOCK(unum_getAttribute, libicui18n, true) \
+ PER_FUNCTION_BLOCK(unum_getSymbol, libicui18n, true) \
+ PER_FUNCTION_BLOCK(unum_open, libicui18n, true) \
+ PER_FUNCTION_BLOCK(unum_toPattern, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ures_close, libicuuc, true) \
+ PER_FUNCTION_BLOCK(ures_getByKey, libicuuc, true) \
+ PER_FUNCTION_BLOCK(ures_getSize, libicuuc, true) \
+ PER_FUNCTION_BLOCK(ures_getStringByIndex, libicuuc, true) \
+ PER_FUNCTION_BLOCK(ures_open, libicuuc, true) \
+ PER_FUNCTION_BLOCK(usearch_close, libicui18n, true) \
+ PER_FUNCTION_BLOCK(usearch_first, libicui18n, true) \
+ PER_FUNCTION_BLOCK(usearch_getMatchedLength, libicui18n, true) \
+ PER_FUNCTION_BLOCK(usearch_last, libicui18n, true) \
+ PER_FUNCTION_BLOCK(usearch_openFromCollator, libicui18n, true) \
+ PER_FUNCTION_BLOCK(usearch_setPattern, libicui18n, true) \
+ PER_FUNCTION_BLOCK(usearch_setText, libicui18n, true)
#if HAVE_SET_MAX_VARIABLE
#define FOR_ALL_SET_VARIABLE_ICU_FUNCTIONS \
- PER_FUNCTION_BLOCK(ucol_setMaxVariable, libicui18n)
+ PER_FUNCTION_BLOCK(ucol_setMaxVariable, libicui18n, true)
#else
#define FOR_ALL_SET_VARIABLE_ICU_FUNCTIONS \
- PER_FUNCTION_BLOCK(ucol_setVariableTop, libicui18n)
+ PER_FUNCTION_BLOCK(ucol_setVariableTop, libicui18n, true)
#endif
#if defined(TARGET_WINDOWS)
#define FOR_ALL_OS_CONDITIONAL_ICU_FUNCTIONS \
- PER_FUNCTION_BLOCK(ucurr_forLocale, libicuuc) \
- PER_FUNCTION_BLOCK(ucurr_getName, libicuuc) \
- PER_FUNCTION_BLOCK(uldn_close, libicuuc) \
- PER_FUNCTION_BLOCK(uldn_keyValueDisplayName, libicuuc) \
- PER_FUNCTION_BLOCK(uldn_open, libicuuc)
+ PER_FUNCTION_BLOCK(ucurr_forLocale, libicuuc, true) \
+ PER_FUNCTION_BLOCK(ucurr_getName, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uldn_close, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uldn_keyValueDisplayName, libicuuc, true) \
+ PER_FUNCTION_BLOCK(uldn_open, libicuuc, true)
#else
// Unix ICU is dynamically resolved at runtime and these APIs in old versions
// of ICU were in libicui18n
#define FOR_ALL_OS_CONDITIONAL_ICU_FUNCTIONS \
- PER_FUNCTION_BLOCK(ucurr_forLocale, libicui18n) \
- PER_FUNCTION_BLOCK(ucurr_getName, libicui18n) \
- PER_FUNCTION_BLOCK(uldn_close, libicui18n) \
- PER_FUNCTION_BLOCK(uldn_keyValueDisplayName, libicui18n) \
- PER_FUNCTION_BLOCK(uldn_open, libicui18n)
+ PER_FUNCTION_BLOCK(ucurr_forLocale, libicui18n, true) \
+ PER_FUNCTION_BLOCK(ucurr_getName, libicui18n, true) \
+ PER_FUNCTION_BLOCK(uldn_close, libicui18n, true) \
+ PER_FUNCTION_BLOCK(uldn_keyValueDisplayName, libicui18n, true) \
+ PER_FUNCTION_BLOCK(uldn_open, libicui18n, true)
#endif
+// The following are the list of the ICU APIs which are optional. If these APIs exist in the ICU version we load at runtime, then we'll use it.
+// Otherwise, we'll just not provide the functionality to users which needed these APIs.
+#define FOR_ALL_OPTIONAL_ICU_FUNCTIONS \
+ PER_FUNCTION_BLOCK(ucal_getWindowsTimeZoneID, libicui18n, false) \
+ PER_FUNCTION_BLOCK(ucal_getTimeZoneIDForWindowsID, libicui18n, false)
+
#define FOR_ALL_ICU_FUNCTIONS \
FOR_ALL_UNCONDITIONAL_ICU_FUNCTIONS \
FOR_ALL_SET_VARIABLE_ICU_FUNCTIONS \
+ FOR_ALL_OPTIONAL_ICU_FUNCTIONS \
FOR_ALL_OS_CONDITIONAL_ICU_FUNCTIONS
// Declare pointers to all the used ICU functions
-#define PER_FUNCTION_BLOCK(fn, lib) EXTERN_C TYPEOF(fn)* fn##_ptr;
+#define PER_FUNCTION_BLOCK(fn, lib, required) EXTERN_C TYPEOF(fn)* fn##_ptr;
FOR_ALL_ICU_FUNCTIONS
#undef PER_FUNCTION_BLOCK
@@ -197,6 +213,8 @@ FOR_ALL_ICU_FUNCTIONS
#define ucal_getKeywordValuesForLocale(...) ucal_getKeywordValuesForLocale_ptr(__VA_ARGS__)
#define ucal_getLimit(...) ucal_getLimit_ptr(__VA_ARGS__)
#define ucal_getTimeZoneDisplayName(...) ucal_getTimeZoneDisplayName_ptr(__VA_ARGS__)
+#define ucal_getTimeZoneIDForWindowsID(...) ucal_getTimeZoneIDForWindowsID_ptr(__VA_ARGS__)
+#define ucal_getWindowsTimeZoneID(...) ucal_getWindowsTimeZoneID_ptr(__VA_ARGS__)
#define ucal_open(...) ucal_open_ptr(__VA_ARGS__)
#define ucal_set(...) ucal_set_ptr(__VA_ARGS__)
#define ucol_close(...) ucol_close_ptr(__VA_ARGS__)
@@ -285,4 +303,9 @@ FOR_ALL_ICU_FUNCTIONS
#define usearch_setPattern(...) usearch_setPattern_ptr(__VA_ARGS__)
#define usearch_setText(...) usearch_setText_ptr(__VA_ARGS__)
+#else // !defined(STATIC_ICU)
+
+#define ucal_getWindowsTimeZoneID_ptr ucal_getWindowsTimeZoneID
+#define ucal_getTimeZoneIDForWindowsID_ptr ucal_getTimeZoneIDForWindowsID
+
#endif // !defined(STATIC_ICU)
diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h
index 2b1ed0929ff4f8..7e05fa35d03ca8 100644
--- a/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h
+++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim_internal_android.h
@@ -436,6 +436,8 @@ int32_t ucal_getLimit(const UCalendar * cal, UCalendarDateFields field, UCalenda
int32_t ucal_getTimeZoneDisplayName(const UCalendar * cal, UCalendarDisplayNameType type, const char * locale, UChar * result, int32_t resultLength, UErrorCode * status);
UCalendar * ucal_open(const UChar * zoneID, int32_t len, const char * locale, UCalendarType type, UErrorCode * status);
void ucal_set(UCalendar * cal, UCalendarDateFields field, int32_t value);
+int32_t ucal_getTimeZoneIDForWindowsID(const UChar * winid, int32_t len, const char * region, UChar * id, int32_t idCapacity, UErrorCode * status);
+int32_t ucal_getWindowsTimeZoneID(const UChar * id, int32_t len, UChar * winid, int32_t winidCapacity, UErrorCode * status);
void ucol_close(UCollator * coll);
void ucol_closeElements(UCollationElements * elems);
int32_t ucol_getOffset(const UCollationElements *elems);
diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c
index ce865cdf0c1ae9..09c2c1ca5092bc 100644
--- a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c
+++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.c
@@ -39,3 +39,44 @@ ResultCode GlobalizationNative_GetTimeZoneDisplayName(const UChar* localeName,
ucal_close(calendar);
return GetResultCode(err);
}
+
+/*
+Convert Windows Time Zone Id to IANA Id
+*/
+int32_t GlobalizationNative_WindowsIdToIanaId(const UChar* windowsId, UChar* ianaId, int32_t ianaIdLength)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ if (ucal_getTimeZoneIDForWindowsID_ptr != NULL)
+ {
+ int32_t ianaIdFilledLength = ucal_getTimeZoneIDForWindowsID(windowsId, -1, NULL, ianaId, ianaIdLength, &status);
+ if (U_SUCCESS(status))
+ {
+ return ianaIdFilledLength;
+ }
+ }
+
+ // Failed
+ return 0;
+}
+
+/*
+Convert IANA Time Zone Id to Windows Id
+*/
+int32_t GlobalizationNative_IanaIdToWindowsId(const UChar* ianaId, UChar* windowsId, int32_t windowsIdLength)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ if (ucal_getWindowsTimeZoneID_ptr != NULL)
+ {
+ int32_t windowsIdFilledLength = ucal_getWindowsTimeZoneID(ianaId, -1, windowsId, windowsIdLength, &status);
+
+ if (U_SUCCESS(status))
+ {
+ return windowsIdFilledLength;
+ }
+ }
+
+ // Failed
+ return 0;
+}
diff --git a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h
index 1c5369e9bfb176..c9fe188979c11f 100644
--- a/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h
+++ b/src/libraries/Native/Unix/System.Globalization.Native/pal_timeZoneInfo.h
@@ -23,3 +23,6 @@ PALEXPORT ResultCode GlobalizationNative_GetTimeZoneDisplayName(const UChar* loc
TimeZoneDisplayNameType type,
UChar* result,
int32_t resultLength);
+
+PALEXPORT int32_t GlobalizationNative_WindowsIdToIanaId(const UChar* windowsId, UChar* ianaId, int32_t ianaIdLength);
+PALEXPORT int32_t GlobalizationNative_IanaIdToWindowsId(const UChar* ianaId, UChar* windowsId, int32_t windowsIdLength);
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs
index 30d4ca75718cbd..fd2d2556527c6d 100644
--- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs
@@ -164,6 +164,36 @@ private static void PopulateAllSystemTimeZones(CachedData cachedData)
}
}
+ private static unsafe string? GetAlternativeId(string id)
+ {
+ if (!GlobalizationMode.Invariant)
+ {
+ if (id.Equals("utc", StringComparison.OrdinalIgnoreCase))
+ {
+ //special case UTC as ICU will convert it to "Etc/GMT" which is incorrect name for UTC.
+ return "Etc/UTC";
+ }
+ foreach (char c in id)
+ {
+ // ICU uses some characters as a separator and trim the id at that character.
+ // while we should fail if the Id contained one of these characters.
+ if (c == '\\' || c == '\n' || c == '\r')
+ {
+ return null;
+ }
+ }
+
+ char* buffer = stackalloc char[100];
+ int length = Interop.Globalization.WindowsIdToIanaId(id, buffer, 100);
+ if (length > 0)
+ {
+ return new string(buffer, 0, length);
+ }
+ }
+
+ return null;
+ }
+
///
/// Helper function for retrieving the local system time zone.
/// May throw COMException, TimeZoneNotFoundException, InvalidTimeZoneException.
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs
index 93195932f052fc..6223e66e018ae1 100644
--- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs
@@ -105,6 +105,31 @@ private static void PopulateAllSystemTimeZones(CachedData cachedData)
}
}
+ private static unsafe string? GetAlternativeId(string id)
+ {
+ if (!GlobalizationMode.Invariant && !GlobalizationMode.UseNls)
+ {
+ foreach (char c in id)
+ {
+ // ICU uses some characters as a separator and trim the id at that character.
+ // while we should fail if the Id contained one of these characters.
+ if (c == '\\' || c == '\n' || c == '\r')
+ {
+ return null;
+ }
+ }
+
+ char* buffer = stackalloc char[100];
+ int length = Interop.Globalization.IanaIdToWindowsId(id, buffer, 100);
+ if (length > 0)
+ {
+ return new string(buffer, 0, length);
+ }
+ }
+
+ return null;
+ }
+
private TimeZoneInfo(in TIME_ZONE_INFORMATION zone, bool dstDisabled)
{
string standardName = zone.GetStandardName();
diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs
index 2669b740835cae..771cef56a3a8cb 100644
--- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs
@@ -46,6 +46,8 @@ private enum TimeZoneInfoResult
private readonly TimeSpan _baseUtcOffset;
private readonly bool _supportsDaylightSavingTime;
private readonly AdjustmentRule[]? _adjustmentRules;
+ // As we support IANA and Windows Ids, it is possible we create equivalent zone objects which differ only in the Ids.
+ private List? _equivalentZones;
// constants for TimeZoneInfo.Local and TimeZoneInfo.Utc
private const string UtcId = "UTC";
@@ -1788,6 +1790,58 @@ internal static DateTime TransitionTimeToDateTime(int year, TransitionTime trans
/// assumes cachedData lock is taken
///
private static TimeZoneInfoResult TryGetTimeZone(string id, bool dstDisabled, out TimeZoneInfo? value, out Exception? e, CachedData cachedData, bool alwaysFallbackToLocalMachine = false)
+ {
+ TimeZoneInfoResult result = TryGetTimeZoneUsingId(id, dstDisabled, out value, out e, cachedData, alwaysFallbackToLocalMachine);
+ if (result != TimeZoneInfoResult.Success)
+ {
+ string? alternativeId = GetAlternativeId(id);
+ if (alternativeId != null)
+ {
+ result = TryGetTimeZoneUsingId(alternativeId, dstDisabled, out value, out e, cachedData, alwaysFallbackToLocalMachine);
+ if (result == TimeZoneInfoResult.Success)
+ {
+ TimeZoneInfo? zone = null;
+ if (value!._equivalentZones == null)
+ {
+ zone = new TimeZoneInfo(id, value!._baseUtcOffset, value!._displayName, value!._standardDisplayName,
+ value!._daylightDisplayName, value!._adjustmentRules, dstDisabled && value!._supportsDaylightSavingTime);
+ value!._equivalentZones = new List();
+ lock (value!._equivalentZones)
+ {
+ value!._equivalentZones.Add(zone);
+ }
+ }
+ else
+ {
+ foreach (TimeZoneInfo tzi in value!._equivalentZones)
+ {
+ if (tzi.Id == id)
+ {
+ zone = tzi;
+ break;
+ }
+ }
+ if (zone == null)
+ {
+ zone = new TimeZoneInfo(id, value!._baseUtcOffset, value!._displayName, value!._standardDisplayName,
+ value!._daylightDisplayName, value!._adjustmentRules, dstDisabled && value!._supportsDaylightSavingTime);
+ lock (value!._equivalentZones)
+ {
+ value!._equivalentZones.Add(zone);
+ }
+ }
+ }
+
+ Debug.Assert(zone != null);
+ value = zone;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private static TimeZoneInfoResult TryGetTimeZoneUsingId(string id, bool dstDisabled, out TimeZoneInfo? value, out Exception? e, CachedData cachedData, bool alwaysFallbackToLocalMachine)
{
Debug.Assert(Monitor.IsEntered(cachedData));
diff --git a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs
index 82ed2ed7c124be..61c6032d0a489c 100644
--- a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs
+++ b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs
@@ -106,7 +106,7 @@ public static IEnumerable