views:

32

answers:

1

I'm building an application that is multi-lingual, multi-timezoned and n-tier. All dates are stored in the database in UTC and all model objects are populated with UTC times. However UTC times are never displayed (unless the user happens to have their timezone set at UTC).

This then means that I need to convert time properties to the correct user timezone repetitively. Repetition is always the sign of bad code or a better way so I was trying to work out the best strategy to implement. Although this is effectively presentation logic my thoughts have been varied because it seems as if the model should know the right values for the current user. So far my thoughts are:

  1. Use a static helper class and then call it each time the property of the model is used. This seems prone to error or being forgotten and makes and calculations cumbersome.

  2. Wrap the model object in a viewmodel object. This is equally cumbersome especially when dealing with lists of objects.

  3. Write an extension method for the model which exists only in the presentation layer. This seems cleaner but unintuitive.

  4. Create an interface in the model layer for the conversion. Implement the helper in the presentation layer and give the model layer the implementation. The model then has properties which use the interface to convert the time. This seems like it should be breaking separation of concerns but doesn't appear to. If you had a default converter then you wouldn't have to worry about getting null object exceptions however then the model layer (currently POCO) would need a container for the conversion helper which seems messy.

  5. Create a convert to local timezone method on the model and pass in the current timezone.

I'm interested in opinions on these strategies or any other that I should or could be using in place of these.

Update What I've currently done is to create an ITimeConvertor and an ITimeConvertorFactory within the model layer. I have then created default implementations of these which just return the original date value. Within the model layer I've added localtime properties for each existing UTC property that was originally on the model. Within these properties I use the factory to get a convertor and convert the UTC value each way in the getter and setter. I've had to add a static settings class with in the model layer (which I don't really like) as a place to store the current timeconvertor factory. Within the web app portion I implement the ITimeConvertorFactory and the ITimeConvertor as WebTimeConvertorFactory and WebTimeConvertor. The WebTimeConvertor knows about the session and the current user so can grab the current timezone. The WebTimeConvertorFactory creates WebTimeConvertors. When the application starts (application_onstart in global.asax) I create the factory and pass that to the model layer static settings property. This allows my model layer to be able to convert local times whilst the data layer only knows of the UTC date properties. It also means I can pass localtimes directly into the model and have it accurately converted provided that the consuming app has provided a convertor factory. As the UTC properties are unchanged, they can still be used anywhere within the app. Whilst it seemed like a lot of code, I've found this solution quite clean once implemented as it allows other consumers of the service to implement their time conversion anyway they want (if at all) whilst also keeping the consumption of the model properties reasonably obvious.

I'm still open to better solutions and critique of my current solution.

+2  A: 

I assume it is your model layer that knows about the user's timezone, so it's up to the model layer to convert the time properties. Otherwise you would have to let the presentation layer know about the timezone, and convert each and every time value there.

Converting time values in the model layer would enable using them in the presentation layer without any conversions, so I think that would be clean. You might for example initialize your objects (POCOs) with converted times at the beginning. But beware of re-converting them in the model, forgetting they are already initialized to local times. Also, if the user can edit the time values, you will need to convert them back to UTC times before you save.

Update: After some more thinking, I gather that UTC time is part of the model and localtimes are a view of this model, so the conversion duty belongs more in the presentation layer (at the expense of disagreeing with myself). With the same thought process, having localtime properties together with UTC times is duplication in essence, and the conversion is still in the model layer. To overcome this, you might have a readonly UTCToLocalTimeConverter-type property in the user POCO which is initialized with the timezone (which also eliminates the need for static methods). Then all calls to time properties in the pages would be wrapped in the converter's ConvertToLocalTime method, that is accessible via user. You could put the converter instance directly in Session too, if you'd like.

I don't know if this approach would work for you, but it is inspired by your strategy, and I think rest of the design runs more smoothly thinking this way. Also the conversion to localtimes is still at client's disposal. The downside is having to convert all time values in the client, but that seems to me as a necessary evil in exchange for getting rid of data duplication and static methods.

henginy
I guess that is some of my point, should my model layer know what timezone the user is in? Currently no. My internal debate is whether this is a presentation or model layer decision. A user POCO stores the timezone but the user POCO does not have time attributes other POCOs do. So I'm then tying the display of the values from one POCO to another which wasn't necessary before...
toxaq
I've updated my answer.
henginy
toxaq
Thanks, glad if it helped.
henginy