JSON that I get from the application has date field in format like
"Date":"2016-04-22T00:00:00.000+0000"
And when it gets deserialized by the RestSharp, the date becomes equal to
"04/22/2016 03:00:00"
After brief investigation, I understood that RestSharp automatically applies UTC offset for the parsed date. But in my case I need to get what is stored in JSON honestly.
Is there any way for RestSharp to disable auto applying UTC offset for date fields in JSON?
Thanks in advance
RestSharp will likely use the .Net DateTime methods to parse the string into a DateTime type. The DateTime.Parse method will convert the input string into the timezone read from Regional and Language options from the Control Panel. If you don't want that to happen, the Parse function should be supplied with a different Culture setting (e.g. InvariantCulture). If you don't have control over the RestSharp code, you could set a thread culture before calling the RestSharp method, use System.Threading.Thread.CurrentThread.CurrentCulture. This won't work if RestSharp runs on a different thread. In that case you could convert the input string by doing your own DateTime conversion negating the local machine timezone difference. Then you can then convert it to the proper string format again and use that as input to RestSharp.
It seems that the UTC JSON DateTime string "2016-04-22T00:00:00.000+0000" gets converted to a local DateTime object "04/22/2016 03:00:00".
One way out is to specify the DateTimeKind for each of you DateTime objects as Local and then convert them back to UTC. See this:
The better way out is to use Json.Net for serialization, under the hood. I have used it liked this:
private class MyWrappedRestClient : RestClient
{
public MyWrappedRestClient(string baseUrl) : base(baseUrl) { }
private IRestResponse<T> Deserialize<T>(IRestRequest request, IRestResponse rawResponse)
{
request.OnBeforeDeserialization(rawResponse);
var restResponse = (IRestResponse<T>)new RestResponse<T>();
try
{
restResponse = rawResponse.ToAsyncResponse<T>();
restResponse.Request = request;
if (restResponse.ErrorException == null)
{
restResponse.Data = JsonConvert.DeserializeObject<T>(restResponse.Content);
}
}
catch (Exception ex)
{
restResponse.ResponseStatus = ResponseStatus.Error;
restResponse.ErrorMessage = ex.Message;
restResponse.ErrorException = ex;
}
return restResponse;
}
public override IRestResponse<T> Execute<T>(IRestRequest request)
{
return Deserialize<T>(request, Execute(request));
}
}
Related
As stated in the title, i need to know how i can set the timezone to use in an OdataV4 Client.
In our Database we are storing DateTime-Values as GMT+1 since ever.
For quite some time we were using a WebApi which was working on OdataV3.
As long as we were on OdataV3 we hadn't had issues related to TimeZones.
After switching to OdataV4, we are now facing some real, almost showstopping issues regarding the fact, that even if we set the TimeZone on the server to GMT+1, the client is now converting DateTimes to UTC.
This is the output from the Server:
As you can see, the times are identical. The +02:00 is related to summertime!
Now for whatever reason, the client displays this timestamp while debugging:
I was trying to find a method, which tells the DataServiceContext to not use UTC but couldnt find any. The closest i got was this post but it seems not applicable to me.
There is quite a lot code, dealing with DateTimes and we cannot afford to refactor this all.
Also switching the Server back to UTC is not an option since every application has to be adjusted then.
Question
How can i set the DataServiceContext or an impacting component (JsonSerializer f.e.) to a TimeZone of my choice?
First thing that I check is that the OData configuration is set to UTC on the server, the following is a standard Registration method I use in my OData v4 APIs, I'll leave the other entries in there to help you identify where in the pipeline to implement the call to SetTimeZoneInfo
public static void Register(HttpConfiguration config)
{
// To enable $select and $filter on all fields by default
config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
config.SetDefaultQuerySettings(new Microsoft.AspNet.OData.Query.DefaultQuerySettings() { EnableCount = true, EnableExpand = true, EnableFilter = true, EnableOrderBy = true, EnableSelect = true, MaxTop = null });
config.AddODataQueryFilter(new EnableQueryAttribute());
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
// Set the timezone to UTC
config.SetTimeZoneInfo(System.TimeZoneInfo.Utc);
// Register the odata routes and other config
...
}
The above code is specifying UTC for all DateTimes that do not specify a timezone. The following variations show how you could set other timezones:
config.SetTimeZoneInfo(System.TimeZoneInfo.Utc);
config.SetTimeZoneInfo(System.TimeZoneInfo.Local);
config.SetTimeZoneInfo(System.TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time"));
If you wanted to affect the serialiser directly then you will need to register your own customised ODataSerializerProvider as the OData v4 framework uses this to deserialise the http request. That can be pretty involved, so try the simple option first.
I think this is a workaround that could perhaps solve your issue:
It does not override deserialization but it fixes the dates by modifying the response.
The response is deserialized to UTC by default, and this will convert all DateTime objects to the local timezone(you can modify the code to change to any timezone you want). I assume your server and clients are in the same timezone.
You can set the server timezone to whatever you want, but I assume it will be +1 (most likely local).
/// <inheritdoc />
public class YourDataServiceContext: DataServiceContext
{
/// <inheritdoc />
protected YourDataServiceContext(Uri uri) : base(uri)
{
//add any code if you need here
Configurations.ResponsePipeline.OnEntityMaterialized(ConvertDatesToLocalZone);
}
private void ConvertDatesToLocalZone(MaterializedEntityArgs obj)
{
var entity = obj?.Entity;
if (entity == null) return;
var props = entity.GetType()
.GetProperties()
.Where(it =>
it.PropertyType == typeof(DateTime)
|| it.PropertyType == typeof(DateTime?));
foreach (var prop in props)
{
//get value and check if it isn't null
var value = prop.GetValue(entity);
if (!(value is DateTime oldValue)) continue;
//check if property has setter
var setMethod = prop.SetMethod;
if (setMethod == null) continue;
//convert to local time
value = oldValue.ToLocalTime();
//set the new value
setMethod.Invoke(entity, new[] { value });
}
}
}
Problem
DateTime format changes from 2019-04-09T11:43:11Z to 2019-04-09T11:43:11+00:0 of my queue message.
Details
Consider two azure functions F1 and F2. F1 enqueues a message in f2-queue.
Consider the following classes for understanding the preparation of a message:
public class MyRequest
{
public int Id {get; set;}
public object Obj {get; set;}
}
public Class MyMessage
{
public DateTime UpdatedDate {get; set;}
}
I am using ADO.NET for reading from the database.
myMessage.UpdatedDate = DateTime.SpecifyKind(Convert.ToDateTime(reader["Updated_Date"]), DateTimeKind.Utc);
myRequest.Obj = myMessage;
I serialize myRequest using JsonConvert.SerializeObject. The serialized object has datetime as 2019-04-09T11:43:11Z. I enqueue myRequest in f2-queue.
F2 is a queue triggered azure function.
[FunctionName("F2")]
public static async Task Run([QueueTrigger("f2-queue", Connection = "")]MyRequest myRequest, ILogger log)
Here in myRequest the DateTime format of UpdatedDate changes to 2019-04-09T11:43:11+00:00.
Things I tried
Placed a DateTime property in MyRequest itself and stored DateTime.UtcNow in it. DateTime format does not change in this case.
It appears that +00:00 changes according to the time zone set in the machine the code is running on.
Originally was using DateTime.TryParse(reader["Updated_Date"].ToString(), out DateTime lastUpdatedDate) and noticed the DateTime.Kind was Unspecified. Changed to the above-mentioned approach. The DateTime.Kind is Utc now but DateTime format is still changing.
Instead of assigning the value from the database, assigned DateTime.UtcNow. The format still changes. It appears there is no problem in assigning the value.
Looked into the queue using Storage Explorer. The message itself has a perfectly fine DateTime format.
It does appear to be because of how Newtonsoft serializes DateTimes when Kind is set to Utc vs. Local. In order to change this serverside you cane use a DateFormatString setting to override the defaults.
I tested it using a console app, but the behavior should be the same within a Function. The WebJob underlying the Functions project is based on a console app.
namespace ConsoleApp8
{
class Program
{
static void Main(string[] args)
{
var serializerSettings = new JsonSerializerSettings()
{
DateFormatString = "yyyy-MM-ddThh:mm:ssZ"
};
Console.WriteLine($"DateTime.Now: {JsonConvert.SerializeObject(DateTime.Now)}, DateTime.Now Kind: {DateTime.Now.Kind}");
Console.WriteLine($"DateTime.UtcNow: {JsonConvert.SerializeObject(DateTime.UtcNow)}, DateTime.UtcNow Kind: {DateTime.UtcNow.Kind}");
Console.WriteLine($"DateTime.Now with formatting: {JsonConvert.SerializeObject(DateTime.Now, serializerSettings)}, DateTime.Now Kind: {DateTime.Now.Kind}");
Console.ReadLine();
}
}
}
The code outputs the following:
DateTime.Now: "2019-04-16T10:53:54.6557713-04:00", DateTime.Now Kind: Local
DateTime.UtcNow: "2019-04-16T14:53:54.9357612Z", DateTime.UtcNow Kind: Utc
DateTime.Now with formatting: "2019-04-16T10:53:54Z", DateTime.Now Kind: Local
I have a rest call that returns this (using Advance Rest Client in Chrome to do testing):
MyObject: [22]
0: {
ID: "123456"
UTC1: "2013-04-19T03:12:32Z"
UTC2: "2013-04-19T03:12:36.994Z"
}
The code that grabs the response and serializes it to an object looks like this:
IRestResponse<List<MyObject>> response = client.Execute<List<MyObject>>(request);
When I look at the response object one of the dates is wrong. If I inspect it or use the objects in any way I get this:
UTC1: 4/19/2013 3:12
UTC2: 4/18/2013 9:12:36 PM <--CONVERTED!!
I need both to be serialized as the time that is returned in the response, not converted from UTC/GMT to local time. As you can see above one value keeps its UTC value while the other gets converted to my timezone. I figured that both were being run through the Convert.DateTime function but if I do that with the strings both values come out as converted to local time. I realize that one fo the original values (the one that is getting converted) doesn't exactly obey ISO 8601 format (too much precision); unfortunately that is the data I have to work with for now.
Can anyone tell me how to force RestSharp to make sure both dates are in UTC?
Use Json.NET to do the deserialization instead of the built in RestSharp deserializer.
response = client.Execute(request);
var myObjects = JsonConvert.Deserialize<List<MyObject>>(response)
Posting this for convenience:
private class CustomRestClient : RestClient
{
public CustomRestClient(string baseUrl) : base(baseUrl) { }
private IRestResponse<T> Deserialize<T>(IRestRequest request, IRestResponse raw)
{
request.OnBeforeDeserialization(raw);
var restResponse = (IRestResponse<T>)new RestResponse<T>();
try
{
restResponse = ResponseExtensions.toAsyncResponse<T>(raw);
restResponse.Request = request;
if (restResponse.ErrorException == null)
{
restResponse.Data = JsonConvert.DeserializeObject<T>(restResponse.Content);
}
}
catch (Exception ex)
{
restResponse.ResponseStatus = ResponseStatus.Error;
restResponse.ErrorMessage = ex.Message;
restResponse.ErrorException = ex;
}
return restResponse;
}
public override IRestResponse<T> Execute<T>(IRestRequest request)
{
return Deserialize<T>(request, Execute(request));
}
}
This is a simple code I put together, it just overrides Execute<T> and uses Json.net under the hood.
Basically, my datepicker uses UK format of dd/mm/yyyy. But when I submit the form, ASP.net is clearly using US format.
(will only accept days if less than 12, i.e. thinks it is the month.)
public ActionResult TimeTable(DateTime ViewDate)
Is there a way to force it to recognise a certain way?
Strangely though, other methods of insert seem to recognise the correct format.
"The parameters dictionary contains a null entry for parameter ViewDate of non-nullable type System.DateTime for method System.Web.Mvc.ActionResult Index(System.DateTime) in Mysite.Controllers.RoomBookingsController. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
Have a read of this. It gives a good explanation of what's happening and why it works as it does.
I'm in the situation where I know everyone using the site is in the UK so I can safely override the default DateTime model binder:
public class DateTimeModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var date = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
if (String.IsNullOrEmpty(date))
return null;
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, bindingContext.ValueProvider.GetValue(bindingContext.ModelName));
try
{
return DateTime.Parse(date);
}
catch (Exception)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, String.Format("\"{0}\" is invalid.", bindingContext.ModelName));
return null;
}
}
}
You need to use custom ModelBinder for your DateTime. I had that same problem as you.
Have you tried setting the Current Culture as en-GB?
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
CultureInfo cultureInfo = CultureInfo.GetCultureInfo("en-GB");
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
}
You can do it:
globally (in global.asax under Application_Start() ):
ModelBinders.Binders.Add(typeof(DateTime), new DateTimeModelBinder());
for a method:
public ActionResult TimeTable([Binder(typeof(DateTimeModelBinder)]DateTime ViewDate)
for a custom model class - ah no, no posibility cause you use struct DateTime ;-)
Ah, sorry I couldn't add a comment to Adams post - it's based on his code.
Basically, my datepicker uses UK format of dd/mm/yyyy
Beginner mistake. It should use whatever format the browser is set to. The problem is not a format, but different formats between client and server. The server should emit code that formats the date according to the negotiated locale which the server then also uses to parse it.
But when I submit the form, ASP.NET is clearly using US format.
Nope. This is saying that when I take a spice it always is salt and then you always take salt. Your Server accepts the currently negotiated culture which - unless you modify settings - is negotiated between client and server. Check the threads current culture when it should do the parsing to see what it is set for.
From my BindigTools for bindning DateTime? (Nullable), based on some book sample - Pro MVC3
public static DateTime? GetValueNDateTime(ModelBindingContext context, string searchPrefix, string key, string format)
{
ValueProviderResult vpr = context.ValueProvider.GetValue(searchPrefix + key);
DateTime outVal;
if (DateTime.TryParseExact(vpr.AttemptedValue, format, null, System.Globalization.DateTimeStyles.None, out outVal))
{
return outVal;
}
else
{
return null;
}
}
It uses parsing exact so you shouldn't have any problem with parsed dates.
I'm having a problem sending a java.util.Date object from my C# client to my java webserver.
When I am calling a WebMethod with a Date WebParam it works. But if I am calling a WebMethod with a custom object that has a Date as WebParam it's always null.
So, this works:
#WebMethod(operationName="thisWorks")
public void thisWorks(#WebParam(name="from")Date from)
{
System.out.println(from); //prints the value of the date
}
This doesn't work:
class MyObj { java.util.Date getMyDate(); }
#WebMethod(operationName="thisDoesntWork")
public void thisDoesntWork(#WebParam(name="myObj")MyObj myObj)
{
System.out.println(myObj.getMyDate()); //prints null
}
Client:
ServiceClient client = new ServiceClient();
client.thisWorks(DateTime.Now);
myObj o = new myObj();
o.myDate = DateTime.Now;
client.thisDoesntWork(o);
The wsdl generates an extra field for the myDate: "bool myDateSpecified". When I set this to true, it works. This is weird, cause when I would have an int field instead of date I also get a specified field for it, but now I dont have to set the specified field for it to work.
This issue seems to be the .Net XML Serializer. I did rewrite my code in Java and it works beautifully.
I can think in a way to workaround the {!variable}Specified = true; sentence:
Move the entire declaration of the object to a separated namespace. So, everytime you update the WSDL your code does not get overwritten.
And do not use the System.Nullable<bool> in the property declaration, use bool or DateTime or Double. Get the point?