I'm writing this to gather comments on our approaches and hopefully help someone else (and my memory).
Scenario
- All of our databases use
DateTime
data types with no time zone information. - Internally we know that all of the dates/times in our databases are in local (New Zealand) time, not UTC. For a web application this is not ideal but we don't control the design of all of these databases as they support other systems (accounting, payroll, etc).
- We are using Entity Framework (model first) for data access.
Our problem
- Without specific time zone information the Breeze / Web Api / Entity Framework stack seems to favour the assumption that times are UTC, not local, which is probably for the best but doesn't suit our application(s).
- Breeze likes to pass dates back to the server in standard UTC format, particularly in query strings (eg
where
clauses). Imagine a Breeze controller that directly exposes a table from the database as an IQueryable. The Breeze client will pass any date filter (where) clauses to the server in UTC format. Entity Framework will faithfully use those dates to create a SQL query, completely unaware that the database table dates are in our local time zone. For us that means that the results are somewhere between 12 to 13 hours offset from the ones we want (depending on daylight savings).
Our objective is to ensure that our server side code (and the database) consistently uses dates in our local time zone, and that all queries return the desired results.
Best Answer
Our solution part 1: Entity Framework
When Entity Framework gets
DateTime
values from the database it sets them toDateTimeKind.Unspecified
. In other words, neither local or UTC. We specifically wanted to mark our dates asDateTimeKind.Local
.To achieve this we decided to tweak Entity Framework's template that generates the entity classes. Instead of our dates being a simple property, we introduced a backing-store date and used a property setter to make the date
Local
if it wasUnspecified
.In the template (.tt file) we replaced...
... with ...
That creates a rather ugly one-line setter but it gets the job done. It does use a helper function to Default the date to a
Local
which looks like this:Our solution part 2: IQueryable filters
The next problem was Breeze passing UTC dates when applying
where
clauses to ourIQueryable
controller actions. After reviewing the code for Breeze, Web API and Entity Framework, we decided the best option was to intercept calls to our controller actions and swap out the UTC dates in theQueryString
with local dates.We chose to do this using a custom attribute that we could apply to our controller actions such as:
The class that implemented this attribute is:
Our solution part 3: Json
This might be more controversial but our web app audience are entirely local too :).
We wanted Json sent to the client to contain dates/times in our local timezone by default. Also we wanted any dates in Json received from the client to be converted to our local timezone. To do this we created a custom
JsonLocalDateTimeConverter
and swapped out the Json converter Breeze installs.The converter looks like this:
And finally to get the above converter installed we created a
CustomBreezeConfig
class:That's about it. All comments and suggestions are welcome.