Runtime: Wrong CurrencyNegativePattern used for fr-FR on centos 7

Created on 11 Feb 2020  Â·  10Comments  Â·  Source: dotnet/runtime

Hello,

Let's consider the following code that I ran on .net core 2.2.8 and .net core 3.0.2.:

CultureInfo.CurrentCulture = new CultureInfo("fr-FR");
string currencySymbol = "€";
NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
nfi = (NumberFormatInfo)nfi.Clone();
nfi.CurrencySymbol = currencySymbol;
var amoutStr = string.Format(nfi, "{0:C}", -16.84);

amountStr varies depending on the underlying operating system
On Windows 10 Version 1909: -16,84 €
On Ubuntu 18.04 LTS: -16,84 €
On Centos 7: (16,84 €)

When I run the code on centos 7, I can see that:
CultureName: fr-FR
CurrencyNegativePattern : 15

When I run the code on Windows 10 or Ubuntu 18.04LTS, I can see that:
CultureName: fr-FR
CurrencyNegativePattern : 8

Result on Windows 10 and Ubuntu 18.04LTS are correct.
I expect this code to behave the same when .net core is installed on centos 7.

area-System.Globalization question

Most helpful comment

ah, I was not viewing the whole file :-( will take a look. thanks.

All 10 comments

@Yanal-Yves .NET depending on the ICU library for getting the globalization data. if you are seeing different results on Centos than Ubuntu, that means Centos is using a different version of the ICU library than Ubuntu. try to check what are the versions of the ICU used on Centos and Ubuntu. I believe you'll just need to upgrade the ICU version on Centos to get the expected results.

I am closing the issue but feel free to respond back and I'll be happy to help with any more questions or asks.

@tarekgh, Thank you for your response. I can't manage to upgrade ICU properly.

I was able to install ICU thanks to https://blog.remirepo.net and I have now the two versions installed on my CentOS 7 system. Unfortunately dotnet is still using the old one.

Do you have any pointer about how to upgrade ICU on Centos 7 and make dotnet use the new version ?

@Yanal-Yves .NET Core looks for the latest version of ICU available on the standard library search paths. If it has picked the old one, then it seems it couldn't locate the new one or there was something wrong with it and we couldn't load it.
What is the new ICU version and in which directory is it installed?
Could you please run dotnet with LD_DEBUG environment variable set to all and share the output (ideally via a gist as it is going to be long)? That would show which versions we were probing for and where the dynamic linker was looking.
You'd run it like this:

LD_DEBUG=all dotnet

or, to save the log into a file dotnet_ldddebug.PID:

LD_DEBUG=all LD_DEBUG_OUTPUT=dotnet_lddebug dotnet

This way generates dotnet_lddebug.PID file where PID is the id of the dotnet process.

Thank you for your anwser @janvorli
As you can see below I have both libicu 50 and libicu 60 installed on my system.
If I run locate libicu I get the following output:

[user@host  ~]$ locate libicu
/usr/lib64/libicudata.so.50
/usr/lib64/libicudata.so.50.2
/usr/lib64/libicudata.so.62
/usr/lib64/libicudata.so.62.1
/usr/lib64/libicui18n.so.50
/usr/lib64/libicui18n.so.50.2
/usr/lib64/libicui18n.so.62
/usr/lib64/libicui18n.so.62.1
/usr/lib64/libicuio.so.50
/usr/lib64/libicuio.so.50.2
/usr/lib64/libicuio.so.62
/usr/lib64/libicuio.so.62.1
/usr/lib64/libicule.so.50
/usr/lib64/libicule.so.50.2
/usr/lib64/libiculx.so.50
/usr/lib64/libiculx.so.50.2
/usr/lib64/libicutest.so.50
/usr/lib64/libicutest.so.50.2
/usr/lib64/libicutest.so.62
/usr/lib64/libicutest.so.62.1
/usr/lib64/libicutu.so.50
/usr/lib64/libicutu.so.50.2
/usr/lib64/libicutu.so.62
/usr/lib64/libicutu.so.62.1
/usr/lib64/libicuuc.so.50
/usr/lib64/libicuuc.so.50.2
/usr/lib64/libicuuc.so.62
/usr/lib64/libicuuc.so.62.1
/usr/share/doc/libicu-50.2
/usr/share/doc/libicu62-62.1
/usr/share/doc/libicu-50.2/license.html
/usr/share/doc/libicu-50.2/readme.html
/usr/share/doc/libicu62-62.1/readme.html
/usr/share/licenses/libicu62-62.1
/usr/share/licenses/libicu62-62.1/LICENSE

I have created a small console app with the following code:

using System;
using System.Globalization;

namespace TestIcu
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                CultureInfo.CurrentCulture = new CultureInfo("fr-FR");
                string currencySymbol = "€";
                NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
                nfi = (NumberFormatInfo)nfi.Clone();
                nfi.CurrencySymbol = currencySymbol;
                var amoutStr = string.Format(nfi, "{0:C}", -16.84);

                System.IO.File.WriteAllText(@"WriteText.txt", amoutStr);
                Console.WriteLine(amoutStr);
                Console.WriteLine("€");
                System.Threading.Thread.Sleep(60000);
            }
        }
    }
}

