How to format url datetime parameter with timezone - c#

I've searched everywhere and can't find the answer. I would like to understand what's going with the way I format the datetime (with timezone) url parameter.
Here is the situation:
The caller program is having DateTime value with UTC timezone.
The receiver Json WebAPI (C#) is running on my local pc which is having Central timezone.
The PoCreationDate is a DateTime type (C#). I do not write code to parse the PoCreationDate value. C# converted it to DateTime object for me auto-magically (thru Serialization?).
Here are the test cases:
Case 1:
This one works
http://******/api/ItemSource/GetItemSourceOption?OrganizationCode=OKC&PoCreationDate=2018-10-08T01:02:03.0000000-05:00
Case 2:
This one does not work and the browser is displaying
<Error>
<Message>The request is invalid.</Message>
</Error>
http://******/api/ItemSource/GetItemSourceOption?OrganizationCode=OKC&PoCreationDate=2018-10-08T05:00:00.0000000+00:00
Notice the different? one of them have a -05:00 the other have +00:00. My timezone is Central (which is -05:00 right now?)
Case 3:
My current work around is to format it this way
http://******/api/ItemSource/GetItemSourceOption?OrganizationCode=OKC&PoCreationDate=2018-10-08T05:00:00Z
=====================
So I am just trying to understand what's going on here and these my thoughts...
I believe that using the Z format is the best solution since the DateTime value (from the source) is always in UTC format.
About the -05:00 and +00:00, are these supposed to be set per the receiver's timezone (the destination Server local timezone)? So for this case, my PC is the receiver (WebAPI) and it's set to Central Time Zone, therefore this value must be -05:00 to represent the current value for Central Time Zone?
Please help me understand this. Thanks.

The + character in the offset is being interpreted as a space, as per URL encoding rules. You will need to encode it, such that it is replaced with %2B.
If you are building this URL from JavaScript, use the encodeURIComponent function.
If you are building it in C#, use the System.Net.WebUtility.UrlEncode method.
Note that doing so will also replace the : characters with %3A, which is optional in a querystring parameter, but still recommended.
In general, parameters passed in the querystring need to be encoded, unless you can guarantee that they contain no special characters.
Also, you may want to ask yourself if this field really needs to be a full date+time+offset. In many cases, one might expect a field like PoCreationDate to be just a date, as in "2018-10-08". Of course, that depends on your application logic and business requirements.

Related

How to strip timezone from DateTime in .NET Core

I have a use case that I'm not sure how to solve in a nice way.
I'm currently developing a .Net Core WebApi that is receiving data from various current systems, from a cross the whole world. Which I then process and lastly I commit it to SAP through oData endpoint.
The problem I'm having is on of parameters I'm receiving in the body payload, is a DateTime. Previous I have not have any issues. But not long ago I started getting data from a other system which deliverers it in a slightly differently way.
Previously this was the format I got: 2020-09-16T16:30:00 not problem with it. But the new system looks like this: 2020-09-16T16:00:00 -05:00 Could also end in +08:00.
The problem I'm facing is that SAP needs to get in local time. But in the my code it converts this: 2020-09-16T16:00:00 -05:00 to 2020-09-16T23:00:00 when I see the incoming payload in the controller.
I have searched quite a bit to find a solution. But 99% only suggest using UTC time, which is not a option for me.
Another option is to use DateTimeOffset, which I have tried but can't the time conversion to use localTime.
My question is. Are it not possible to custom convert to strip the timezone before it hits the controller?
Generarally when you're working with datetime data that includes offsets for time zone the DateTimeOffset type is a good place to start. The sample string 2020-09-16T16:00:00 -05:00 can be passed to DateTimeOffset.Parse() to get a correct DTO value with timezone information attached. From there you can get the local time, UTC time or a DateTime value with the timezone stripped.
string source = "2020-09-16T16:00:00 -05:00";
string fmt = #"yyyy-MM-dd\THH:mm:ss zzz"
// Same as input
Console.WriteLine(DateTimeOffset.Parse(source).ToString(fmt));
// Adjusted to your local timezone
Console.WriteLine(DateTimeOffset.Parse(source).ToLocalTime().ToString(fmt));
// DateTime portion of the source, timezone offset ignored
Console.WriteLine(DateTimeOffset.Parse(source).DateTime.ToString());
Getting the UTC time is simple too via the UtcDateTime property.
It sounds like what you want is the last one - just the date and time from the inputt string with the timezone offset stripped. If you just want the corresponding local time then DateTime.Parse should give that to you directly.
The JsonSerializer class doesn't support this format for DateTimeOffset so you might have some trouble getting it converted before hitting your controller. In that case you'd need to accept a string and do the conversion by hand in your code. You also might need to investigate the TryParseExact method.
Use DateTime.Parse() , for example
string timeInString1 = "2020-09-16T16:00:00 -05:00";
DateTime moment1 = DateTime.Parse(timeInString1);
string timeInString2 = "2020-09-16T16:00:00 +08:00";
DateTime moment2 = DateTime.Parse(timeInString2);
string timeInString3 = "2020-09-16T16:30:00";
DateTime moment3 = DateTime.Parse(timeInString3);
but momen1, momen2, or moment3 is non-timezone awareness value.

UTC timezone in the OK() function in .NET WebAPI C#

I have a .NET WebAPI which returns some dates as JSON.
I'm using the Ok() function which takes the C# DateTime list and convert them into a JSON array.
My requirement is to specify the timezone in any case (even +00:00)
Now, the format I'm using is:
jsonSerializerSettings.DateFormatString = "o";
which uses the ISO standard and a "Z" to specify UTC.
Unfortunately my web application doesn't understand such convention (I mean, the final "Z") and it requires a timezone offset to be present in any case.
In other words, I'd like to use a "+00:00" instead of the "Z".
How can I tell WebAPI to use such convention?
Previously I had:
jsonSerializerSettings.DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffzzz";
which doesn't had the timezone information.
This is not as easy as one would like - mainly, because the DateTime type is confusing at its best, and wrong at its worst.
I would recommend handling only UTC dates on the server (i.e. assuming UTC on any incoming date, saving everything as UTC and returning UTC everywhere) and letting your clients decide if they need to do something about that (e.g. convert to a local time for correct display). Then, it could be as easy as setting
jsonSerializerSettings.DateFormatString = "yyyy-MM-ddTHH:mm:ss.fff+00:00";
i.e. hard-coding the +00:00 there, since you know (by assumption) that it's going to be UTC.
Use System.DateTimeOffset instead of Use System.DateTime in your return statement (Ok). This will automatically append the +00:00 the the serialized string (+00:00 is assuming its a UTC point in time).
for Web API 2 and higher, I am not sure about previous versions
This is also the default date/time format for other Web API extensions like oData.

Setting format of DateTime - how do I keep the value in DateTime format?

I have a DateTime being created in the format dd/MM/yyyy HH:mm:ss. I am writing code that interacts with a third-party SOAP library that requires a DateTime variable, in the format yyyy-MM-dd HH:mm:ss.
How do I change the way the information is stored in the DateTime variable, for the purpose of the call to the third-party SOAP library, i.e. no system-wide changes to dates?
I have investigated CultureInfo, which is mildly confusing and possibly too permanent a solution; the only time I need the DateTime changing is for an instance of this single call.
As an explanation, the library has a function GetOrders(DateTime startDate, DateTime endDate, TradingRoleCodeType roleType, OrderStatusCodeType statusType). When attempting to perform the function with DateTimes as created, it generates an error "Sorry, the end date was missing, invalid, or before the start date. must be in YYYY-MM-DD or YYYY-MM-DD HH:MI:SS format, and after the start date.". Given the format that is passed in as dd/MM/yyyy HH:mm:ss, I'd think this may be the problem.
I have a DateTime being created in the format dd/MM/yyyy HH:ii:ss
No, you do not. You have a DateTime. It has no format. It is a number - which is well documented, you know, in the documentation. The string form is never used in a stored DateTime, only when generating the string for presentation.
How do I change the way the information is stored in the DateTime
variable, for the purpose of the call to the third-party SOAP library,
i.e. no system-wide changes to dates?
You do not. I would suggest you talk to your SOAP library - and it is not SOAP btw., IIRC the format you give as example is not valid in SOAP. Yes, bad news. Someone wants Pseudo-Soap.
http://www.w3schools.com/schema/schema_dtypes_date.asp
describes all valid date, time and datetime formats and yours is NOT there.
You can change the default format on a thread level back and forth, so one solution is to set it before calls into the soap library. Another one is to have someone fix the SOAP layer to accept standard formats.
You can create a dummy date :
public class SomeClass
{
[XmlIgnore]
public DateTime SomeDate { get; set; }
[XmlElement("SomeDate")]
public string SomeDateString
{
get { return this.SomeDate.ToString("yyyy-MM-dd HH:mm:ss"); }
set { this.SomeDate = DateTime.Parse(value); }
}
}
Source : Force XmlSerializer to serialize DateTime as 'YYYY-MM-DD hh:mm:ss' --kbrimington
As it turns out, the problem - as some have pointed out - is not to do with the variable being a DateTime, nor its "format" which is not a "format", but is certainly the representation of the information in a method to be understood.
The basic issue with the information was a DateTime comparison between standard time and UTC time. The third-party library examined the DateTime as a UTC DateTime, which when at the right time of year to be caught with a difference in times can cause a problem comparing a DateTime; despite being presented as after the reference time to the user, the time is actually before the reference time when being calculated, meaning the comparison fails.
The main takeaway for this question is to interrogate the information being passed to functions, if you don't have access to third-party library code nor access to documentation with sufficient detail, and errors are occurring when interacting with said third-party code.
Particularly, test various use cases to determine what variable values cause a failure and which cause successful execution of code; identify a pattern, and then test specific use cases that confirm the pattern. From there, determine the actual error that is occurring and code to fix the issue.
In the case of DateTimes, where the code understands DateTimeKinds such as C#, remember to test the different DateTimeKinds to establish whether they can be a part of the problem; its not happened to me often, but it has happened (as evidenced by this question).
Finally, error codes don't help much, and can lead to poor questions and poor advice; trial and error appears to be the best in cases similar to this.
You don't need to change how it's stored, as already mentioned above.
You need to format is as a string according to ISO8601, which is what your SOAP service expects datetime parameter to be.
Check How to parse and generate DateTime objects in ISO 8601 format
and
Given a DateTime object, how do I get an ISO 8601 date in string format?

DateTime Conversion with Different Cultures

I am wondering what is the best way to figure out how to convert a datetime?
Right now my computer is setup as dd/mm/yyyy yet I am taking date as mm/dd/yyy so when I try to do
DateTime.Convert();
DateTime.Parse();
DateTime.TryParse();
I either get nothing back or it crashes. Now I could tell it about the mm/dd/yyyy format and it probably would convert. However the problem is these dates are are coming from user uploaded files.
They could be in any format combination. I am trying to find ways to do it.
The problem I see is that I am looking at the dates in an uploaded file so I am not sure if looking say at the browser date format would help or not.
I am using asp.net mvc and I am trying to get a solution that handle date formats automatically so I don't have to ask the user about the date format they have (I figure the more things I have to ask them the less likely the user will continue on)
No, you can't figure out automatically what date-time format a user meant to use once the value is on the server. You need more information to parse it correctly (e.g. 1/2/3 can mean a lot of different dates depending on the culture).
Consider one of the following solutions:
Convert the entered date to a text representation in a standard format (i.e. ISO 8601 - 2012-02-09) using JavaScript on the client before you send it to the server. The code would look something like this: d.getUTCFullYear()+"-" + d.getUTCMonth() + "-" + d.getUTCDate().
Send the local culture information to the server along with date value to be converted and do the conversion on the server.
Force the user to enter the date in a specific format (e.g. Use 3 text boxes labeled "Month", "Day", and "Year" instead of one text box with free input).
chobo2 (I like the 'handle') :)
you can detect the locale culture and work on that at will. see the following SO Q/A for pointers:
Where is the system locale/culture set for .Net
the key is to NOT have to set anything in particular, but identify the locale and act accordingly.

Timezone Strategy

I am building a MVC 3 application where the users may not be in the same time zone, so my intent was to store everything in UTC and convert from UTC to local time in the views and localtime to UTC on submissions.
Doing some browsing though there doesn't seem to be a lot of good solutions to this. To be honest, I sort of expected an attribute to be available to auto convert UTC time into local time at least, but it seems not to exist.
I feel like just trying to be diligent about manually converting every input to UTC and manually converting every view to local time display will be very error prone and lead to difficult to detect bugs where the time is not converted to or from.
Any suggestions on how to deal with this as a general strategy?
EDIT
Everyone seems very stuck on the "how do I get the client timezone" piece, which as I mention in one of the comments is not my concern. I am fine with a user setting that determines their timezone, so assume I already know what the client time zone is...that doesn't address my problem.
Right now, on each view when I render a date, I would need to call a method to render it in the local time zone from utc. Every time I send a date for submission to the server I need to convert it from the local timezone to UTC. If I forget to do this there will be problems...either a submitted date will be wrong or client side reports and filters will be wrong.
What I was hoping existed was a more automated method, especially since the view model is strongly typed in MVC 3 I was hoping for sum magic to be able to at least automatically render in a time zone, if not handle the submission, just like the date format or range can be controlled by an attribute.
So like
[DateRange]
Public DateTime MyDate
I could have something like
[ConvertToUTC(offset)]
Public DateTime MyDate
Anyway, I guess it look like my only approach would be to write a custom data annotation to render it in a time zone, and a override on the MVC 3 model binder so incoming dates are converted unless I want to wrap ever date in a method call. So unless anyone has further comments or suggestions it will be one of those two options, I'm just surprised something doesn't exist already to do this.
If I do implement a solution I will be sure to post it.
Edit 2
Something like This http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx for MVC 3 views and view models is what I am looking for.
Final Edit
I marked epignosisx answer as correct, but also have a few comments to add.
I found something similar here:
http://dalldorf.com/blog/2011/06/mvc3-timezones-1/
With an implementation of getting the timezone from the client by placing it in the cookie for people that want that in part 2 (link below since the link on the first part of the article to part 2 doesn't work)
http://dalldorf.com/blog/2011/09/mvc3-timezones-2/
Its important to note with these approaches that you MUST you Editfor and Displayfor instead of things like TextForFor as only EditFor and DisplayFor make use of the metadata providers used to tell MVC how to display the property of that type on the model. If you access the model values directly in the view (#Model.MyDate) no conversion will take place.
You could handle the problem of converting UTC to user local time by using website-wide DisplayTemplate for DateTime.
From your Views you would use #Html.DisplayFor(n => n.MyDateTimeProperty)
The second problem is tougher to tackle. To convert from user local time to UTC you could override the DefaultModelBinder. Specifically the method SetProperty. Here is a naive implementation that demonstrates the point. It only applies for DateTime but could easily be extended to DateTime?. Then set it up as your Default binder in the Global.asax
public class MyDefaultModelBinder : DefaultModelBinder
{
protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
{
//special case for DateTime
if(propertyDescriptor.PropertyType == typeof(DateTime))
{
if (propertyDescriptor.IsReadOnly)
{
return;
}
try
{
if(value != null)
{
DateTime dt = (DateTime)value;
propertyDescriptor.SetValue(bindingContext.Model, dt.ToUniversalTime());
}
}
catch (Exception ex)
{
string modelStateKey = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
bindingContext.ModelState.AddModelError(modelStateKey, ex);
}
}
else
{
//handles all other types
base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
}
}
}
First this is mostly a duplicate of How can I determine a web user's time zone?, I agree with the majority vote there for this response:
The most popular (==standard?) way of determining the time zone I've
seen around is simply asking the user herself. If your website
requires subscription, this could be saved in the users' profile data.
For anon users, the dates could be displayed as UTC or GMT or some
such.
That being said the most common approach to automatically setting this value is to use javascript's Date getTimezoneOffset(). This can then be feed back to the server via a cookie or ajax request and stored with the user's profile, session, or cookie.
Ultimately I still think you should allow users to change this setting so that you can determine not just the UTC offset, but the actual timezone and daylight savings information as well.
When collecting input from users conversion to and from UTC via DateTime should suffice. DateTimeOffset is great when the client is managed code; however, with pure html/javascript it really isn't going to buy you much. Moreover the additional information of DateTimeOffset is not necessarily needed by most applications unless you intend to display to other users the originating timezone information.
I feel like just trying to be diligent about manually converting every
input to UTC and manually converting every view to local time display
will be very error prone and lead to difficult to detect bugs where
the time is not converted to or from.
You should not depend upon diligence for correct date+time formatting and parsing. The .NET framework should handle this for you, for starters see "How to: Set the Culture and UI Culture for ASP.NET Web Page Globalization".
Closing remark
Frankly this is all a pain in the neck. We threw out the implementation some years ago and started transferring all date+time information in UTC, then we use Javascript to convert to local time and display format. This is really the only working model IMHO.
You can use something like MomentJS to display dates/times. Helps with formatting and local times.
This is not possible automatically, you will have to do some manual work.
1 If you do not want to store user's timezone in db
1.1 Without masterpage: As csharptest.net suggested use java script's getDateTimeOffset() to get the timezone offset, set value in cookie, write a module to check for cookie if cookie is not present insert java script code and cookie using module.
1.2 Using masterpage: Same thing but no need to write module for checking.
2 Store user's timezone in db (best & easiest)
No need to use javascript to get timezone just convert datetime according to user's timezone.

Categories

Resources