How use a specific CultureInfo in Asp.net Web API - c#

I've recently added WEB API to an existing VS desktop application and everything worked fine, until yesterday when I had to add a GET method that took three parameters, one of them a Date.
Well, at first I thought that that was going to be a piece of cake, but much to my surprise I noticed that when I sent 2014/07/09 (9th July) on the server where the application was installed it was treated like 2014/09/07 (7th September) and for that reason all my comparisons never worked.
I have tried things like changing from a GET method to a POST method, changing my Regional and Language Options settings to the same on the server, passing the date as a String a created a Datetime object on the server using the parts of the string. Unfortunately none of them worked.
Then I remember that this desktop application have some methods on its WCF project (which I'm passing now to web API) that passed dates with no problem at all. Looking in the code for a while I found that they used something like this on every class of they WCF project that uses dates:
Imports System.Globalization
Imports System.Security.Permissions
Imports System.Threading
Public Class ServicioRemotoVentas
Implements IServicioRemotoVentas
Public Sub New()
MyBase.New()
Thread.CurrentThread.CurrentCulture = New CultureInfo("es-PE", False)
End Sub
Surely this Thread.CurrentThread.CurrentCulture = New CultureInfo("es-PE", False), must be there for something.
Now I would like to know if you have used something like that in Web API before? if so how and where did you put such a configuration.
These are the settings on my pc :
And these are the server settings:
I almost forgot to mention that I pass all the dates using this format yyyy/M/d with all the other parameters using json.
Is it perhaps that when the string is deserialized in the Web API this is done using the system date format because I haven't specify the culture info to use?? or maybe it is a Json error when trying serialize/deserialize the dates??
As always, any advice or resources you could provide would be greatly appreciated.

As discussed in the comments, the ASP.NET runtime does have a solution for these scenarios: it is the web.cofig element <globalization> - (see MSDN <globalization> Element)
It's structure is defined as:
<configuration>
<system.web>
<globalization
enableClientBasedCulture="true|false"
requestEncoding="any valid encoding string"
responseEncoding="any valid encoding string"
fileEncoding="any valid encoding string"
responseHeaderEncoding = "any valid encoding string"
resourceProviderFactoryType = string
enableBestFitResponseEncoding = "true|false"
culture="any valid culture string"
uiCulture="any valid culture string"/>
So, in case, that we want to force server/dev workstation to act in en-US culture we should use these explicit settings:
<globalization
enableClientBasedCulture="false"
uiCulture="en-US"
culture="en-US" />
This will use the proper (desired and set) culture for any http request.
Also interesting could be the default setting overview:
<globalization
requestEncoding="utf-8"
responseEncoding="utf-8"
fileEncoding=""
culture=""
uiCulture=""
enableClientBasedCulture="false"
responseHeaderEncoding="utf-8"
resourceProviderFactoryType=""
enableBestFitResponseEncoding="false" />
See also similar here:
Set uiCulture automatically based on browser accept language
How to change my computer's cultureInfo
and the globalization Element itself

It is mentioned in your question that the input is a string not a datetime object ("I almost forgot to mention that I pass all the dates using this format yyyy/M/d").
Your service interface isn't posted but I guess that the parameter type is DateTime and this is why the deserialization is incorrect.
Although the setting posted by Radim works it's not a fix but a hack/workaround.
There are two recommended ways to implement this:
Use strings at both ends, i.e. the API parameter should also be string and the API specification should state what date format to use. Use DateTime.Parse(String, IFormatProvider) and specify the culture in contract (i.e. new CultureInfo("es-PE")) when the input string is converted to DateTime.
https://msdn.microsoft.com/en-gb/kc8s65zs
Use date objects at both ends. In this case the serializer will serialize the date object to a well known, culture independent format and the desrializer will deserialize the string to correct DateTime object.
public void GetOrders(DatetTime fromDate)
I'd go with #2 because is generic and doesn't force the client to use a specific culture.
See also:
The "right" JSON date format

Related

Maintaining source string format when reading a date-time from a JSON path and writing it to another file

How can I have Newtonsoft.Json read the value of a path without converting or otherwise meddling with values?
This code
jsonObject.SelectToken("path.to.nested.value").ToString()
Returns this string
03/07/2019 00:02:12
From this string in the JSON document
2019-07-03T00:02:12.1542739Z
It's lost its original formatting, ISO 8601 in this case.
I would like all values to come through as strings, verbatim. I'm writing code to reshape JSON into other formats and I don't want to effect the values as they pass through my .NET code.
What do I need to change? I am not wedded to Newtonsoft.Json btw.
I got it, I think.
jsonObject.SelectToken(path).ToString(Newtonsoft.Json.Formatting.None);
The other options were to supply nothing or this.
Newtonsoft.Json.Formatting.Indented
Which is strange logic in this API as you'd think None means not indented but it means not ... I don't know. Hang on....
Okay so None or Indented returns
"2019-07-03T00:02:12.1542739Z"
(including quotes) but using the overload taking no parameters returns
03/07/2019 00:02:12
That's an odd API design ¯\_(ツ)_/¯
Here's a screenshot which shows really simple repro code.

User!Language vs CurrentThread

For formatting our Dates in the RDL-Files, we use the following format:
=First(FormatDateTime(Fields!SomeDate.Value, 2))
According to this Page, it should take the Computer's Regional Settings.
The problem is: If I call the Reporting-Service via another Service and try to set the Language:
rs.SetExecutionParameters(MapParameters(Report.Parameters).ToArray(), "de-CH");
This gets ignored. I tried to override the Thread-Cultures via
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("de-CH");
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("de-CH");
Which gets ignored as well.
Whats really string: The Reporting-Server itself has de-CH as culture as well, but it keeps using the english date-format.
Can someone tells me what's meant with "Computer's Regional Settings" and why the Reporting-Service refuses to take the passed Culture?
Edit: The language in the Report is
=User!Language
Generally said I'd like to pass the Report-Language from outside, be it via CurrentThread or via Parameter. But both get ignored.
Short Answer
Instead of FormatDateTime, you could use Format and specify your expected output format:
=First(Format(Fields!SomeDate.Value, "dd.MM.yyyy"))
Or another alternative to have the culture in a parameter, but in this case you will have to read the long answer.
Long answer
Your first attempt with the SetExecutionParameters was to set the culture of the parameters, which does not affect the report itself (only the parameters you pass to it).
Your second attempt was to change the culture of the client application, which also does not affect the report (only the client application culture).
The FormatDateTime function usually uses the computer regional settings, but not in Reporting Services. It will take the report culture, which in your case is User!Language.
User!Language returns the language configured in the client web browser when browsing to the report server.
I'm not sure what the behavior is when calling from web services (a specific setting taken or default to en-US).
The Report Language property can be an expression, so nothing stops you from adding another text parameter to the report, say, ReportCulture, and use this in Properties => Language:
=Parameters!ReportCulture.Value
You would have to keep using your expression for the dates:
=First(FormatDateTime(Fields!SomeDate.Value, 2))
You can configure a default value (de-CH in your case), so that this setting will only be specified if you want to override it.

Fixing JSON Date serialization from .Net web method

I am currently working on a project where I am sending a .Net type via ajax to a client application via ajax. I have no issues with the object being serialized and set to the client.
I run into issues when I take the exactly same object and post it back to the server via a web method with the following error: /Date(1373950800000)/ is not a valid value for DateTime. Which is pretty annoying as that is how Microsoft gave it to me, but that's besides the point.
Does anyone have a quick fix for this? I want a seamless way this can be accomplished without having to change the object right before returning it from the ajax call.
Your issue comes down to the server-side JavaScript serializer you are using; either JsonDataContractSerializer (default serializer for ASP.NET MVC) or NewtonSoft Json Serializer (default serializer for ASP.NET Web API).
For a visual example of this date mangling issue as well as possible solutions, check out JSON Dates are Different in ASP.NET MVC and Web API.
Handle the DateFormat while serialization
following code will resolve your problem
JsonConvert.SerializeObject(yourobject, Formatting.Indented,
new JsonSerializerSettings
{
DateFormatHandling = DateFormatHandling.IsoDateFormat
});
It will result the dates in 2009-02-15T00:00:00Z format
This one will help you with the error: click me
var yourDateTimeObject = ...
var converter = new IsoDateTimeConverter();
string isoDateTime = Newtonsoft.Json.JsonConvert.SerializeObject(yourDateTimeObject, converter);
This is the method that I use for these things:
First you need to clean the junk out of the date parameter
String unixDate = "/Date(1373950800000)/";
unixDate = unixDate.Replace("/Date(","").Replace(")/", "");
Now, as .NET and unix measure time in a different way, you have to compensate for that by creating a date set to the 1st of Jan 1970 and then adding the numeric part of the date that you were passed
DateTime dotNetDate = new DateTime(1970, 1, 1);
dotNetDate = dotNetDate.AddMilliseconds(Convert.ToInt64(unixDate)
You should also note that there will a loss of precision here - .NET dates are to the nearest nanosecond where unix dates are to the nearest millisecond
Try sending the dates with the forward slashes escaped. I have an iPad client that posts JSON to an ASP.NET WebAPI service method and we have to send dates this way:
"due": "\/Date(1335830400000)\/"

ASP.NET Globalization -- Displaying dates

Good morning,
Apologies for the newbie question. I'm just getting started with ASP.NET internationalization settings.
Background info:
I have a website which displays a <table> HTML object. In that <table> HTML object, I have a column which displays dates. My server being in the US, those dates show up as MM/DD/YYYY. Many of my users plug into this webpage through Excel, via the Data --> Import External Data --> Import Web Query interface. My users, for the most part, are in the US, so those dates show up correctly in their Excel screens.
Now I need to make the webpage work for UK users. As is, they are downloading the dates as MM/DD/YYYY, which makes their spreadsheets unusable since their regional settings are set to DD/MM/YYYY.
My question is:
How do I make it so the web server realizes that the incoming request has a en-GB culture setting? I could engineer my own little custom workaround, but I'm sure I'm not the first programmer to come across this. How do the pro's handle this? I'm looking for a solution that would be relatively simple and quick to put up, but I don't want to just put some crappy buggy piece of my own logic togethe that I'm going to dread 6 months from now.
Thanks a lot in advance,
-Alan.
A couple of points:
The <globalization> element also needs the attribute culture="auto". The uiCulture attribute affects the language used to retrieve resources. The culture attribute affects the culture used for formatting numbers an dates.
As noted in this MSDN article, it is not a best practice to rely exclusively on browser settings to determine the UI culture for a page. Users frequently use browsers that are not set to their preferences (for example, in an Internet cafe). You should provide a method for users to explicitly choose a language or language and culture (CultureInfo name) for the page.
You can allow the browser to set your UI culture automatically if you wish, by opening up the web.config, like this:
<configuration>
<system.web>
<globalization uiCulture="auto" />
...
And then the culture set by the browser will be automatically set in your app. This means that when you have the framework display date/time values, they will be formatted according to the current thread's UI Culture.
This will also help if you are using currency and/or localized text (however you have to provide the localized resources for each culture you support).
You could also accept a query string parameter for overriding the culture settings.
Culture initialization should go in the Page.InitializeCulture method.
protected override void InitializeCulture ( )
{
Thread.CurrentThread.CurrentCulture
= Thread.CurrentThread.CurrentUICulture
= Request.QueryString [ "culture" ] != null ? new CultureInfo ( Request.QueryString [ "culture" ] ) : CultureInfo.InvariantCulture;
//base.InitializeCulture ( );
}
Usage: http://tempuri.org/page.aspx?culture=en-GB

Best way to parse float?

What is the best way to parse a float in CSharp?
I know about TryParse, but what I'm particularly wondering about is dots, commas etc.
I'm having problems with my website. On my dev server, the ',' is for decimals, the '.' for separator. On the prod server though, it is the other way round.
How can I best capture this?
I agree with leppie's reply; to put that in terms of code:
string s = "123,456.789";
float f = float.Parse(s, CultureInfo.InvariantCulture);
Depends where the input is coming from.
If your input comes from the user, you should use the CultureInfo the user/page is using (Thread.CurrentThread.CurrentUICulture).
You can get and indication of the culture of the user, by looking at the HttpRequest.UserLanguages property. (Not correct 100%, but I've found it a very good first guess) With that information, you can set the Thread.CurrentThread.CurrentUICulture at the start of the page.
If your input comes from an internal source, you can use the InvariantCulture to parse the string.
The Parse method is somewhat easier to use, if your input is from a controlled source. That is, you have already validated the string. Parse throws a (slow) exception if its fails.
If the input is uncontrolled, (from the user, or other Internet source) the TryParse looks better to me.
If you want persist values ( numbers, date, time, etc... ) for internal purpose. Everytime use "InvariantCulture" for formating & parsing values. "InvariantCulture" is same on every computer, every OS with any user's culture/language/etc...
string strFloat = (15.789f).ToString(System.Globalization.CultureInfo.InvariantInfo);
float numFloat = float.Parse(System.Globalization.CultureInfo.InvariantInfo, strFloat);
string strNow = DateTime.Now.ToString(System.Globalization.CultureInfo.InvariantInfo);
DateTime now = DateTime.Parse(System.Globalization.CultureInfo.InvariantInfo, strNow);
You could always use the overload of Parse which includes the culture to use?
For instance:
double number = Double.Parse("42,22", new CultureInfo("nl-NL").NumberFormat); // dutch number formatting
If you have control over all your data, you should use "CultureInfo.InvariantCulture" in all of your code.
Use a neutral culture (or one you know) when parsing with Try/Parse.
Pass in a CultureInfo or NumberFormatInfo that represents the culture you want to parse the float as; this controls what characters are used for decimals, group separators, etc.
For example to ensure that the '.' character was treated as the decimal indicator you could pass in CultureInfo.InvariantCulture (this one is typically very useful in server applications where you tend to want things to be the same irrespective of the environment's culture).
Try to avoid float.Parse, use TryParse instead as it performs a lot better but does the same job.
this also applies to double, DateTime, etc...
(some types also offer TryParseExact which also performs even better!)
The source is an input from a website. I can't rely on it being valid. So I went with TryParse as mentioned before.
But I can't figure out how to give the currentCulture to it.
Also, this would give me the culture of the server it's currently running on, but since it's the world wide web, the user can be from anywhere...
you can know current Cuklture of your server with a simple statement:
System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CurrentCulture;
Note that there id a CurrentUICulture property, but UICulture is used from ResourceMeanager form multilanguages applications. for number formatting, you must considere CurrentCulture.
I hope this will help you
One approach is to force localization to use dot instead of comma separator - this way your code will work identically on all windows machines independently from selected language and settings.
This approach is applicable to small gained applications, like test applications, console applications and so on. For application, which was localization in use this is not so useful, but depends on requirements of application.
var CurrentCultureInfo = new CultureInfo("en", false);
CurrentCultureInfo.NumberFormat.NumberDecimalSeparator = ".";
CurrentCultureInfo.NumberFormat.CurrencyDecimalSeparator = ".";
Thread.CurrentThread.CurrentUICulture = CurrentCultureInfo;
Thread.CurrentThread.CurrentCulture = CurrentCultureInfo;
CultureInfo.DefaultThreadCurrentCulture = CurrentCultureInfo;
This code forces to use dot ('.') instead of comma, needs to be placed at application startup.
Since you don't know the web user's culture, you can do some guesswork. TryParse with a culture that uses , for separators and . for decimal, AND TryParse with a culture that uses . for separators and , for decimal. If they both succeed but yield different answers then you'll have to ask the user which they intended. Otherwise you can proceed normally, given your two equal results or one usable result or no usable result.

Categories