I then run the following command and put the result in a gist as you asked me for:

LD_DEBUG=all LD_DEBUG_OUTPUT=dotnet_lddebug dotnet
output result: https://gist.github.com/Yanal-Yves/a2b1f3249fa00f3a90dbb78555d46e77
As we can expect, I don't see any reference to libicu.

LD_DEBUG=all LD_DEBUG_OUTPUT=dotnet_lddebug dotnet TestIcu.dll
output result: https://gist.github.com/Yanal-Yves/fa626676f2d183cc79d4bc27a6a55d8a
As we can expect, the TestIcu.dll is using System.Globalization and I can see the system loading libicu*.so.50 shared libraries.

Do you have any idea what can I do to force dotnet to use lib ICU 62 ?

@Yanal-Yves could you please check if your system defines the environment variable DOTNET_SYSTEM_GLOBALIZATION_INVARIANT? or your app runtimeconfig.json has the "System.Globalization.Invariant": true

I am asking this because in your results I am not seeing icu libs are loaded. so maybe you are running with the Invariant Globalisation mode on?

@tarekgh, I don't have DOTNET_SYSTEM_GLOBALIZATION_INVARIANT set and no file named runtimeconfig.json were edited.

I you open the second gist I have poste, you'll see icu lib references at line 11464. Did you miss something or am I wrong somewhere ?

ah, I was not viewing the whole file :-( will take a look. thanks.

I believe the issue is in this line:

https://github.com/dotnet/runtime/blob/master/src/libraries/Native/Unix/System.Globalization.Native/pal_icushim.c#L201

which force loading the min version first which is happened to be the one in your system.

@Yanal-Yves could you please try setting the environment variable:

CLR_ICU_VERSION_OVERRIDE=62.1

and look if this will help?

@janvorli please correct me if I was wrong. Also, I think we need to change this logic and avoid loading the min version first.

I have tried and this is working as expected !

[user@host  ~/TestIcu]$ dotnet TestIcu.dll
(16,84 €)
€
^C
[user@host  ~/TestIcu]$ CLR_ICU_VERSION_OVERRIDE=62.1 dotnet TestIcu.dll
-16,84 €
€
^C

I have also ran with LD_DEBUG=all LD_DEBUG_OUTPUT=dotnet_lddebug

[user@host  ~/TestIcu]$ LD_DEBUG=all LD_DEBUG_OUTPUT=dotnet_lddebug CLR_ICU_VERSION_OVERRIDE=62.1 dotnet TestIcu.dll
-16,84 €
€
^C

Please find here the output result: https://gist.github.com/Yanal-Yves/cae70e049d9971e3b0553bd38881a962
As you can see the version 62 is loaded as expected !

I would like to thank you for your support. How can I pay you a beer ?

I am happy you were able to work around the issue. let me know if you get any more questions or issues.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bencz picture bencz  Â·  3Comments

jzabroski picture jzabroski  Â·  3Comments

GitAntoinee picture GitAntoinee  Â·  3Comments

jchannon picture jchannon  Â·  3Comments

v0l picture v0l  Â·  3Comments