Skip to main content

2 posts tagged with "date"

View All Tags

A folk story wherein we shall find dates, DataAnnotations & data impedance mismatch

If you ever take a step back from what you're doing it can sometimes seem pretty abstract. Here's an example. I was looking at an issue in an app that I was supporting. The problem concerned a field which was to store a date value. Let's call it, for the sake of argument, valuation_date. (Clearly in reality the field name was entirely different... Probably.) This field was supposed to represent a specific date, like June 15th 2012 or 19th August 2014. To be clear, a date and *not* in any way, a time.

valuation_date was stored in a SQL database as a <a href="http://msdn.microsoft.com/en-gb/library/ms187819.aspx">datetime</a>. That's right a date with a time portion. I've encountered this sort of scenario many times on systems I've inherited. Although there is a <a href="http://msdn.microsoft.com/en-gb/library/bb630352.aspx">date</a> type in SQL it's pretty rarely used. I think it only shipped in SQL Server with 2008 which may go some way to explaining this. Anyway, I digress...

valuation_date was read into a field in a C# application called ValuationDate which was of type <a href="http://msdn.microsoft.com/en-us/library/system.datetime.aspx">DateTime</a>. As the name suggests this is also a date with a time portion. After a travelling through various layers of application this ended up being serialized as JSON and sent across the wire where it became a JavaScript variable by the name of valuationDate which had the type <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date">Date</a>. Despite the deceptive name this is also, you guessed it, a date with a time portion. (Fine naming work there JavaScript!)

You can probably guess where I'm going with this... Despite our (cough) rock solid naming convention, the situation had arisen where actual datetimes had snuck in. That's right, in the wilds of production, records with valuation_dates with time components had been spotted. My mission was to hunt them, kill them and stop them reproducing...

A Primitive Problem#

Dates is a sticky topic in many languages. As I mentioned, SQL Server has a <a href="http://msdn.microsoft.com/en-gb/library/bb630352.aspx">date</a> data type. C# has <a href="http://msdn.microsoft.com/en-gb/library/system.datetime.aspx">DateTime</a>. If you want to operate on Dates alone then you're best off talking looking at Jon Skeet's NodaTime - though most people start with DateTime and stick with it. (After all, it's native.) As to JavaScript, well primitive-wise there's no alternative to Date - but <a href="http://momentjs.com/">Moment.js</a> may help.

My point is that it is a long standing issue in the development world. We represent data in types that aren't entirely meant for the purpose that they are used. It's not just restricted to dates, numbers have a comparable story around the issue of decimals and doubles. As a result of data type issues, developers experience problems. Like the one I was facing.

An Attribute Solution#

The source of the problem turned out to be the string JavaScript <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date">Date constructor</a> in an earlier version of Internet Explorer. The fix was switching away from using the JavaScript Date constructor in favour of using Moment.js's more dependable ability to parse strings into dates. Happy days we're working once more! Some quick work to put together a SQL script to fix up the data and we have ourselves our patch!

But we didn't want to get bitten again. We wanted ourselves a little belts and braces. What do do? Hang on a minute, lads – I've got a great idea... It's <a href="http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.validationattribute(v=vs.110).aspx">ValidationAttribute</a> time!

We whipped ourselves up an attribute that looked like this:

using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
namespace My.Attributes
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public class DateOnlyAttribute: ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
if (value is DateTime)
{
// Date but not Time check
var date = (DateTime) value;
if (date.TimeOfDay != TimeSpan.Zero)
{
return new ValidationResult(date.ToString("O", CultureInfo.InvariantCulture) + " is not a date - it is a date with a time", new[] { validationContext.MemberName });
}
}
else
{
return new ValidationResult("DateOnlyAttribute can only be used on DateTime? and DateTime", new[] { validationContext.MemberName });
}
}
return ValidationResult.Success;
}
}
}

