Change DateTimeOffset.Offset Property?
Asked Answered
F

3

7

My end goal is to get universal time from client with NO offset - just UTC time. I try to accomplish this like so:

Javascript: (new Date()).toUTCString(), the output of which logs: Thu, 17 Mar 2016 15:13:23 GMT, which is exactly what I need.

Then I take it to server and try to convert it to DateTimeOffset:

string dateTimeOffsetPattern = "ddd, dd MMM yyyy HH:mm:ss 'GMT'"; 

DateTimeOffset clientLoginTime = DateTimeOffset.ParseExact
    (timeStringFromClient, dateTimeOffsetPattern, CultureInfo.InvariantCulture);

Which results in:

3/17/2016 3:13:23 PM -04:00 

Somehow it adjusts the time for my local (Eastern) offset. I do NOT want this to happen, I want it to just return the UTC time, like so:

3/17/2016 3:13:23 PM +00:00

P.S. I did just ask another question about this, and I apologize, since I feel like it should be easy enough, but I don't get it. This should be really simple, but it looks like offset doesn't have a setter (unless I am completely missing some C# basics as usual):

public TimeSpan Offset { get; }
Fireboard answered 17/3, 2016 at 15:45 Comment(5)
I literally just send the string to the server. I have a Web API2 controller taking a string argument, which receives the string created by: (new Date()).toUTCString()Fireboard
Web API2 - good, thats important. Are you sending it as JSON? Are you using JSON.NET for the deserialization? Also what is the value of the raw string/payload that is being sent?Consistory
@HansPassant: The answer I needed just got posted. But yea, I am not debating that I could be architecting this better. Igor, I updated with the info you asked. I think I know where you were going with that, but it doesn't seem to be changing the string here.Fireboard
You should really send date time objects to/from the server using ISO8601 format. That would be yyyy-MM-ddTHH-mm-ss.sssz. Especially when using browsers where the culture of the browse will determine the string of the date if you send it like that.Consistory
Thanks Igor, that's something for me to check.Fireboard
E
7

There's an overload of ParseExact where you can specify a DateTimeStyles. One of the values of DateTimeValues is AssumeUniversal, which says:

If format does not require that input contain an offset value, the returned DateTimeOffset object is given the UTC offset (+00:00).

which basically means "don't assume it's local, assume it's universal." Assuming local is the default, which is why you're seeing the result you are in that it's adjusting to local. Specifying AssumeUniversal should parse it the way you want.

DateTimeOffset clientLoginTime = DateTimeOffset.ParseExact
(timeStringFromClient, dateTimeOffsetPattern, CultureInfo.InvariantCulture, 
    DateTimeStyles.AssumeUniversal);
Eudocia answered 17/3, 2016 at 15:53 Comment(1)
That's EXACTLY what I needed. Works perfectly. Thank you, will come back to accept after countdown ends.Fireboard
J
5

I would parse from JS normally, then do the following:

  • Strip OffSet from DateTimeOffset by returning DateTime
  • Set OffSetby instantiating another DateTimeOffset having TimeSpan set to ZERO.

In your case:

var clientLoginTime = new DateTimeOffset(clientLoginTime.DateTime, TimeSpan.FromHours(0));

This can be easily converted into an extension method

public static DateTimeOffset SetOffset(this DateTimeOffset dto, int timeZoneDiff)
{
    return new DateTimeOffset(dto.DateTime, TimeSpan.FromHours(timeZoneDiff));
}
Jonna answered 4/7, 2016 at 6:23 Comment(0)
C
2

From JavaScript (or any other client) you should send DateTimes using ISO8601 format which would be yyyy-MM-ddTHH-mm-ss.sssz. The Z indicates that the datetime instance is GMT (UTC). You can also change this to add + or - from GMT. You can do this using the JavaScript Date object using myDate.toISOString()

When creating your WebAPI model(s) you can then use either a DateTime or DateTimeOffset types directly. The JSON.NET serializer for Web API will automatically deserialize the sent ISO8601 datetime string to the correct DateTime or DateTimeOffset type (depending on which one you are using). This means that you do not have to do any manuall parsing in your code which is good. (Imagine if you had to send everything as string and parse everything manually in all your methods?).

So you can now have a method

public async Task<IHttpActionResult> GetRecords(DateTimeOffset? startFrom)

And the startFrom will automatically be populated based on the sent ISO8601 formatted DateTime string.

Finally, the last and most import reason to do this is that your clients will probably not all use the same locale. You could have a user that has their browser set to Spanish so .toUTCString() will not yield an English string or possibly even a string with mm/dd but the reverse (like most countries other than the USA).

Long story short for WebAPI

  • Use ISO8601 from/to client.
  • Use DateTimeOffset or DateTime instances directly in your model (no parsing).
Consistory answered 17/3, 2016 at 16:23 Comment(1)
Appreciate the info.Fireboard

© 2022 - 2024 — McMap. All rights reserved.