1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 ////////////////////////////////////////////////////////////////////////////
9 // Purpose: This class represents the software preferences of a particular
10 // culture or community. It includes information such as the
11 // language, writing system, and a calendar used by the culture
12 // as well as methods for common operations such as printing
13 // dates and sorting strings.
17 // !!!! NOTE WHEN CHANGING THIS CLASS !!!!
19 // If adding or removing members to this class, please update CultureInfoBaseObject
20 // in ndp/clr/src/vm/object.h. Note, the "actual" layout of the class may be
21 // different than the order in which members are declared. For instance, all
22 // reference types will come first in the class before value types (like ints, bools, etc)
23 // regardless of the order in which they are declared. The best way to see the
24 // actual order of the class is to do a !dumpobj on an instance of the managed
25 // object inside of the debugger.
27 ////////////////////////////////////////////////////////////////////////////
29 using System.Collections.Generic;
30 using System.Diagnostics;
31 using System.Threading;
33 namespace System.Globalization
36 using StringCultureInfoDictionary = Dictionary<string, CultureInfo>;
37 using StringLcidDictionary = Dictionary<int, CultureInfo>;
41 using StringCultureInfoDictionary = LowLevelDictionary<string, CultureInfo>;
42 using StringLcidDictionary = LowLevelDictionary<int, CultureInfo>;
45 public partial class CultureInfo : IFormatProvider, ICloneable
47 //--------------------------------------------------------------------//
48 // Internal Information //
49 //--------------------------------------------------------------------//
51 // We use an RFC4646 type string to construct CultureInfo.
52 // This string is stored in _name and is authoritative.
53 // We use the _cultureData to get the data for our object
55 private bool _isReadOnly;
56 private CompareInfo compareInfo;
57 private TextInfo textInfo;
58 internal NumberFormatInfo numInfo;
59 internal DateTimeFormatInfo dateTimeInfo;
60 private Calendar calendar;
62 // The CultureData instance that we are going to read data from.
63 // For supported culture, this will be the CultureData instance that read data from mscorlib assembly.
64 // For customized culture, this will be the CultureData instance that read data from user customized culture binary file.
66 internal CultureData _cultureData;
68 internal bool _isInherited;
70 private CultureInfo _consoleFallbackCulture;
72 // Names are confusing. Here are 3 names we have:
74 // new CultureInfo() _name _nonSortName _sortName
75 // en-US en-US en-US en-US
76 // de-de_phoneb de-DE_phoneb de-DE de-DE_phoneb
77 // fj-fj (custom) fj-FJ fj-FJ en-US (if specified sort is en-US)
80 // Note that in Silverlight we ask the OS for the text and sort behavior, so the
81 // textinfo and compareinfo names are the same as the name
83 // This has a de-DE, de-DE_phoneb or fj-FJ style name
84 internal string _name;
86 // This will hold the non sorting name to be returned from CultureInfo.Name property.
87 // This has a de-DE style name even for de-DE_phoneb type cultures
88 private string _nonSortName;
90 // This will hold the sorting name to be returned from CultureInfo.SortName property.
91 // This might be completely unrelated to the culture name if a custom culture. Ie en-US for fj-FJ.
92 // Otherwise its the sort name, ie: de-DE or de-DE_phoneb
93 private string _sortName;
95 //--------------------------------------------------------------------//
97 // Static data members
99 //--------------------------------------------------------------------//
101 //Get the current user default culture. This one is almost always used, so we create it by default.
102 private static volatile CultureInfo s_userDefaultCulture;
104 //The culture used in the user interface. This is mostly used to load correct localized resources.
105 private static volatile CultureInfo s_userDefaultUICulture;
107 // All of the following will be created on demand.
110 // WARNING: We allow diagnostic tools to directly inspect these three members (s_InvariantCultureInfo, s_DefaultThreadCurrentUICulture and s_DefaultThreadCurrentCulture)
111 // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details.
112 // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools.
113 // Get in touch with the diagnostics team if you have questions.
115 //The Invariant culture;
116 private static volatile CultureInfo s_InvariantCultureInfo;
118 //These are defaults that we use if a thread has not opted into having an explicit culture
119 private static volatile CultureInfo s_DefaultThreadCurrentUICulture;
120 private static volatile CultureInfo s_DefaultThreadCurrentCulture;
122 internal static AsyncLocal<CultureInfo> s_asyncLocalCurrentCulture;
123 internal static AsyncLocal<CultureInfo> s_asyncLocalCurrentUICulture;
125 internal static void AsyncLocalSetCurrentCulture(AsyncLocalValueChangedArgs<CultureInfo> args)
127 Thread.m_CurrentCulture = args.CurrentValue;
130 internal static void AsyncLocalSetCurrentUICulture(AsyncLocalValueChangedArgs<CultureInfo> args)
132 Thread.m_CurrentUICulture = args.CurrentValue;
135 private static readonly Lock _lock = new Lock();
136 private static volatile StringCultureInfoDictionary s_NameCachedCultures;
137 private static volatile StringLcidDictionary s_LcidCachedCultures;
139 //The parent culture.
140 private CultureInfo _parent;
142 // LOCALE constants of interest to us internally and privately for LCID functions
143 // (ie: avoid using these and use names if possible)
144 internal const int LOCALE_NEUTRAL = 0x0000;
145 private const int LOCALE_USER_DEFAULT = 0x0400;
146 private const int LOCALE_SYSTEM_DEFAULT = 0x0800;
147 internal const int LOCALE_CUSTOM_UNSPECIFIED = 0x1000;
148 internal const int LOCALE_CUSTOM_DEFAULT = 0x0c00;
149 internal const int LOCALE_INVARIANT = 0x007F;
152 // The CultureData instance that reads the data provided by our CultureData class.
154 // Using a field initializer rather than a static constructor so that the whole class can be lazy
156 private static readonly bool init = Init();
157 private static bool Init()
159 if (s_InvariantCultureInfo == null)
161 CultureInfo temp = new CultureInfo("", false);
162 temp._isReadOnly = true;
163 s_InvariantCultureInfo = temp;
166 s_userDefaultCulture = GetUserDefaultCulture();
167 s_userDefaultUICulture = GetUserDefaultUILanguage();
171 ////////////////////////////////////////////////////////////////////////
173 // CultureInfo Constructors
175 ////////////////////////////////////////////////////////////////////////
178 public CultureInfo(String name)
184 public CultureInfo(String name, bool useUserOverride)
188 throw new ArgumentNullException(nameof(name),
189 SR.ArgumentNull_String);
192 InitializeFromName(name, useUserOverride);
195 private CultureInfo(CultureData cultureData)
197 Debug.Assert(cultureData != null);
198 _cultureData = cultureData;
199 _name = cultureData.CultureName;
200 _isInherited = false;
203 private static CultureInfo CreateCultureInfoNoThrow(string name, bool useUserOverride)
205 Debug.Assert(name != null);
206 CultureData cultureData = CultureData.GetCultureData(name, useUserOverride);
207 if (cultureData == null)
212 return new CultureInfo(cultureData);
214 public CultureInfo(int culture) : this(culture, true)
218 public CultureInfo(int culture, bool useUserOverride)
220 // We don't check for other invalid LCIDS here...
223 throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum);
226 InitializeFromCultureId(culture, useUserOverride);
229 private void InitializeFromCultureId(int culture, bool useUserOverride)
233 case LOCALE_CUSTOM_DEFAULT:
234 case LOCALE_SYSTEM_DEFAULT:
236 case LOCALE_USER_DEFAULT:
237 case LOCALE_CUSTOM_UNSPECIFIED:
238 // Can't support unknown custom cultures and we do not support neutral or
239 // non-custom user locales.
240 throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
243 // Now see if this LCID is supported in the system default CultureData table.
244 _cultureData = CultureData.GetCultureData(culture, useUserOverride);
247 _isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo));
248 _name = _cultureData.CultureName;
251 private void InitializeFromName(string name, bool useUserOverride)
253 // Get our data providing record
254 _cultureData = CultureData.GetCultureData(name, useUserOverride);
256 if (_cultureData == null)
258 throw new CultureNotFoundException(nameof(name), name, SR.Argument_CultureNotSupported);
261 _name = _cultureData.CultureName;
262 _isInherited = (this.GetType() != typeof(System.Globalization.CultureInfo));
265 // Constructor called by SQL Server's special munged culture - creates a culture with
266 // a TextInfo and CompareInfo that come from a supplied alternate source. This object
267 // is ALWAYS read-only.
268 // Note that we really cannot use an LCID version of this override as the cached
269 // name we create for it has to include both names, and the logic for this is in
270 // the GetCultureInfo override *only*.
271 internal CultureInfo(String cultureName, String textAndCompareCultureName)
273 if (cultureName == null)
275 throw new ArgumentNullException(nameof(cultureName),SR.ArgumentNull_String);
278 _cultureData = CultureData.GetCultureData(cultureName, false);
279 if (_cultureData == null)
280 throw new CultureNotFoundException(nameof(cultureName), cultureName, SR.Argument_CultureNotSupported);
282 _name = _cultureData.CultureName;
284 CultureInfo altCulture = GetCultureInfo(textAndCompareCultureName);
285 compareInfo = altCulture.CompareInfo;
286 textInfo = altCulture.TextInfo;
289 // We do this to try to return the system UI language and the default user languages
290 // This method will fallback if this fails (like Invariant)
292 // TODO: It would appear that this is only ever called with userOveride = true
293 // and this method only has one caller. Can we fold it into the caller?
294 private static CultureInfo GetCultureByName(String name, bool userOverride)
296 CultureInfo ci = null;
297 // Try to get our culture
300 ci = userOverride ? new CultureInfo(name) : CultureInfo.GetCultureInfo(name);
302 catch (ArgumentException)
308 ci = InvariantCulture;
315 // Return a specific culture. A tad irrelevent now since we always return valid data
316 // for neutral locales.
318 // Note that there's interesting behavior that tries to find a smaller name, ala RFC4647,
319 // if we can't find a bigger name. That doesn't help with things like "zh" though, so
320 // the approach is of questionable value
322 public static CultureInfo CreateSpecificCulture(String name)
328 culture = new CultureInfo(name);
330 catch (ArgumentException)
332 // When CultureInfo throws this exception, it may be because someone passed the form
333 // like "az-az" because it came out of an http accept lang. We should try a little
334 // parsing to perhaps fall back to "az" here and use *it* to create the neutral.
339 for (idx = 0; idx < name.Length; idx++)
341 if ('-' == name[idx])
345 culture = new CultureInfo(name.Substring(0, idx));
348 catch (ArgumentException)
350 // throw the original exception so the name in the string will be right
358 // nothing to save here; throw the original exception
363 // In the most common case, they've given us a specific culture, so we'll just return that.
364 if (!(culture.IsNeutralCulture))
369 return (new CultureInfo(culture._cultureData.SSPECIFICCULTURE));
372 internal static bool VerifyCultureName(String cultureName, bool throwException)
374 // This function is used by ResourceManager.GetResourceFileName().
375 // ResourceManager searches for resource using CultureInfo.Name,
376 // so we should check against CultureInfo.Name.
378 for (int i = 0; i < cultureName.Length; i++)
380 char c = cultureName[i];
381 // TODO: Names can only be RFC4646 names (ie: a-zA-Z0-9) while this allows any unicode letter/digit
382 if (Char.IsLetterOrDigit(c) || c == '-' || c == '_')
388 throw new ArgumentException(SR.Format(SR.Argument_InvalidResourceCultureName, cultureName));
395 internal static bool VerifyCultureName(CultureInfo culture, bool throwException)
397 //If we have an instance of one of our CultureInfos, the user can't have changed the
398 //name and we know that all names are valid in files.
399 if (!culture._isInherited)
404 return VerifyCultureName(culture.Name, throwException);
407 // We need to store the override from the culture data record.
408 private bool _useUserOverride;
410 internal static CultureInfo GetCurrentUICultureNoAppX()
412 CultureInfo ci = GetUserDefaultCultureCacheOverride();
418 if (Thread.m_CurrentUICulture != null)
420 return Thread.m_CurrentUICulture;
423 ci = s_DefaultThreadCurrentUICulture;
429 return UserDefaultUICulture;
432 internal static CultureInfo UserDefaultUICulture
436 // if s_userDefaultUICulture == null means CultureInfo statics didn't get initialized yet. this can happen if there early static
437 // method get executed which eventually hit the cultureInfo code while CultureInfo statics didn’t get chance to initialize
438 if (s_userDefaultUICulture == null)
443 Debug.Assert(s_userDefaultUICulture != null);
444 return s_userDefaultUICulture;
448 public static CultureInfo InstalledUICulture
452 if (s_userDefaultCulture == null)
456 Debug.Assert(s_userDefaultCulture != null, "[CultureInfo.InstalledUICulture] s_userDefaultCulture != null");
457 return s_userDefaultCulture;
461 public static CultureInfo DefaultThreadCurrentCulture
463 get { return s_DefaultThreadCurrentCulture; }
466 // If you add pre-conditions to this method, check to see if you also need to
467 // add them to Thread.CurrentCulture.set.
469 s_DefaultThreadCurrentCulture = value;
473 public static CultureInfo DefaultThreadCurrentUICulture
475 get { return s_DefaultThreadCurrentUICulture; }
478 //If they're trying to use a Culture with a name that we can't use in resource lookup,
479 //don't even let them set it on the thread.
481 // If you add more pre-conditions to this method, check to see if you also need to
482 // add them to Thread.CurrentUICulture.set.
486 CultureInfo.VerifyCultureName(value, true);
489 s_DefaultThreadCurrentUICulture = value;
493 ////////////////////////////////////////////////////////////////////////
497 // This instance provides methods, for example for casing and sorting,
498 // that are independent of the system and current user settings. It
499 // should be used only by processes such as some system services that
500 // require such invariant results (eg. file systems). In general,
501 // the results are not linguistically correct and do not match any
504 ////////////////////////////////////////////////////////////////////////
507 public static CultureInfo InvariantCulture
511 return (s_InvariantCultureInfo);
516 ////////////////////////////////////////////////////////////////////////
520 // Return the parent CultureInfo for the current instance.
522 ////////////////////////////////////////////////////////////////////////
524 public virtual CultureInfo Parent
530 string parentName = _cultureData.SPARENT;
532 if (String.IsNullOrEmpty(parentName))
534 _parent = InvariantCulture;
538 _parent = CreateCultureInfoNoThrow(parentName, _cultureData.UseUserOverride);
541 // For whatever reason our IPARENT or SPARENT wasn't correct, so use invariant
542 // We can't allow ourselves to fail. In case of custom cultures the parent of the
543 // current custom culture isn't installed.
544 _parent = InvariantCulture;
552 public virtual int LCID
556 return (this._cultureData.ILANGUAGE);
560 public virtual int KeyboardLayoutId
564 return _cultureData.IINPUTLANGUAGEHANDLE;
568 public static CultureInfo[] GetCultures(CultureTypes types)
570 // internally we treat UserCustomCultures as Supplementals but v2
571 // treats as Supplementals and Replacements
572 if ((types & CultureTypes.UserCustomCulture) == CultureTypes.UserCustomCulture)
574 types |= CultureTypes.ReplacementCultures;
576 return (CultureData.GetCultures(types));
579 ////////////////////////////////////////////////////////////////////////
583 // Returns the full name of the CultureInfo. The name is in format like
584 // "en-US" This version does NOT include sort information in the name.
586 ////////////////////////////////////////////////////////////////////////
587 public virtual String Name
591 // We return non sorting name here.
592 if (_nonSortName == null)
594 _nonSortName = _cultureData.SNAME;
595 if (_nonSortName == null)
597 _nonSortName = String.Empty;
604 // This one has the sort information (ie: de-DE_phoneb)
605 internal String SortName
609 if (_sortName == null)
611 _sortName = _cultureData.SCOMPAREINFO;
618 public string IetfLanguageTag
622 // special case the compatibility cultures
635 ////////////////////////////////////////////////////////////////////////
639 // Returns the full name of the CultureInfo in the localized language.
640 // For example, if the localized language of the runtime is Spanish and the CultureInfo is
641 // US English, "Ingles (Estados Unidos)" will be returned.
643 ////////////////////////////////////////////////////////////////////////
644 public virtual String DisplayName
648 Debug.Assert(_name != null, "[CultureInfo.DisplayName] Always expect _name to be set");
650 return _cultureData.SLOCALIZEDDISPLAYNAME;
654 ////////////////////////////////////////////////////////////////////////
658 // Returns the full name of the CultureInfo in the native language.
659 // For example, if the CultureInfo is US English, "English
660 // (United States)" will be returned.
662 ////////////////////////////////////////////////////////////////////////
663 public virtual String NativeName
667 return (_cultureData.SNATIVEDISPLAYNAME);
671 ////////////////////////////////////////////////////////////////////////
675 // Returns the full name of the CultureInfo in English.
676 // For example, if the CultureInfo is US English, "English
677 // (United States)" will be returned.
679 ////////////////////////////////////////////////////////////////////////
680 public virtual String EnglishName
684 return (_cultureData.SENGDISPLAYNAME);
689 public virtual String TwoLetterISOLanguageName
693 return (_cultureData.SISO639LANGNAME);
698 public virtual String ThreeLetterISOLanguageName
702 return _cultureData.SISO639LANGNAME2;
706 ////////////////////////////////////////////////////////////////////////
708 // ThreeLetterWindowsLanguageName
710 // Returns the 3 letter windows language name for the current instance. eg: "ENU"
711 // The ISO names are much preferred
713 ////////////////////////////////////////////////////////////////////////
714 public virtual String ThreeLetterWindowsLanguageName
718 return _cultureData.SABBREVLANGNAME;
722 ////////////////////////////////////////////////////////////////////////
724 // CompareInfo Read-Only Property
726 // Gets the CompareInfo for this culture.
728 ////////////////////////////////////////////////////////////////////////
729 public virtual CompareInfo CompareInfo
733 if (this.compareInfo == null)
735 // Since CompareInfo's don't have any overrideable properties, get the CompareInfo from
736 // the Non-Overridden CultureInfo so that we only create one CompareInfo per culture
737 this.compareInfo = UseUserOverride
738 ? GetCultureInfo(this._name).CompareInfo
739 : new CompareInfo(this);
741 return (compareInfo);
745 ////////////////////////////////////////////////////////////////////////
749 // Gets the TextInfo for this culture.
751 ////////////////////////////////////////////////////////////////////////
752 public virtual TextInfo TextInfo
756 if (textInfo == null)
758 // Make a new textInfo
759 TextInfo tempTextInfo = new TextInfo(_cultureData);
760 tempTextInfo.SetReadOnlyState(_isReadOnly);
761 textInfo = tempTextInfo;
767 ////////////////////////////////////////////////////////////////////////
771 // Implements Object.Equals(). Returns a boolean indicating whether
772 // or not object refers to the same CultureInfo as the current instance.
774 ////////////////////////////////////////////////////////////////////////
777 public override bool Equals(Object value)
779 if (Object.ReferenceEquals(this, value))
782 CultureInfo that = value as CultureInfo;
786 // using CompareInfo to verify the data passed through the constructor
787 // CultureInfo(String cultureName, String textAndCompareCultureName)
789 return (this.Name.Equals(that.Name) && this.CompareInfo.Equals(that.CompareInfo));
796 ////////////////////////////////////////////////////////////////////////
800 // Implements Object.GetHashCode(). Returns the hash code for the
801 // CultureInfo. The hash code is guaranteed to be the same for CultureInfo A
802 // and B where A.Equals(B) is true.
804 ////////////////////////////////////////////////////////////////////////
806 public override int GetHashCode()
808 return (this.Name.GetHashCode() + this.CompareInfo.GetHashCode());
812 ////////////////////////////////////////////////////////////////////////
816 // Implements Object.ToString(). Returns the name of the CultureInfo,
817 // eg. "de-DE_phoneb", "en-US", or "fj-FJ".
819 ////////////////////////////////////////////////////////////////////////
822 public override String ToString()
828 public virtual Object GetFormat(Type formatType)
830 if (formatType == typeof(NumberFormatInfo))
831 return (NumberFormat);
832 if (formatType == typeof(DateTimeFormatInfo))
833 return (DateTimeFormat);
837 public virtual bool IsNeutralCulture
841 return _cultureData.IsNeutralCulture;
845 public CultureTypes CultureTypes
849 CultureTypes types = 0;
851 if (_cultureData.IsNeutralCulture)
852 types |= CultureTypes.NeutralCultures;
854 types |= CultureTypes.SpecificCultures;
856 types |= _cultureData.IsWin32Installed ? CultureTypes.InstalledWin32Cultures : 0;
858 // Disable warning 618: System.Globalization.CultureTypes.FrameworkCultures' is obsolete
859 #pragma warning disable 618
860 types |= _cultureData.IsFramework ? CultureTypes.FrameworkCultures : 0;
862 #pragma warning restore 618
863 types |= _cultureData.IsSupplementalCustomCulture ? CultureTypes.UserCustomCulture : 0;
864 types |= _cultureData.IsReplacementCulture ? CultureTypes.ReplacementCultures | CultureTypes.UserCustomCulture : 0;
870 public virtual NumberFormatInfo NumberFormat
876 NumberFormatInfo temp = new NumberFormatInfo(_cultureData);
877 temp.isReadOnly = _isReadOnly;
878 Interlocked.CompareExchange(ref numInfo, temp, null);
886 throw new ArgumentNullException(nameof(value), SR.ArgumentNull_Obj);
893 ////////////////////////////////////////////////////////////////////////
895 // GetDateTimeFormatInfo
897 // Create a DateTimeFormatInfo, and fill in the properties according to
900 ////////////////////////////////////////////////////////////////////////
901 public virtual DateTimeFormatInfo DateTimeFormat
905 if (dateTimeInfo == null)
907 // Change the calendar of DTFI to the specified calendar of this CultureInfo.
908 DateTimeFormatInfo temp = new DateTimeFormatInfo(_cultureData, this.Calendar);
909 temp._isReadOnly = _isReadOnly;
910 Interlocked.CompareExchange(ref dateTimeInfo, temp, null);
912 return (dateTimeInfo);
919 throw new ArgumentNullException(nameof(value), SR.ArgumentNull_Obj);
922 dateTimeInfo = value;
926 public void ClearCachedData()
928 Init(); // reset the default culture values
930 RegionInfo.s_currentRegionInfo = null;
931 #pragma warning disable 0618 // disable the obsolete warning
932 TimeZone.ResetTimeZone();
933 #pragma warning restore 0618
934 TimeZoneInfo.ClearCachedData();
935 s_LcidCachedCultures = null;
936 s_NameCachedCultures = null;
938 CultureData.ClearCachedData();
941 /*=================================GetCalendarInstance==========================
942 **Action: Map a Win32 CALID to an instance of supported calendar.
943 **Returns: An instance of calendar.
944 **Arguments: calType The Win32 CALID
946 ** Shouldn't throw exception since the calType value is from our data table or from Win32 registry.
947 ** If we are in trouble (like getting a weird value from Win32 registry), just return the GregorianCalendar.
948 ============================================================================*/
949 internal static Calendar GetCalendarInstance(CalendarId calType)
951 if (calType == CalendarId.GREGORIAN)
953 return (new GregorianCalendar());
955 return GetCalendarInstanceRare(calType);
958 //This function exists as a shortcut to prevent us from loading all of the non-gregorian
959 //calendars unless they're required.
960 internal static Calendar GetCalendarInstanceRare(CalendarId calType)
962 Debug.Assert(calType != CalendarId.GREGORIAN, "calType!=CalendarId.GREGORIAN");
966 case CalendarId.GREGORIAN_US: // Gregorian (U.S.) calendar
967 case CalendarId.GREGORIAN_ME_FRENCH: // Gregorian Middle East French calendar
968 case CalendarId.GREGORIAN_ARABIC: // Gregorian Arabic calendar
969 case CalendarId.GREGORIAN_XLIT_ENGLISH: // Gregorian Transliterated English calendar
970 case CalendarId.GREGORIAN_XLIT_FRENCH: // Gregorian Transliterated French calendar
971 return (new GregorianCalendar((GregorianCalendarTypes)calType));
972 case CalendarId.TAIWAN: // Taiwan Era calendar
973 return (new TaiwanCalendar());
974 case CalendarId.JAPAN: // Japanese Emperor Era calendar
975 return (new JapaneseCalendar());
976 case CalendarId.KOREA: // Korean Tangun Era calendar
977 return (new KoreanCalendar());
978 case CalendarId.THAI: // Thai calendar
979 return (new ThaiBuddhistCalendar());
980 case CalendarId.HIJRI: // Hijri (Arabic Lunar) calendar
981 return (new HijriCalendar());
982 case CalendarId.HEBREW: // Hebrew (Lunar) calendar
983 return (new HebrewCalendar());
984 case CalendarId.UMALQURA:
985 return (new UmAlQuraCalendar());
986 case CalendarId.PERSIAN:
987 return (new PersianCalendar());
989 return (new GregorianCalendar());
992 /*=================================Calendar==========================
993 **Action: Return/set the default calendar used by this culture.
994 ** This value can be overridden by regional option if this is a current culture.
998 ** ArgumentNull_Obj if the set value is null.
999 ============================================================================*/
1000 public virtual Calendar Calendar
1004 if (calendar == null)
1006 Debug.Assert(_cultureData.CalendarIds.Length > 0, "_cultureData.CalendarIds.Length > 0");
1007 // Get the default calendar for this culture. Note that the value can be
1008 // from registry if this is a user default culture.
1009 Calendar newObj = _cultureData.DefaultCalendar;
1011 System.Threading.Interlocked.MemoryBarrier();
1012 newObj.SetReadOnlyState(_isReadOnly);
1019 /*=================================OptionCalendars==========================
1020 **Action: Return an array of the optional calendar for this culture.
1021 **Returns: an array of Calendar.
1024 ============================================================================*/
1027 public virtual Calendar[] OptionalCalendars
1032 // This property always returns a new copy of the calendar array.
1034 CalendarId[] calID = _cultureData.CalendarIds;
1035 Calendar[] cals = new Calendar[calID.Length];
1036 for (int i = 0; i < cals.Length; i++)
1038 cals[i] = GetCalendarInstance(calID[i]);
1044 public bool UseUserOverride
1048 return _cultureData.UseUserOverride;
1052 public CultureInfo GetConsoleFallbackUICulture()
1054 CultureInfo temp = _consoleFallbackCulture;
1057 temp = CreateSpecificCulture(_cultureData.SCONSOLEFALLBACKNAME);
1059 _consoleFallbackCulture = temp;
1064 public virtual Object Clone()
1066 CultureInfo ci = (CultureInfo)MemberwiseClone();
1067 ci._isReadOnly = false;
1069 //If this is exactly our type, we can make certain optimizations so that we don't allocate NumberFormatInfo or DTFI unless
1070 //they've already been allocated. If this is a derived type, we'll take a more generic codepath.
1073 if (this.dateTimeInfo != null)
1075 ci.dateTimeInfo = (DateTimeFormatInfo)this.dateTimeInfo.Clone();
1077 if (this.numInfo != null)
1079 ci.numInfo = (NumberFormatInfo)this.numInfo.Clone();
1084 ci.DateTimeFormat = (DateTimeFormatInfo)this.DateTimeFormat.Clone();
1085 ci.NumberFormat = (NumberFormatInfo)this.NumberFormat.Clone();
1088 if (textInfo != null)
1090 ci.textInfo = (TextInfo)textInfo.Clone();
1093 if (calendar != null)
1095 ci.calendar = (Calendar)calendar.Clone();
1101 public static CultureInfo ReadOnly(CultureInfo ci)
1105 throw new ArgumentNullException(nameof(ci));
1112 CultureInfo newInfo = (CultureInfo)(ci.MemberwiseClone());
1114 if (!ci.IsNeutralCulture)
1116 //If this is exactly our type, we can make certain optimizations so that we don't allocate NumberFormatInfo or DTFI unless
1117 //they've already been allocated. If this is a derived type, we'll take a more generic codepath.
1118 if (!ci._isInherited)
1120 if (ci.dateTimeInfo != null)
1122 newInfo.dateTimeInfo = DateTimeFormatInfo.ReadOnly(ci.dateTimeInfo);
1124 if (ci.numInfo != null)
1126 newInfo.numInfo = NumberFormatInfo.ReadOnly(ci.numInfo);
1131 newInfo.DateTimeFormat = DateTimeFormatInfo.ReadOnly(ci.DateTimeFormat);
1132 newInfo.NumberFormat = NumberFormatInfo.ReadOnly(ci.NumberFormat);
1136 if (ci.textInfo != null)
1138 newInfo.textInfo = TextInfo.ReadOnly(ci.textInfo);
1141 if (ci.calendar != null)
1143 newInfo.calendar = Calendar.ReadOnly(ci.calendar);
1146 // Don't set the read-only flag too early.
1147 // We should set the read-only flag here. Otherwise, info.DateTimeFormat will not be able to set.
1148 newInfo._isReadOnly = true;
1154 public bool IsReadOnly
1158 return (_isReadOnly);
1162 private void VerifyWritable()
1166 throw new InvalidOperationException(SR.InvalidOperation_ReadOnly);
1170 // For resource lookup, we consider a culture the invariant culture by name equality.
1171 // We perform this check frequently during resource lookup, so adding a property for
1172 // improved readability.
1173 internal bool HasInvariantCultureName
1175 get { return Name == CultureInfo.InvariantCulture.Name; }
1178 // Helper function both both overloads of GetCachedReadOnlyCulture. If lcid is 0, we use the name.
1179 // If lcid is -1, use the altName and create one of those special SQL cultures.
1180 internal static CultureInfo GetCultureInfoHelper(int lcid, string name, string altName)
1182 // retval is our return value.
1185 // Temporary hashtable for the names.
1186 StringCultureInfoDictionary tempNameHT = s_NameCachedCultures;
1190 name = CultureData.AnsiToLower(name);
1193 if (altName != null)
1195 altName = CultureData.AnsiToLower(altName);
1198 // We expect the same result for both hashtables, but will test individually for added safety.
1199 if (tempNameHT == null)
1201 tempNameHT = new StringCultureInfoDictionary();
1205 // If we are called by name, check if the object exists in the hashtable. If so, return it.
1206 if (lcid == -1 || lcid == 0)
1211 ret = tempNameHT.TryGetValue(lcid == 0 ? name : name + '\xfffd' + altName, out retval);
1214 if (ret && retval != null)
1221 // Next, the Lcid table.
1222 StringLcidDictionary tempLcidHT = s_LcidCachedCultures;
1224 if (tempLcidHT == null)
1226 // Case insensitive is not an issue here, save the constructor call.
1227 tempLcidHT = new StringLcidDictionary();
1231 // If we were called by Lcid, check if the object exists in the table. If so, return it.
1237 ret = tempLcidHT.TryGetValue(lcid, out retval);
1239 if (ret && retval != null)
1246 // We now have two temporary hashtables and the desired object was not found.
1247 // We'll construct it. We catch any exceptions from the constructor call and return null.
1253 // call the private constructor
1254 retval = new CultureInfo(name, altName);
1258 retval = new CultureInfo(name, false);
1262 retval = new CultureInfo(lcid, false);
1266 catch (ArgumentException)
1271 // Set it to read-only
1272 retval._isReadOnly = true;
1278 // This new culture will be added only to the name hash table.
1279 tempNameHT[name + '\xfffd' + altName] = retval;
1281 // when lcid == -1 then TextInfo object is already get created and we need to set it as read only.
1282 retval.TextInfo.SetReadOnlyState(true);
1286 // Remember our name (as constructed). Do NOT use alternate sort name versions because
1287 // we have internal state representing the sort. (So someone would get the wrong cached version)
1288 string newName = CultureData.AnsiToLower(retval._name);
1290 // We add this new culture info object to both tables.
1293 tempNameHT[newName] = retval;
1300 tempLcidHT[lcid] = retval;
1304 // Copy the two hashtables to the corresponding member variables. This will potentially overwrite
1305 // new tables simultaneously created by a new thread, but maximizes thread safety.
1308 // Only when we modify the lcid hash table, is there a need to overwrite.
1309 s_LcidCachedCultures = tempLcidHT;
1312 s_NameCachedCultures = tempNameHT;
1314 // Finally, return our new CultureInfo object.
1318 // Gets a cached copy of the specified culture from an internal hashtable (or creates it
1319 // if not found). (LCID version)... use named version
1320 public static CultureInfo GetCultureInfo(int culture)
1322 // Must check for -1 now since the helper function uses the value to signal
1323 // the altCulture code path for SQL Server.
1324 // Also check for zero as this would fail trying to add as a key to the hash.
1327 throw new ArgumentOutOfRangeException(nameof(culture), SR.ArgumentOutOfRange_NeedPosNum);
1329 CultureInfo retval = GetCultureInfoHelper(culture, null, null);
1332 throw new CultureNotFoundException(nameof(culture), culture, SR.Argument_CultureNotSupported);
1337 // Gets a cached copy of the specified culture from an internal hashtable (or creates it
1338 // if not found). (Named version)
1339 public static CultureInfo GetCultureInfo(string name)
1341 // Make sure we have a valid, non-zero length string as name
1344 throw new ArgumentNullException(nameof(name));
1347 CultureInfo retval = GetCultureInfoHelper(0, name, null);
1350 throw new CultureNotFoundException(
1351 nameof(name), name, SR.Argument_CultureNotSupported);
1356 // Gets a cached copy of the specified culture from an internal hashtable (or creates it
1358 public static CultureInfo GetCultureInfo(string name, string altName)
1360 // Make sure we have a valid, non-zero length string as name
1363 throw new ArgumentNullException(nameof(name));
1366 if (altName == null)
1368 throw new ArgumentNullException(nameof(altName));
1372 CultureInfo retval = GetCultureInfoHelper(-1, name, altName);
1375 throw new CultureNotFoundException("name or altName",
1376 SR.Format(SR.Argument_OneOfCulturesNotSupported, name, altName));
1381 // This function is deprecated, we don't like it
1382 public static CultureInfo GetCultureInfoByIetfLanguageTag(string name)
1384 // Disallow old zh-CHT/zh-CHS names
1385 if (name == "zh-CHT" || name == "zh-CHS")
1387 throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_CultureIetfNotSupported, name));
1390 CultureInfo ci = GetCultureInfo(name);
1392 // Disallow alt sorts and es-es_TS
1393 if (ci.LCID > 0xffff || ci.LCID == 0x040a)
1395 throw new CultureNotFoundException(nameof(name), SR.Format(SR.Argument_CultureIetfNotSupported, name));