This attribute does 2 things:

  1. Most importantly it fails validation for any DateTime or DateTime? that includes a time portion. It only allows through DateTimes where the clock strikes midnight. It's optimised for Cinderella.
  2. It fails validation if the attribute is applied to any property which is not a DateTime or DateTime?.

You can decorate DateTime or DateTime? properties on your model with this attribute like so:

namespace My.Models
{
public class ImAModelYouKnowWhatIMean
{
public int Id { get; set; }
[DateOnlyAttribute]
public DateTime ValuationDate { get; set; }
// Other properties...
}
}

And if you're using MVC (or anything that makes use of the validation data annotations) then you'll now find that you are nicely protected from DateTimes masquerading as dates. Should they show up you'll find that ModelState.IsValid is false and you can kick them to the curb with alacrity!

Beg, Steal or Borrow a Decent JavaScript DateTime Converter

I've so named this blog post because it shamelessly borrows from the fine work of others: Sebastian Markbåge and Nathan Vonnahme. Sebastian wrote a blog post documenting a good solution to the ASP.NET JavaScriptSerializer DateTime problem at the tail end of last year. However, his solution didn't get me 100% of the way there when I tried to use it because of a need to support IE 8 which lead me to use Nathan Vonnahme's ISO 8601 JavaScript Date parser. I thought it was worth documenting this, hence this post, but just so I'm clear; the hard work here was done by Sebastian Markbåge and Nathan Vonnahme and not me. Consider me just a curator in this case. The original blog posts that I am drawing upon can be found here: 1. http://blog.calyptus.eu/seb/2011/12/custom-datetime-json-serialization/ and here: 2. http://n8v.enteuxis.org/2010/12/parsing-iso-8601-dates-in-javascript/

DateTime, JSON, JavaScript Dates....#

Like many, I've long been frustrated with the quirky DateTime serialisation employed by the System.Web.Script.Serialization.JavaScriptSerializer class. When serialising DateTimes so they can be JSON.parsed on the client, this serialiser uses the following approach: (from MSDN) Date object, represented in JSON as "\/Date(number of ticks)\/". The number of ticks is a positive or negative long value that indicates the number of ticks (milliseconds) that have elapsed since midnight 01 January, 1970 UTC." Now this is not particularly helpful in my opinion because it's not human readable (at least not this human; perhaps Jon Skeet...) When consuming your data from web services / PageMethods using jQuery.ajax you are landed with the extra task of having to convert what were DateTimes on the server from Microsofts string Date format (eg "\/Date(1293840000000)\/") into actual JavaScript Dates. It's also unhelpful because it's divergent from the approach to DateTime / Date serialisation used by a native JSON serialisers:

Just as an aside it's worth emphasising that one of the limitations of JSON is that the JSON.parsing of a JSON.stringified date will *not* return you to a JavaScript Date but rather an ISO 8601 date string which will need to be subsequently converted into a Date. Not JSON's fault - essentially down to the absence of a Date literal within JavaScript. ## Making JavaScriptSerializer behave more JSON'y

