Internationalisation query in SQL SERVER and C# - c#

I have to make an application that will be used in USA which has decimal separator as . and in
scandinavia which has decimal separator as ,
Also scandinavian countries have extra characters like ø æ å etc
I am fairly new to such type of internationlisation.
The application and the database will be same for both USA and scandinavia.
I need your help with these questions:-
What type of collation should I use in the database ?
In the front end ( C#) Is it possible that in USA I can show the numeric values displayed as 100.00 and in scandinavia to show them as 100,00 and somehow make sure that the calculations and saving to the database works without any problem?
Thank you

In SQL Server, collation is used for operations such as sorting on text data, so I don't think that decision would affect currency etc.
From .NET front-end (and sql back-end), you have to choose correct data type (such as Decimal, DateTime) for manipulating the data - for display & input purpose, you can do locale specific formatting (e.g. see currency format specifier in numeric formatting string). You can specify the format in functions such as Decimal.ToString or String.Format. These will use your current locale but you can have overrides to specify locale specific format providers. Similarly you have Parse method to convert from string to actual data type.

You can choose a collation here Selecting a SQL Server Collation there are scandinavian collations.
The decimal delimeter depends on Windows settings and will not affect your calculations.
You will have some problems with converting character expressions to a numeric data type.
Data Type Conversion:
Character expressions that are being converted to an exact numeric
data type must consist of digits, a decimal point, and an optional
plus (+) or minus (-). Leading blanks are ignored. Comma separators,
such as the thousands separator in 123,456.00, are not allowed in the
string.

Make all your string data types are NVARCHAR (or NCHAR if fixed length) so that you support unicode characters.
Since you say that your 'database' will be the same, do you mean that the server is the same physical instance, or just the same schema?
If you will have a separate server for USA to Scandinavia, this means you just set your SQL server collation and your Windows Server localization settings to USA or Scandinavia, you will need to test your solution under both environments continuously through development to ensure than any error doesn't propagate for too long unnoticed.
It's also possible to have a single database instance and then just change the localization at the WS / application side, for example this means you pass all times and numbers as USA format. But you display the values as either USA or Scandinavia format, if you're a installed (and not a web-app) you could run this control off the user's local computer localization setting which should do most of the heavy lifting for you without a problem.
eg: DateTime.Now.ToString() will report "16:34 14/11/2011" for one localization setting but "16:34 11/14/2011" for USA localization, internally the data is the same.
Lastly, if you are using a single central database, make sure you save your dates as UTC time rather than local time, otherwise you will be ignoring timezone differences between the data.

Related

Date field value is changing after saving data to database [duplicate]

I have a legacy application where the input is a date string, i.e.:
06/12/2009
The format of the input is always a string, and is consistent, it's always dd/mm/yyyy
At the moment the legacy app just INSERTS this in a DateTime fields. Obviously if the Localization Culture settings of the Server change, we have a bug.
Two questions:
One:
Whats the safest way to store Dates in SQLServer in this situation?
Is there a format that will always be correctly interpreted regardless of the order of day and month?
Two:
What settings exactly determines the culture of a SQLServer DB, is it an OS setting, or a setting of that DB, or what?
cheers
Format YYYY-MM-DD is unambiguous, meaning that SQL Server won't confuse the month
and day when converting a string value to DATETIME. (I've never experienced a problem with an implicit conversion using that format using the four digit year.)
The "safest" (and most convenient) way to store date values in SQL Server is to use DATETIME datatype.
Use the CONVERT function to explicitly specify the input and output formats when converting between DATETIME and strings.
SQL Server 2005 Documentation on CONVERT style argument values:
http://msdn.microsoft.com/en-us/library/ms187928(SQL.90).aspx
To convert a string representation to DATETIME datatype:
select CONVERT(datetime, '2009-06-03', 20)
The first argument is datatype to convert to, the second argument is the expression to be converted, the third argument is the style.
(style 20 is ODBC Canonical format = 'YYYY-MM-DD HH:MI:SS' (24 hour clock)
[FOLLOWUP]
To convert a DATETIME expression (e.g. getdate() to VARCHAR in 'YYYY-MM-DD' format:
select CONVERT(varchar(10), getdate(), 20)
Note that specifying varchar(10) gets you just the first 10 characters of the etnire 'YYYY-MM-DD HH:MM:SS' format.
[/FOLLOWUP]
As to what determines the default formats, that's going to be research. We avoid the issues caused by default formats by specifying the formats.
I would recommend storing all dates in UTC time when they are placed into the database. It will be consistent that way.
Storing dates like this seems to work well...
YYYY-MM-DD
See SET DATEFORMAT. The SQL 'culture' is set by SET LANGUAGE at a session level. SQL Server has its own date format settings, independent of the hosting OS. This is for several reasons: ANSI compliance, to prevent OS changes from affecting applications using the database hosted on that host and not least is compatibility, the SQL long predates the OS is currently running on.
Keep in mind that DATA is not its PRESENTATION. In this case that DATA is a DATE or DATETIME, regardless of how you show them.
As for inserting/updating/comparing datetime values, I quote the BOL:
When specifying dates in comparisons
or for input to INSERT or UPDATE
statements, use constants that are
interpreted the same for all language
settings: ADO, OLE DB, and ODBC
applications should use the ODBC
timestamp, date, and time escape
clauses of:
{ ts 'yyyy-mm-dd
hh:mm:ss[.fff] '} such as: { ts
'1998-09-24 10:02:20' }
{ d 'yyyy-mm-dd'} such as: { d '1998-09-24' }
{ t 'hh:mm:ss'} such as: { t '10:02:20'}
I can assure you that, if you use this formats they will always work, regardless of the locale of you server
I'm a bit conservative in these matters, but I prefer to use separate Year / Month / Day fields in the table, rather than a Date field that uses a DBMS-specific data type. It certainly takes more space, but the lack of ambiguity and increased portability is worth it to me.
The price you pay is that you don't get free date/time arithmetic and sorting, but it's easy enough to do yourself or by a slightly more complex "ORDER BY" clause.
I agree with the advice from spencer7593, but please be aware that using cast or convert without a format can give unexpected results. This T-SQL query returns 12, not 1.
set language British
select month(CAST('2016-01-12' AS datetime))
Normally I prefer to insert as
insert into tbl values('yyyyMMdd')
Then, itll be inserted in proper format based on db.

Is this c# code enough to handle globalization?

My C# winforms program will be used in following countries
United Kingdom : date format day-month-year , currency separator is '.'
United States : date format month-day-year , currency separator is '.'
Denmark : date format day-month-year , currency separator is ','
I want to make the program run properly irrespective of what regional settings are on users computer. My main concern is handling date format and currency fields
(Language translation is not a problem because the program will only show English text)
To do this I have decided that all dates in the database will be saved with yyyy-mm-dd format and all decimal fields will be saved with . as separator.
I created the database with Danish_Norwegian_CI_AS collation .
So I am assuming the data will be saved in the above datetime format & decimal format without me requiring to do anything special.
I have put the following code in my program
var cult = new CultureInfo("en-GB");
Thread.CurrentThread.CurrentCulture = cult;
Thread.CurrentThread.CurrentUICulture = cult;
CultureInfo.DefaultThreadCurrentCulture = cult;
CultureInfo.DefaultThreadCurrentUICulture = cult;
Please help me by telling from your experience if the above code is enough to make my program safe
Culture applies only in the following two scenarios:
Converting a string representation of a value to a native format, which is called "parsing".
Converting the native format of a value to a string representation, which is called "formatting".
When you store a datetime or a decimal or some other type into the database, it is stored in a native format. In a SQL database, this is usually some compact binary value that you will never work with directly.
Consider the following SQL:
declare #dt datetime
set #dt = '01/02/2015 12:34:56'
select #dt
In the first line, we declare a variable of type datetime. It's not a string, it's a specific data type that takes up 8 bytes of memory or disk.
In the second line, we assign a string value to the variable. SQL parses the string, converting it to a datetime so it can be stored in the #dt variable. The actual value stored has a hexadecimal representation of 0x0000A41400CF5940.
When it did the parsing, the current culture for the environment where the code was running was applied. Because I am in the USA, it interpreted the date as January 2nd. If I was in Europe, it would interpret the date as February 1st (changing the internal value to 0x0000A43200CF5940).
Using dates in yyyy-mm-dd format will avoid misinterpretation, but that does not mean that the actual value is stored as a string in that format. It's just that the format is unambiguous, so it will be parsed the same way regardless of culture.
In the third line of code above, we select the variable to include it in a result set. Though we are selecting it in its native form without any conversion, we ultimately see it in a string representation. If you are running the query in a tool such as SQL Server Management Studio, the output window will format the native values to strings so you can read them. When doing so, the current culture is again applied. SQL's default is to show dates in yyyy-mm-dd format, rather than the culture-specific format. But other values, such as decimals, will use the current culture's separator.
If instead of running this in SSMS you actually retrieved the results through your own code in a SqlDataReader (for example), then formatting never occurs. The reader maps SQL's binary native value directly to the appropriate .NET native type, using the mappings shown here. A SQL datetime gets natively mapped to a .NET DateTime.
DateTime dt = (DateTime) reader["dt"];
Now quite often, you see someone doing silly things like this:
DateTime dt = Convert.ToDateTime(reader["dt"].ToString());
This is wasteful because the value is already a DateTime, and this code would use the current culture to format the string, then use it again to parse the string. That's a lot of string manipulation for no reason whatsoever.
Ultimately, in your .NET code, you will end up using that DateTime value and converting it to a string somewhere for output. When you do, that's when you apply the current culture.
Likewise, when you receive an input string from your user (such as when filling out a form), you parse the value to a DateTime using the current culture again.
Native data types are not strings - and are thus not affected by culture.
Notes:
If you want to see the hexadecimal representation of the native binary form of any SQL data type, you can use something like: select convert(varbinary, #dt)
Be aware of whatever the native format is for anywhere you are working. If you're writing to an http stream, a text file, or a document database, etc., the string representation does indeed matter, because a string is the native format in those scenarios.
And just to prove this applies to more than dates, consider:
select 123, 123.45, convert(varbinary, 123), convert(varbinary, 123.45)
--results: 123 123.45 0x0000007B 0x0502000139300000

C# AddDays throws ArgumentOutOfRangeException when data received is in 24hours format

here's my problem. I am importing an Excel file with a field having a time field. On some servers (french) when I do
double cellValueAsDouble = double.Parse(cellValue.Replace(".", ","));
MyDate.AddDays(cellValueAsDouble);
to add more than 12hours it works. But on an another servers (english) I have an error throwing an ArgumentOutOfRangeException. If I put less than or 12:00:00 it works fine. Does anyone knows why and how I can fix that?
I try to guess the answer:
The cellValue contains a comma as a decimal separator. so it's
value is e.g. 0,5833333333.
You replace the comma with a point => 0.5833333-
Converting this to a double on a french server reveals 58333333333.
Adding this to a date produces the exception.
Your software does not expect a point '.' as the decimal separator, but whatever is configured on the target system. As long as the excel source remains on the same computer, you don't have to exchange anything.
Also, is it really and XLS-format or are you talking about CSV? If your data comes from a french EXCEL exported as CSV, you will end up in having decimal values formatted using a comma. Now you check your software on your local PC (which has US culture I guess) and you are adding the code to replace the comma with a point. Wow, it now works on your PC! But don't expect that same code to work on another machine.
You either have to parse the decimal value manually or just rely that the data source (your EXCEL data) is formatted on a machine having the same culture settings as your the machine running your application.
One guess why values less than 12:00 work: Maybe by accident. If you check 06:00 it will be 0,25 as decimal and converted to 25 which is perfectly alright (well, it does not throw an exception).
Please don't forget: It is only a guess as we haven't seen all relevant information.
Its probably a problem with the culture and the decimal point.
Use this to convert the cell value with a ´.´ as decimal point:
Double.Parse(cellValue, CultureInfo.InvariantCulture);

How to use CultureInfo to format deprecated currencies?

In dotnet, the recommended way of formatting currencies in a culture-specific way, is (as far as I can find):
using System.Globalization
var info = CultureInfo.GetCultureInfo("en-GB")
return string.Format(info, "{0:c}", 1234.5678)
This returns:
£1,234.57
However. No specific currency is given here. So if Great Britain ever converts to Euro, the same method would suddenly return something like:
€ 1,234.57
This has just happened to Latvia. This year, it converted to the euro. Various systems contain both amounts in letvian lats and euros. I need to be able to still print and format old currencies. And I need to be sure new currencies can also be handled. This formatting is not just about the symbol, but also about the placement (before, after, with or without space) and the local number format (decimal separator and thousands separator).
Every amount in our database has a currency (e.g. EUR) and a corresponding culture (e.g. nl-NL). Euro amounts are formatting differently depending on if they originate from our German or our Dutch office. (they both use euros, but format the amounts differently)
Does dotnet provide access to older currencies?
What is a future-proof way of writing the formatting amounts?
Right now, the only way I can think of, is to copy the culture-database to my database.
You can create a custom CultureInfo (by cloning one and modifying) which uses a different currency symbol/format (ie. set its NumberFormat to a different instance of NumberFormatInfo).
Then pass this CultureInfo to formatting functions as needed.
As the comment to the question notes, .NET (and Windows in general) doesn't provide historic data (similarly for time zones but there's a library for that). In the cases you need you'll need to hold enough data yourself.
Remember ISO-4217 currency codes are not reused under such a change, so holding that against amounts allows you to format correctly. Additionally just because a country formats their currency amounts one way doesn't mean everyone does. Eg. 25 French Francs was be "FF25.00" in the UK but "25FF00" or "FF25,00" in other locales. (EDIT: I note you covered this last paragraph in the question.)
Of course, the simplest way is to not use the locale-specific currency format, but rather to format the amount as a simple number and pre- or suffix it with the ISO currency code. The convention
------------
Sum: ATS 1.376,00 (= EUR 100,00)
is commonly found on invoices (using locale de-AT as an example).
If you want to use the built-in currency formatting options, I would suggest to replace the currency symbol with the one stored in the database. I.e., in your currency table, you'd need to map currencies to symbols:
EUR -> €
ATS -> S
...
and then you'd replace myCultureInfo.NumberFormat.CurrencySymbol with the one in the database. That way, you ensure that a value is never shown with the wrong currency symbol.
If you are targeting Windows 8 or above, this deficiency is addressed by the Windows.Globalization.NumberFormatting.CurrencyFormatter. It requires that you provide the explicit currency and you can also explicitly provide a language.

System.Convert.ToSingle() Problems, (1.5) vs (1,5)

I'm writing program in C# for converting between model formats.
The model format has numbers as text such as "-0.136222".
I can use System.Convert.ToSingle() to convert this to a floating point number. But here in Germany we use commas as decimal points (-0,136222), and System.Convert picks up on this. Now I have the problem of it not recognizing decimal points since it expects commas.
In a nutshell;
We have this: "-0.136222"
We get this: -0136222.0f
because it expects this: "-0,136222"
Can I tell system to recognize commas as decimal points just for my program? A work around won't work since it needs to be portable (to other countries).
Use Single.Parse() instead, like this:
Single.Parse("-0.136222", CultureInfo.InvariantCulture);
InvariantCulture is the way to tell the method to parse the string ignoring locale-specific decimal and grouping separators.
Leave it as it is. The .Net framework installed on your clients' computers will automatically choose the correct way to parse the data for the country settings that computer is set for. So for example, in continental Europe it will parse your float using commas, but in the USA, UK and others where we use decimal points it will parse the data using decimal points.
Of course, you can override this Culture-specific localisation feature of .Net by using CultureInfo.InvariantCulture (like Pawel has suggested) or any other CultureInfo but this will mean you have to set it specifically for each country you sell your software to. Far better to just let the framework do the work for you :)
Note: It will also mean that a (say) German person working in the USA with his PC set to be localised for Germany will have his floats parsed with commas, as he expects, not with decimal points just because he is located in the USA.

Categories