Xamarin.forms: Thai crash: Not a valid calendar for the given culture.

Created on 8 Oct 2018  ·  21Comments  ·  Source: xamarin/Xamarin.Forms

Thai crash Not a valid calendar for the given culture.

_Submitted by a community member on 2017-08-28 08:25 UTC_

Previously I reported bug with Thai culture (https://bugzilla.xamarin.com/show_bug.cgi?id=31228).
It was indeed fixed and now works fine, but now I experience another bug which can be reproduced using the attached project. All encodings are checked and included in the project.
In order to reproduce it set android phone language (I tested it on android 4.4) to be Thai language.
Put the following line in the OnCreate:
var date = DateTime.Parse("2000-31-07");
or
var date = DateTime.ParseExact("2000-31-07","yyyy-dd-MM",null);
And it will crash at runtime with the following exception:
UNHANDLED EXCEPTION:
System.ArgumentOutOfRangeException: Not a valid calendar for the given culture.
Parameter name: value
at System.Globalization.DateTimeFormatInfo.set_Calendar (System.Globalization.Calendar value) [0x00154] in <9e5b8ac258034478aed80c25b6e73e52\>:0
at System.Globalization.DateTimeFormatInfo..ctor (System.Globalization.CultureData cultureData, System.Globalization.Calendar cal) [0x00022] in <9e5b8ac258034478aed80c25b6e73e52\>:0
at System.Globalization.CultureInfo.get_DateTimeFormat () [0x00033] in <9e5b8ac258034478aed80c25b6e73e52\>:0
at System.Globalization.CultureInfo.GetFormat (System.Type formatType) [0x0002f] in <9e5b8ac258034478aed80c25b6e73e52\>:0
at System.Globalization.DateTimeFormatInfo.get_CurrentInfo () [0x00021] in <9e5b8ac258034478aed80c25b6e73e52\>:0
at System.DateTime.Parse (System.String s) [0x00000] in <9e5b8ac258034478aed80c25b6e73e52\>:0
at ThaiBug2.MainActivity.OnCreate (Android.OS.Bundle savedInstanceState) [0x00009] in C:\Users\username\Documents\Visual Studio 2017\Projects\ThaiBug2\ThaiBug2\MainActivity.cs:14
at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_savedInstanceState) [0x0000f] in <947c88b5da6c4d94ab126d7acb391dc6\>:0
at (wrapper dynamic-method) System.Object:bc4a6bc1-1943-4d65-9d85-57b6d5875e98 (intptr,intptr,intptr)


_Reference: https://bugzilla.xamarin.com/show_bug.cgi?id=59077_

Appears related to #3620

I'm amending this because I just recently observed the crash on iOS. I had previously seen the crash on Android and the suggested fix from the Bugzilla thread to load the calendar in MainActivity.cs worked.

Present with Xamarin.Forms 3.1.0.697729 (Unable to upgrade to 3.2 yet due to another issue)

needs-info ❓

Most helpful comment

I have some Arabic users I believe based on positive app store reviews. But also just got bit by the Thai issue via CultureInfo.CurrentCulture. I am being more defensive and calling the following one time:

        // These classes won't be linked away because of the code,
        // but we also won't have to construct unnecessarily either,
        // hence the if statement with (hopefully) impossible
        // runtime condition.
        //
        // This is to resolve crash at CultureInfo.CurrentCulture
        // when language is set to Thai. See
        // https://github.com/xamarin/Xamarin.Forms/issues/4037
        if (Environment.CurrentDirectory == "_never_POSSIBLE_")
        {
            new System.Globalization.ChineseLunisolarCalendar();
            new System.Globalization.HebrewCalendar();
            new System.Globalization.HijriCalendar();
            new System.Globalization.JapaneseCalendar();
            new System.Globalization.JapaneseLunisolarCalendar();
            new System.Globalization.KoreanCalendar();
            new System.Globalization.KoreanLunisolarCalendar();
            new System.Globalization.PersianCalendar();
            new System.Globalization.TaiwanCalendar();
            new System.Globalization.TaiwanLunisolarCalendar();
            new System.Globalization.ThaiBuddhistCalendar();
            new System.Globalization.UmAlQuraCalendar();
        }

All 21 comments

@BioTurboNick You've observed this crash on iOS with the attached repro project?

@hartez No, in my own project. Haven't attempted a reproduction project yet. However, the crash report from my app in the wild was identical:

System.ArgumentOutOfRangeException: Not a valid calendar for the given culture. Parameter name: value
DateTimeFormatInfo.set_Calendar (System.Globalization.Calendar value)
System.Globalization.DateTimeFormatInfo..ctor (System.Globalization.CultureData cultureData, System.Globalization.Calendar cal) <0x10027ecb0 + 0x0007b> in <7d5a05cfcb09432d8cc656b9d781e54b#ff6acbce5e9e0b3c660094ef8ca6f68c>:0
CultureInfo.get_DateTimeFormat ()
[…]

