22

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.

Amasuriel
  • 2,212
  • 3
  • 18
  • 25
  • I'm currently using DateTimeOffset in both C# and SQL Server. Pondering the same issues, but not sure where I'll wind up. You might find some helpful information at [Daylight saving time and Timezone best practices](http://stackoverflow.com/questions/2532729/daylight-saving-time-and-timezone-best-practices) and [The Death of DateTime?](http://blogs.msdn.com/b/bartd/archive/2009/03/31/the-death-of-datetime.aspx). – HABO Jan 10 '12 at 01:53
  • 2
    JavaScript can do it... You create a date object via `Date.UTC(...)`, and you then invoke `.getTimezoneOffset()` to figure out the local time... – Šime Vidas Jan 10 '12 at 02:12
  • @vidas But then I am still in the business of manually converting every date time – Amasuriel Jan 10 '12 at 03:04
  • Consider using something that will try to prevent you from using naive datetimes. (Ie, date times that do not have a timezone) - Python's datetime is exceptional in this regard, but maybe Nodatime will help. – Arafangion Jan 12 '12 at 02:35
  • I always save in UTC but work with a struct that contains their timezone information (also needed for daylight savings). Since I know the users timezone I can convert it to the expected UTC and use the seconds since 1970 to convert to JavaScript dates. I have based my struct on this:http://stackoverflow.com/questions/246498/creating-a-datetime-in-a-specific-time-zone-in-c-sharp-fx-3-5/246529#246529 – row1 Jan 12 '12 at 07:00

4 Answers4

4

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);
        }
    }
}
epignosisx
  • 6,152
  • 2
  • 30
  • 31
2

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.

Community
  • 1
  • 1
csharptest.net
  • 62,602
  • 11
  • 71
  • 89
  • I'm fine with having a timezone setting...nowhere in my question was a requirement that I necessarily had to programmatically determine the client time zone, though that would certainly be a nice bonus. In terms of formatting datetimes of course I can use uiculture, but I didn't see a way to set timezone info in the same way. Maybe you could provide a link or example of how to automatically convert a display of a date based on timezone and elaborate on the javascript only strategy you mention in your closing remark? – Amasuriel Jan 12 '12 at 18:34
1

You can use something like MomentJS to display dates/times. Helps with formatting and local times.

Adam Heath
  • 4,703
  • 2
  • 35
  • 50
  • I have the same issue with this that I have with Sime Vidas' solution...there is no way to ensure that the consumer of data (the view) will always be formatted like it would if there were a server side annotation...it relies on me being extremely careful to convert the format to and from UTC on both the server and the view. – Amasuriel Jan 10 '12 at 04:52
0

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.

Udayan
  • 45
  • 4
  • Here is the link for module (by cprieto); [link](http://weblogs.asp.net/cprieto/archive/2010/01/03/handling-timezone-information-in-asp-net.aspx) and github link;[link](https://github.com/cprieto/TimeZoneForAspNet/blob/master/TimeZoneForAspNet/UtcInfoModule.cs) – Udayan Jan 13 '12 at 08:12