Best practices when working with currencies - c#

I'm working on a desktop application that may potentially be deployed world-wide. My biggest issue with internationalization is currencies. A couple questions:
I'm using SQL Server Compact Edition, which supports the MONEY type. My first question is will this handle data storage regardless of the user's culture?
My second question is data processing. I know I can use the "C" format string, and I guess that's all right for displaying, but when saving back how do I handle user-entered values? Is there a standard way to parse the entered value and save it to the MONEY field, all while being culture-aware and not having to write for any kind of exceptions?
I'm working with C# in WPF, but I'm guessing there is probably a near-universal solution to all this.

The MONEY type just tells the system to use the required accuracy and rounding rules required to handle monetary calculations properly. It doesn't know anything about the actual currency. You need to handle that in the UI and with currency conversion rules.
The framework uses the current culture to display the correct currency symbol for monetary string formats, but if you are displaying different currencies you'll need to override this behaviour.
With C#/WPF (and Silverlight) if you output the currency using the StringFormat in XAML then it handles the user input as well (or at least it has in my experience).

Related

Using multiple Cultures in an application

I'm having trouble in my app managing one Culture used for data and one used for user-interaction. For data I want the Culture to always be English, as that's how I'm specifying my dates, prices, etc and how I need them to be parsed. However when displaying strings to the user those should be shown formatted correctly for the user's Culture.
Currently I'm not specifying a Culture so typically everything is processed using the user's Culture by default. I then fix the problems as they arise on an ad-hoc basis, but this is not ideal. I need a more systemic approach. Is there a way to specify the culture that should always be used on a class by class basis? Or some other way to manage cultures better than what I'm currently doing?

Parse string to DateTime in C# mimicking behavior of SQL Server CAST/CONVERT

I'm working with a database containing date time values stored as strings in various different formats. These values are obtained from different external sources and their formats are outside my control. It may be possible to enumerate all formats currently stored in the database, but some as-yet-unseen format may show up at some point in the future.
In the past, these values were parsed to SQL DATETIME with SQL Server's CONVERT function and then returned to a .NET application. Now the application is receiving the string instead and parsing has to take place there.
I initially used DateTime.Parse(), but that failed because it didn't handle some formats which SQL Server handles by default (such as "yyyyMMdd"). The next approach was to use the DateTime.TryParseExact overload that takes a string array for various formats. Something like:
var dateTimeString = "20210101";
var dateTimeFormatInfo = System.Threading.Thread.CurrentThread.CurrentCulture.DateTimeFormat;
var expectedDateTimeFormats = dateTimeFormatInfo.GetAllDateTimePatterns().ToList();
expectedDateTimeFormats.Add("yyyyMMdd");
DateTime.TryParseExact(dateTimeString, expectedDateTimeFormats.ToArray(), CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var myDateTime);
This works except that it also doesn't handle certain formats, like "yyyy-mm-dd hh:mi:ss.mmm", which SQL Server handles by default. Ironically that particular example is also handled fine by DateTime.Parse().
Is there a good way to mimic the behavior of SQL Server's CONVERT(DATETIME, val) in C# to try and successfully parse at least as many formats as were being handled in the original solution where the conversion was done in the database?
I think a solution with ParseExact that includes all the formats listed here may work but I'm wondering if there's a better way. Or maybe a better approach altogether.
Update: #jeroen-mostert's comment about using SqlDateTime.Parse is probably a better answer than what what we wound up going with.
Original answer:
In case it helps anyone in the future, the answer wound up being to use DateTime.TryParse() and, if that failed, calling DateTime.TryParseExact() with the special formats not already supported by TryParse().
From glancing over the source code it seems that Parse/TryParse is more lenient about things like separators, such that it works even with similar formats that are not shown when enumerating dateTimeFormatInfo.GetAllDateTimePatterns(). I believe that explains why TryParseExact didn't seem to work with formats that TryParse supported, even when TryParseExact was using all the DateTime patterns provided by the DateTimeFormatInfo object.
I did also validate format support against the listed Date and Time Styles in Microsoft's docs. This solution doesn't support 100% of them, though. That is by design since some combinations can be non-deterministic, and we are thankfully confined to a specific culture.
Not a perfect solution, as pointed out in the comments, but it did the trick here.
If you have control of the schema of your database your could either create a View or a Computed Column on the Table with the FORMAT() function to format the dates in a standardized and single consistent format that C#'s DateTime can easily handle.
Please note that the FORMAT() function can be less-than performant compared to other native SQL Server functions, in certain contexts.