Country: Thailand
Language: Thai
Device: iPhone SE
OS: iOS 10.3.1 (14E304)

I think I can confirm that the workaround for Android also works for iOS:

In AppDelegate.cs, call this from FinishedLaunching():

        private static void InitThaiCalendarCrashFix()
        {
            var localeIdentifier = NSLocale.CurrentLocale.LocaleIdentifier;
            if (localeIdentifier == "th_TH")
            {
                new System.Globalization.ThaiBuddhistCalendar();
            }
        }

There was an issue with Arabic before also, I'm not sure if it is still existing.

I think I can confirm that the workaround for Android also works for iOS:

In AppDelegate.cs, call this from FinishedLaunching():

        private static void InitThaiCalendarCrashFix()
        {
            var localeIdentifier = NSLocale.CurrentLocale.LocaleIdentifier;
            if (localeIdentifier == "th_TH")
            {
                new System.Globalization.ThaiBuddhistCalendar();
            }
        }

@BioTurboNick

If you've included all of the additional encodings and the workaround above is fixing the issue, then it's likely that the problem is the linker. What is your "Linker behavior" setting (in the iOS project options)?

For my part, I don't include any encodings. That statement is from the Bugzilla original. I'm using Unicode exclusively in my app.

iOS linker is set to "Link Framework SDKs Only"; Android to "SDK Assemblies Only"

Bump!
I ran into this issue, seems as though the compiler is stripping some of the System.Globalization namespace. We worked around this issue by setting the Linker to None. For both iOS and Android projects.

Through testing, we also found that... CultureInfo.CreateSpecificCulture("th-TH").DateTimeFormat.DayNames ... returns an empty list. This issue was raised back in 2014 https://bugzilla.xamarin.com/show_bug.cgi?id=22726 but I cannot find any bug on here raised.

@Maxgamerboy1 If you find that turning off the linker fixes your problem, then take a look at https://github.com/xamarin/Xamarin.Forms/issues/3620#issuecomment-415925727 and see if add those exceptions to the "Skip linking assemblies" option addresses the problem with the linker enabled.

As for the empty DayNames list, if you're still having that problem then please open a Github issue with a link back to the original Bugzilla issue and we will take a look. Thanks!

For my part, I don't include any encodings. That statement is from the Bugzilla original. I'm using Unicode exclusively in my app.

iOS linker is set to "Link Framework SDKs Only"; Android to "SDK Assemblies Only"

@BioTurboNick If you aren't including the additional encodings in your project, then the Thai calendar is not going to work without the workaround. You'll need to include them in your build and make sure that they aren't linked out (see https://github.com/xamarin/Xamarin.Forms/issues/3620#issuecomment-415925727)).

@hartez - But it does work. I've tested it repeatedly.

What is the reason that the linker is unable to include the parts necessary for Thai culture by default, and why does it seem to only affect Thai?

Update: This comment is specific to addressing the problem on iOS

A couple of things:

For iOS, if you set Linking to "Don't Link", I believe you won't have any problems because all of the international codesets are included by default.

If you are using any other Linking options, then you'll need to explicitly retain the international codesets you want by using the "Codesets" checkboxes under "Internationalization" in your iOS project options.

why does it seem to only affect Thai

It's not that Thai is the only affected language option; that's just the only one you've seen reported. I don't know about your code specifically, but the code from the original linked report will also fail if the phone is set to Arabic; other languages/calendars may also be affected. Any date parsing that doesn't explicitly specify the calendar will (absent any other calendar info) use the system calendar, and if that calendar info isn't available to your application then you'll see this error.

What is the reason that the linker is unable to include the parts necessary for Thai culture by default

If the Linker doesn't see any code which explicitly uses the Thai calendar, then it doesn't have any reason to think you need the Thai calendar. The Linker's job is to trim down the necessary code to as little as possible, so it automatically removes the information about the Thai calendar if it doesn't think you are using it.

That's what your workaround (the InitThaiCalendarCrashFix() method) is doing - explicitly referencing the Thai calendar so the Linker doesn't remove it. If you want to explicitly retain information about the Thai calendar in your iOS application without resorting to dummy methods, the way to do so is to check the "other" box in the "Codesets" section of your project properties; this tell the Linker to retain information about the cultures included in that library.

Also, you may want to review how you are handling date parsing. If the dates being parsed are controlled entirely by your application and are never displayed using the local culture, you may be able to provide a set culture (e.g., CultureInfo.InvariantCulture) when parsing/displaying dates and avoid worrying about local calendars entirely.