Anyway, I didn't think there was anything I could really do about this in an ASP.NET classic / WebForms world because, to my knowledge, it is not possible to swap out the serialiser that is used. JavaScriptSerializer is the only game in town. (Though I am optimistic about the future; given the announcement that I first picked up on Rick Strahl's blog that Json.NET was going to be adopted as the default JSON serializer for ASP.NET Web API; what with Json.NET having out-of-the-box ISO 8601 support. I digress...) Because it can make debugging a much more straightforward process I place a lot of value on being able to read the network traffic that web apps generate. It's much easier to drop into Fiddler / FireBug / Chrome dev tools etc and watch what's happening there and then instead of having to manually process the data separately first so that you can understand it. I think this is nicely aligned with the KISS principle. For that reason I've been generally converting DateTimes to ISO 8601 strings on the server before returning them to the client. A bit of extra overhead but generally worth it for the gains in clarity in my opinion. So I was surprised and delighted when I happened upon Sebastian Markbåge's blog post which provided a DateTime JavaScriptConverter that could be plugged into the JavaScriptSerializer. You can see the code below (or on Sebastian's original post with a good explanation of how it works):

Using this converter meant that a DateTime that previously would have been serialised as "\/Date(1293840000000)\/" would now be serialised as "2011-01-01T00:00:00.0000000Z" instead. This is entirely agreeable because 1. it's entirely clear what a "2011-01-01T00:00:00.0000000Z" style date represents and 2. this is more in line with native browser JSON implementations and &lt;statingTheObvious&gt;consistency is a good thing.&lt;/statingTheObvious&gt;

Getting your web services to use the ISO 8601 DateTime Converter#

Sebastian alluded in his post to a web.config setting that could be used to get web services / pagemethods etc. implementing his custom DateTime serialiser. This is it:

With this in place your web services / page methods will happily be able to serialise / deserialise ISO style date strings to your hearts content. ## What no ISO 8601 date string Date constructor?

As I mentioned earlier, Sebastian's solution didn't get me 100% of the way there. There was still a fly in the ointment in the form of IE 8. Unfortunately IE 8 doesn't have JavaScript Date constructor that takes ISO 8601 date strings. This lead me to using Nathan Vonnahme's ISO 8601 JavaScript Date parser, the code of which is below (or see his original post here):

With this in place I could parse ISO 8601 Dates just like anyone else. Great stuff. parseISO8601Date("2011-01-01T00:00:00.0000000Z") would give me a JavaScript Date of Sat Jan 1 00:00:00 UTC 2011. Obviously in the fullness of time the parseISO8601Date solution should no longer be necessary because EcmaScript 5 specifies an ISO 8601 date string constructor. However, in the interim Nathan's solution is a lifesaver. Thanks again both to Sebastian Markbåge and Nathan Vonnahme who have both generously allowed me use their work as the basis for this post. ## PS And it would have worked if it wasn't for that pesky IE 9...

Subsequent to writing this post I thought I'd check that IE 9 had implemented a JavaScript Date constructor that would process an ISO 8601 date string like this: new Date("2011-01-01T00:00:00.0000000Z"). It hasn't. Take a look:

This is slightly galling as the above code works dandy in Firefox and Chrome. As you can see from the screenshot you can get the JavaScript IE 9 Date constructor to play nice by trimming off the final 4 "0"'s from the string. Frustrating. Obviously we can still use Nathan's solution but it's a shame that we can't use the native support. Based on what I've read here I think it would be possible to amend Sebastians serializer to fall in line with IE 9's pendantry by changing this: ```cs return new CustomString(((DateTime)obj).ToUniversalTime()

.ToString("O")

);

To this: ```cs
return new CustomString(((DateTime)obj).ToUniversalTime()
.ToString(<b>"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffzzz"</b>)
);

I've held off from doing this myself as I rather like Sebastian's idea of being able to use Microsoft's Round-trip ("O", "o") Format Specifier. And it seems perverse that we should have to move away from using Microsoft's Round-trip Format Specifier purely because of (Microsoft's) IE! But it's a possibility to consider and so I put it out there. I would hope that MS will improve their JavaScript Date constructor with IE 10. A missed opportunity if they don't I think. ## PPS Just when you thought is over... IE 9 was right!

Sebastian got in contact after I first published this post and generously pointed out that, contrary to my expectation, IE 9 technically had the correct implementation. According to the EMCAScript standard the Date constructor should not allow more than millisecond precision. In this case, Chrome and Firefox are being less strict - not more correct. On reflection this does rather make sense as the result of a JSON.stringify(new Date()) never results in an ISO date string to the 10 millionths of a second detail. Sebastian has himself stopped using Microsoft's Round-trip ("O", "o") Format Specifier in favour of this format string: ```cs return new CustomString(((DateTime)obj).ToUniversalTime()

.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")

);

This results in date strings that comply perfectly with the ECMAScript spec. I suspect I'll switch to using this also now. Though I'll probably leave the first part of the post intact as I think the background remains interesting. Thanks again Sebastian!