I need to store multiple currencies in SQL server. I understand that SQL won't support all different types of currencies (unless I store it as a string, but I don't want to do that).
My idea was to convert all the values from their currency format to a standard double and store that instead. Then just re-format based on the culture info when displaying. However, I have tried doing something like e.g.
var cultureInfo = new System.Globalization.CultureInfo("en-US");
double plain = return Double.Parse("$20,000.00", cultureInfo);
This doesn't ever seem to work it always throws a FormatException. Even removing the currency symbol and just trying to do this based on the number alone does the same thing. This is just an example I want to support pretty much any type of currency.
Is there a standard way of stripping out currency and getting the value as a double?
I think this should work:
double.Parse(currencyValue, NumberStyles.AllowCurrencySymbol | NumberStyles.Currency);
Here you can see more about the NumberStyles.
Edit: In case anyone sees this answer without looking at the other answers/comments, this answer answered the question as written, but storing currency as a double is not a good idea, and it would be better to use decimal instead.
You should pass NumberStyles to the Parse function
Decimal.Parse("$20,000.00", NumberStyles.AllowCurrencySymbol | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, new CultureInfo("en-US"));
A few other things, for currencies I would suggest you use Decimal. And this might be way off, but it might be better to store the currency data as Money in the DB and add a currency code to identify the currency of the value.
Yes, and the answers suggestung NumberStyles.Currency that would be better. It is a pre-Or'd value, if you still think you want to use the strings.
You can also use the tryparse()
string input = "$2,000.00";
double parsed = 0d;
double.TryParse(input, NumberStyles.AllowCurrencySymbol | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, CultureInfo.CurrentCulture, out parsed))
Related
I have a value that is formatted in currency format and displayed in a textbox. I need to pick it and convert it into decimal. e.g. I have $1,633.75 as string and want to cast it as a decimal to save it in the db.
There are questions and some answers available on them already, but they all suggest more complex UDF etc to solve the problem.
Question is, why cannot we use the replace method on a string to remove commas (",") and "$" and simply use decimal.Parse?
Say tbAmountPaid is $1,633.75
What is downside of just doing:
decimal.Parse(tbAmountPaid.Text.Replace(",","").Replace("$",""))
to get decimal type 1633.75 value?
Downside is that hardcoding. You never know the culture and the format in which string is written. There could be multiple bugs in the string itself. If you do hard coding you may end up having a bug in your code
I recommend you to use the format provider. I took code from here
https://learn.microsoft.com/en-us/dotnet/api/system.decimal.parse?redirectedfrom=MSDN&view=netframework-4.7.2#System_Decimal_Parse_System_String_System_Globalization_NumberStyles_System_IFormatProvider_
string value;
decimal number;
NumberStyles style;
CultureInfo provider;
value = "$1,633.75";
style = NumberStyles.Number | NumberStyles.AllowCurrencySymbol;
provider = new CultureInfo("en-US");
number = Decimal.Parse(value, style, provider);
Console.WriteLine("'{0}' converted to {1}.", value, number);
OK, so you're not asking how to do it, you're asking "why not do it this way?"...
Your alternative takes more effort to type than the correct solution,
More effort for the computer to process,
More memory usage (since strings are immutable),
Compiles to larger IL,
Your approach is culture specific. Some cultures use dot for the thousand separator and comma for the decimal place, let alone a different currency symbol.
Turning the question around, what do you feel is better about your approach?
I'm trying to unit test a getprice method using NUnit. I am stuck with parsing the rawprice into double. My cultureinfo is en-US but I set it to de-DE for this test. Double parsing with numberstyles.any and invariantculture returns unexpected result.
The rawprice cultureinfo is unknown, it can be any. Also the server where it will run is also unknown and can be in any language.
For this test, I tried German for the rawprice and machine.
I tried parsing "9,42" but the result is 942.
[Test]
[SetCulture("de-DE")]
public void GetPrice_PriceTextWithCommaDecimal_ReturnsInvariantPrice()
{
var rawPriceText = "9,42";
double.TryParse(rawPriceText, NumberStyles.Any, CultureInfo.InvariantCulture, out double price);
//parsed price result is 942
...
}
It's not clear from your question what you expected. However, as far as what the code is doing, it's doing exactly what you told it to:
Providing NumberStyles.Any tells double.TryParse() to allow any format, except AllowHexSpecifier. This includes the AllowThousands option.
Providing the InvariantCulture causes parsing to use the ',' character as the thousands separator.
Parsing doesn't actually care where a thousands separator appears. I.e. it doesn't actually force the separator to be in the location where it would indicate a thousands-multiple digit.
So, when you ask it to parse "9,42", that text is interpreted using InvariantCulture (i.e. ignoring your current culture of de-DE), the ',' character is treated as a thousands separator (i.e. ignored for the purpose of computing the actual value), and you get the value 942, just like you asked for.
If you don't want that result, you need to use different arguments for the call to double.TryParse(). You would need to explain what you do want if you want advice on what arguments you should use. All we can say given the information currently in your question is what arguments you apparently don't want.
The cultures in ToString and TryParse must match.
It's either
var s = rawPrice.ToString(CultureInfo.InvariantCulture);
//rawPrice becomes 9.42
double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out double price);
or
CultureInfo.CurrentCulture = new CultureInfo("de-DE");
var s= rawPrice.ToString(CultureInfo.CurrentCulture);
//rawPrice becomes 9,42
double.TryParse(s, NumberStyles.Any, CultureInfo.CurrentCulture, out double price);
You should put your culture into the TryParse mechanism.
e.g.
double.TryParse(rawPriceText, NumberStyles.Any, new CultureInfo("de"), out double price);
For this case you could use CultureInfo.CurrentUICulture instead of creating a new CultureInfo.
You set the culture to de-DE.
But apparently you then decide to use the InvariantCulture instead, which will not recognize the , separator as decimal separator.
Using CurrentCulture instead will give you the expected result.
VS:2005, framework: 2.0
I have a dropdown with French and English languages. As User selects a language, We set CultureInfo as fr-FR or en-US respectively.
When retrieving float values from DB we assign value as
Convert.ToDecimal(table.Rows[0]["diametre"].ToString(), System.Globalization.CultureInfo.InvariantCulture)
In french, 1.3 is displayed as 13 or sometime gives "input string was not in a correct format." error. I tried follwing code:
System.Globalization.NumberFormatInfo numberFormatInfo = new System.Globalization.NumberFormatInfo();
if (System.Globalization.CultureInfo.CurrentCulture.Name == "fr-FR")
numberFormatInfo.NumberDecimalSeparator = ",";
else
numberFormatInfo.NumberDecimalSeparator = ".";
Convert.ToDecimal(table.Rows[0]["densite"].ToString(), numberFormatInfo)
But gives Input String error as above. When "en-US" culture, it works perfectly fine!
What else should I try?
This is giving quite a pain now. Please help me.
TIA
Update:
Thanks all, for responses!
I had help from a colleague while fixing bug. We did following change:
decimal diametre = Convert.ToDecimal(table.Rows[0]["diametre"].ToString().Replace(",", "."), new System.Globalization.CultureInfo("en-US"));
Thanks Tim for useful info like "ToString will use the current culture's decimal separator"
Why is diametre a string at all? If it's a decimal in the database you should use:
decimal diametre = table.Rows[0].Field<decimal>("diametre");
That will be more efficient and also prevents localization issues.
Otherwise you can use decimal.Parse with the appropriate culture-info:
var french = new CultureInfo("fr-FR");
decimal diametre = decimal.Parse(table.Rows[0].Field<string>("diametre"), french);
Update i've only just seen that you're using .NET2, then the Field-extension method is not available. You have to cast it to the correct type, i still wouldn't use ToString to convert everything to string since that could modify it(f.e. if it's a decimal, ToString will use the current culture's decimal separator).
decimal diametre = (decimal)table.Rows[0]["diametre"];
or
decimal diametre = decimal.Parse((string)table.Rows[0]["diametre"], french);
If it's always stored with the dot as decimal separator as in en-US for example, you can use:
decimal diametre = decimal.Parse((string)table.Rows[0]["diametre"], CultureInfo.InvariantCulture);
If you then want to diplay it with the correct format use decimal.ToString:
string output = diametre.ToString(french);
Basiclly, you have 2 cultures in .NET.
You have a culture and you have a UI culture.
The Culture affects how culture-dependent data (dates, currencies, numbers and other information) is being displayed to the user.
The UI culture affects the resource file to be used, so first of all, if you just want to change the language, but keeping the date formates and other information like configured on the computer, you should only change the UI culture and not the culture.
However, the answers given above are valid.
If the field is a number, store it as a number and not a string.
Use Parse with the correct culture.
#Tim I gave you credits for your answer, but I wanted to contribute with this aswell.
I know this question is old, but it might help others who have the same issue and none the above solutions has helped them. For me, adding this line of code to the MainPage constructor of my app (I'm using xamarin.Forms), solved the issue.
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-GB");
Hope it helps, it simply sets the culture of the application to english, regardless of what the devices culture/language has been set to, so if you need the app to behave locally, then this might not be a good solution
You can convert your dbValue first like this:
string MyValue = (String)(table.Rows[0]["diametre"] == DBNull.Value ? string.Empty : table.Rows[0]["diametre"].ToString);
then replace possible comma by dot:
if(!string.isNullOrEmpty(MyValue)){
MyValue.Replace(",",".");
}
and then Convert it to decimal:
Convert.ToDecimal(MyValue, System.Globalization.CultureInfo.InvariantCulture)
I'm having trouble with a very simple problem. Basically I'm taking a string, like "$30.00", and removing the '$' and then trying to convert it to a decimal. Although I keep getting an error stating that the string isn't in the correct format. Not sure what to do from here...
string freightstart = PostFregihtAmount.ToString();
freightstart = freightstart.TrimStart('$');
decimal freight = Decimal.Parse(freightstart);
I tried the following, per Alex Skiba's suggestion in the comments:
Decimal.Parse(freightstart, System.Globalization.CultureInfo.InvariantCulture);
This is the error I receive upon debugging:
This is a plugin for our onsite CRM 2011 system and some fields that I have to reference on the quote form are currency fields. When I query the data they come back as string formats, hence the example "$30.00". Long story short I need to convert it to decimal so that I may do my tax solution.
Try explicitly allowing the decimal point when you parse the string, as well as the currency symbol (then you don't have to trim the $ before parsing):
var freight = Decimal.Parse("$30.00",
NumberStyles.AllowCurrencySymbol | NumberStyles.AllowDecimalPoint);
I need to format the Decimal variables Latitude=9113267; Longitude=59300357;
to string format 9,113267 and 59,300357
Thx
john
VascoP is right in how to convert the number to a "proper" decimal but he is wrong about how to convert those decimal values to a string. the ToString method has an overload whose signature is
public string ToString(IFormatProvider provider)
See: http://msdn.microsoft.com/en-us/library/3ebe5aks.aspx
You can use this to create a culture specific string. The examples on the linked page show how to do it but for completeness of answer an example might be:
(Latitude/1000000).ToString(CultureInfo.CreateSpecificCulture("en-GB") // Outputs with a "." decimal separator
(Latitude/1000000).ToString(CultureInfo.CreateSpecificCulture("de-DE") // Outputs with a "," decimal separator
I assume you have a specific culture that you want to be able to understand this so you should use the correct culture. That makes it much easier to change later if you want (eg you can pick up the culture from a global config setting) or have a user preference for the number format, etc.
Also if you start using custom formats (eg to put thousand separators in) then the cultureinfo object will again do the right thing.
It should also be noted at the end of all this that you may just need the .ToString if the default culture is actually the one you are using. You didn't provide that info though so I just assumed that a simple ToString wouldn't be doing the trick.
(Latitude/1000000).ToString().Replace('.', ',');
(Longitude/1000000).ToString().Replace('.', ',');
EDIT: Although this works, as stated by Chris, it is not best practice. You should use his solution instead.