Passing a time zone into a web api call from moment with nodatime

SpaceCowboy74 picture SpaceCowboy74 · May 12, 2015 · Viewed 7k times · Source

I'm about to go insane dealing with datetime issues and the web.

I have a web server hosted in the Central Time Zone. When clients in the Eastern Time Zone try and schedule an item for a given day using my app, they pass in the value of (for example) 3/14/2015. When we pass the code back to our model, which is sent to the web api, we persist is using something like the code below.

moment.utc($("#mydatepicker").val).hour(0).minute(0).second(0)).toISOString();

This results in a string like the following:

2015-03-14T04:00:00.000Z

When the item is converted back on the server in web api, it converts to

3/13/2015 11:00:00 PM

Logic then strips off time and you can see what happens from here. Since I stripped off the time, it is now the day prior and that is the value persisted to the database.

I need to know some way to send a value from moment, into the web api preferrably as a ZonedDateTime in the client's time zone. I can then convert it to UTC for persistance in the DB.

I've seen things about using NodaTime.Serialization.JsonNet, but I am unclear on how to to use it with Moment and pass it back and forth across web api/ajax.

Answer

Matt Johnson-Pint picture Matt Johnson-Pint · May 12, 2015

I need to know some way to send a value from moment, into the web api preferrably as a ZonedDateTime in the client's time zone. I can then convert it to UTC for persistance in the DB.

If that's what you want, then:

  • In your moment.js code, use .format() instead of .toISOString(), which will still give you an ISO8601 string, but will include the local offset instead of setting it to UTC.

  • In your ASP.Net code, define your values as a DateTimeOffset (or a noda OffsetDateTime) rather than a DateTime.

However, I don't think that's really what you want. When it comes to dates and times, context is super important. Here, you said you were picking a date from a date picker. When you do that - what time is being chosen by the user? In most cases, they aren't choosing a time - they're just picking a date. But since the JavaScript Date object is really a "date + time" object, it assigns midnight as a default time. Moment is no better in this regard.

Really, converting to UTC doesn't make logical sense when you are just talking about a calendar date. The string value you probably should be sending across the wire should just be a whole date, as in "2015-03-14". My guess is that is what you are starting with anyway. If not, then do moment.utc(yourvalue).format("YYYY-MM-DD") to get it. (Using UTC here is just a way to avoid local time zone issues, like midnight not existing in Brazil on the spring-forward day.)

This corresponds to the NodaTime LocalDate type in your .NET code. If you weren't using Noda Time, you would define the type as a DateTime and just ignore the time portion. In your database, if there's a date-only type available, then use it. For example, SQL Server has a date type.

I'd also encourage you to watch my Pluralsight course, Date and Time Fundamentals - which covers many of these issues.

Regarding using NodaTime.Serialization.JsonNet in WebAPI (so you can use LocalDate directly), in your WebApiConfig.cs file, wire it up like so:

config.Formatters.JsonFormatter.SerializerSettings
                 .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);

Then it should just work.