@hartez thanks for your response! I'll look into the exclusions tomorrow! And for ref. Regarding the Day/Month Names, please see https://github.com/xamarin/Xamarin.Forms/issues/4361

@hartez Thank you for the detailed explanation. And sorry, I misunderstood your prior comment (with/without).

Testing again (Android)--

No linking: Works

Adding encodings: Still crashes - I suspect this is incorrect advice. Documentation is not clear, but suggests that it is about ASCII encoding schemes only. Are you certain this option is related?

Custom "skip linking assemblies" as you suggested: Works

Arabic is just fine, no crashes in any situation. Really just seems to be Thai.

My next question is, shouldn't the relevant library have hints of some sort to ensure the linker doesn't remove things like this? And/or this step should be documented in the internationalization/localization/globalization guides?

@BioTurboNick Sorry for the confusion, the last comment was specific to addressing the problem on iOS. I've added an update to the comment to make it more explicit. For solving this problem on Android, you should be following the Android recommendations from https://github.com/xamarin/Xamarin.Forms/issues/3620#issuecomment-415925727.

Also, note that reproducing the problem on Android requires disabling the "Use Shared Runtime" option.

Adding encodings: Still crashes - I suspect this is incorrect advice. Documentation is not clear, but suggests that it is about ASCII encoding schemes only. Are you certain this option is related?

I think you are correct here - if you only need to address the calendar issue (and don't need any other Thai language facilities) then you _don't_ need to include the other encodings on Android. I've updated the other issue thread to reflect this.

Custom "skip linking assemblies" as you suggested: Works

Yes, and if you're only trying to address this calendar issue, it appears that you can pare the "Skip linking assemblies" setting on Android down to netstandard.

Arabic is just fine, no crashes in any situation. Really just seems to be Thai.

Again, I can't speak to your code specifically. If I just use the reproduction originally provided (var date = DateTime.ParseExact("2000-31-07", "yyyy-dd-MM", null);), after setting the device language to Arabic this problem occurs. Perhaps your code does not have this problem.

My next question is, shouldn't the relevant library have hints of some sort to ensure the linker doesn't remove things like this?

The linker functions by removing all code which is provably unused, with exceptions specified by the developer. It would defeat the goal of the linker (the smallest possible amount of code) to speculatively include code which _might_ be requested at runtime even though the developer didn't specify it.

@hartez - Thank you, I understand. There are similar linker issues that have cropped up with e.g. EntityFramework, and one of the suggestions a developer made was to ensure the problem code was always included, since it was likely to not properly be detected as necessary by the linker with normal usage. But this part is all rather beyond my expertise.

Thank you again. And I guess this can be closed if those are the intended solutions. As long as this advice gets added to the documentation on globalization/localization :-)

@BioTurboNick I'll see what I can do about getting it into the documentation. And hopefully the comments for this issue will at least make things easier for the next person who searches for this error message.

Incase if anyody needs it.. I too got the issue while developing iPhone app, I had set the device language to arabic. Followed the same method as @BioTurboNick..! Thanks

private static void InitArabicCalendarCrashFix()
{
var localeIdentifier = NSLocale.CurrentLocale.LocaleIdentifier;
if (localeIdentifier == "ar_SA")
{
new System.Globalization.UmAlQuraCalendar();
}
}

I have some Arabic users I believe based on positive app store reviews. But also just got bit by the Thai issue via CultureInfo.CurrentCulture. I am being more defensive and calling the following one time:

        // These classes won't be linked away because of the code,
        // but we also won't have to construct unnecessarily either,
        // hence the if statement with (hopefully) impossible
        // runtime condition.
        //
        // This is to resolve crash at CultureInfo.CurrentCulture
        // when language is set to Thai. See
        // https://github.com/xamarin/Xamarin.Forms/issues/4037
        if (Environment.CurrentDirectory == "_never_POSSIBLE_")
        {
            new System.Globalization.ChineseLunisolarCalendar();
            new System.Globalization.HebrewCalendar();
            new System.Globalization.HijriCalendar();
            new System.Globalization.JapaneseCalendar();
            new System.Globalization.JapaneseLunisolarCalendar();
            new System.Globalization.KoreanCalendar();
            new System.Globalization.KoreanLunisolarCalendar();
            new System.Globalization.PersianCalendar();
            new System.Globalization.TaiwanCalendar();
            new System.Globalization.TaiwanLunisolarCalendar();
            new System.Globalization.ThaiBuddhistCalendar();
            new System.Globalization.UmAlQuraCalendar();
        }

I do not understand how such hacks in our code are considered a good long-term solution? I have spent many hours trying to understand why my app is crashing on the store with certain cultures, until I found this workaround.

Was this page helpful?
0 / 5 - 0 ratings