c# how can I read String of double correctly despite the number Format?

Hi guys my problem is this. I made a software in c# that is able to read and edit dxf files, I have to give this software to an american company but I have discovered the following problem:
Where I live we use the ',' to separate the integer part from the decimal part of the number (for example: 2,3658) but in the USA they use the '.' so they write 2.3658.
When I try to read the string "2.3658" and convert it into a double with "Double.Parse("2.3658")" the double I get is 23658 like the method "Parse()" didn't recognised the decimal part.
I have found the following solution:
UpdatedCoorx = double.Parse(shiftedE[w + 1] ,NumberStyles.Number,CultureInfo.CreateSpecificCulture ("en-US"));
Using CultureInfo.CreateSpecificCulture ("en-US") the c# can read the numbers correctly.
My question is : is there a way that make c# automatically recognised the "Culture" of the pc where is installed so that it can read the number correctly???
is there a way that make c# automatically recognised the "Culture" of the pc where is installed so that it can read the number correctly?
That's what it's doing by default - and why you're having a problem, because the culture used to created of the value you're parsing ("2.3658") isn't the culture on your local machine.
For any particular value, you should really know which culture produced it. For machine-to-machine communication, it's best to use the invariant culture (CultureInfo.Invariant) which is mostly similar to the US. Ideally, you shouldn't store values in a culture-specific format at all; either store them in a binary representation instead of a string, or if you must store a string, treat that as effectively machine-to-machine communication.
If you're in the unfortunate position of receiving data where you know it's been formatted according to some human culture, but you don't know which one, you should probably use some heuristics to detect what the culture is. That can easily fail though - for example, is "1,234" meant to be a 1 followed by a "grouping" separator, followed by 234 - meaning one thousand, two hundred and thirty-four... or is it meant to be a 1 followed by a decimal separator, making the value just a bit more than 1? Both interpretations are valid, depending on the culture you use...
If you want to detect the Culture, you should be able to do so with this
As explained in the link CultureInfo.CurrentCulture returns the Culture from the Windows GetUserDefaultLocaleName function.
Jon's answer is pretty spot on (as usual). I just wanted to add that if you are developing an application that will be used by people in another country, it may be helpful for you to use CultureInfo.DefaultThreadCurrentUICulture while you are in development mode: just assign your users' culture to this property when the application starts, and you will have exactly the same usage experience (culture-related-things-wise) that your users will have. (Just remember to remove this when you ship your application! You could for example use a #if debug block)

How to convert existing dates to local browser date in c# and javascript and show them back in the browser [duplicate]

I am storing all the DateTime fields as UTC time. When a user requests a web page, I would like to take his preferred local timezone (and not the local timezone of the server machine) and automatically display all the DateTime fields in all the web forms as local dates.
Of course, I could apply the conversion on every DateTime.ToString() call in every form or implement some helper utility but it is a time consuming task, and also there are some 3rd party components which are tricky to configure with custom DateTime display templates.
Essentially, I would like to make the DateTime class to behave as follows:
from this moment on for this web request,
whenever some code calls DateTime.ToString(), convert it to the local time
using the timezone offset given at the very beginning of the web request,
but if possible, please keep .NET core library DateTime.ToString() calls intact
(I don't want to mess up event logging timestamps etc.)
Is there any way to do it?
BTW, I am using ASP.NET MVC 4, if it matters.
You can't do directly what you asked for, but I will suggest some alternatives. As Nicholas pointed out, there is nothing in HTTP that would give you the time zone directly.
Option 1
First, decide which type of time zone data you want to work with. There are two different types available, either the Microsoft time zones that you can access with the TimeZoneInfo class, or the IANA/Olson time zones that the rest of the world uses. Read here for more info. My recommendation would be the latter, using the implementation provided by NodaTime.
Then determine which time zone you want to convert to. You should allow your user a setting somewhere to pick their time zone.
You might show a drop-down list to pick one of several time zones, or you might do something more useful, like display a map of the world that they can click to select their time zone. There are several libraries that can do this in Javascript, but my favorite is this one.
You might want to guess a default time zone to use, so you can be as close to accurate as possible before they pick from the list (or map). There is a great library for this called jsTimeZoneDetect. It will interrogate the browser's clock and make a best guess assumption of what time zone it might be. It is fairly good, but it is still just a guess. Don't use it blindly - but do use it to determine a starting point. Update You can now also do this with moment.tz.guess(), in the moment-timezone component of moment.js.
Now that you know the time zone of the user, you can use that value to convert your UTC DateTime values to that local time zone. Unfortunately, there is nothing you can set on the thread that will do that. When you change the system time zone, it is global for all processes and threads. So you have no choice but to pass the time zone to each and every place you are sending it back. (I believe this was your main question.) See this almost duplicate here.
Before you convert it to a string, you will need to also know the user's locale (which you can get from the Request.UserLanguages value). You can assign it to the current thread, or you can pass it as a parameter to the DateTime.ToString() method. This doesn't do any time zone conversion - it just makes sure that the numbers are in the correct position, using the correct separators, and the appropriate language for names of weekdays or months.
Option 2
Don't convert it to local time on the server at all.
Since you said you are working with UTC values, make sure their .Kind property is Utc. You should probably do this when you load from your database, but if you have to you can do it manually:
myDateTime = DateTime.SpecifyKind(myDateTime, DateTimeKind.Utc);
Send it back to the browser as pure UTC, in an invariant format like ISO8601. In other words:
myDateTime.ToString("o"); // example: "2013-05-02T21:01:26.0828604Z"
Use some JavaScript on the browser to parse it as UTC. It will automatically pick up the local time settings of the browser. One way is to use the built-in Date object in JavaScript, like this:
var dt = new Date('2013-05-02T21:01:26.0828604Z');
However, this will only work in newer browsers that support the ISO-8601 format. Instead, I recommend using the moment.js library. It is consistent across browsers, and it has better support for ISO dates, and localization. Plus you get a lot of other useful parsing and formatting functions.
// pass the value from your server
var m = moment('2013-05-02T21:01:26.0828604Z');
// use one of the formats supported by moment.js
// this is locale-specific "long date time" format.
var s = m.format('LLLL');
The advantage of Option 1 is that you can work with times in any time zone. If you can ask the user for their timezone from a dropdown list, then you need not use any Javascript.
The advantage of Option 2 is that you get the browser to do some of the work for you. This is the best way to go if you're sending out raw data, such as making AJAX calls to a WebAPI. However, JavaScript is only aware of UTC and the browser's local time zone. So it doesn't work so well if you need to convert to other zones.
You should also be aware that if you choose Option #2, you may be affected by a flaw in the design of ECMAScript 5.1. This comes into play if you are working with dates that are covered by a different set of daylight saving time rules than are currently in effect. You can read more in this question, and on my blog.
It would be so much easier if we had some time zone information in the HTTP headers, but unfortunately we don't. These are a lot of hoops to jump through, but it's the best way to have both flexibility and accuracy.
The short answer is that you can't. HTTP doesn't require (or even provide a standard way) for the user agent (browser) to provide local time or timezone information in the HTTP request.
You either need to
ask the user for their preferred time zone, or
have client-side javascript report it to you somehow (cookie? ajax? other?)
Bear in mind that a client-side javascript solution isn't perfect, either. Javascript disabled (or non-existent, for some browsers). Javascript might not have access to timezone information. Etc.

Why doesn't a phone number datatype/library exist

Just wondering if anyone is aware of any low level phone number storage constructs for C#. I have been surprised to find that all of my searches have proved fruitless for such a library.
Essentially I am hoping for something that can take a string phone number input (in all its varied goodness) and both validate and segment the given string into its various sections (IE: country code, area code, number) along with providing a common format to store this data.
Does any such library exist? If not, any idea why something like this hasn't been attempted? (Is it really that hard a problem?)
I think that the main reason is that between people themselves there seems to be no real standard way of writing the phone number. For instance, people living on small islands tend to not have regional codes and there is no need for the country code when calling residents of the same island.
This changes when you move to larger places. Also, I have seen certain numbers written as (XXXX)XXXXX or XXXXX-XXXXX or XXXXXXXXXX.
The standard way of dealing with this seems to be with regular expressions. The developer usually takes a few possible input formats and uses regular expressions to validate and transform the format of the number.
So it turns out there is actually a pretty awesome library available after all.
Original Java version: http://code.google.com/p/libphonenumber/
C# version: https://bitbucket.org/pmezard/libphonenumber-csharp/wiki/Home
Me discussing the library on my blog.

Categories

Resources