Search code examples
angularjsdatetimenancy

NancyFX not serializing dates with a trailing Z to indicate UTC/Zulu


We store all of our dates in our database as UTC.

When they are returned to us from the API, they are in the following format

"createdDate":"2014-07-30T18:34:45"

But as you can see, the date doesn't have the trailing Z (which indicates to our Angular app that it's UTC / Zulu). It should look like this

"createdDate":"2014-07-30T18:34:45Z"

I do have the following setting in our Bootstrapper

JsonSettings.ISO8601DateFormat = true;

Where in my config can I ensure that there's a trailing Z for the purpose of UTC parsing?


Solution

  • What version of NancyFx are you using? Because in v0.23.0 or later, the JsonSerializer code has been changed to use the "o" date format instead of the "s" date format, which should give you the trailing Z that you're looking for. (But only on UTC datetimes.)

    This is the commit that made this change. Note how DateTimeKind.Unspecified dates are treated as local; that might be one possible cause of your problem, if you're not explicitly creating your DateTime objects as DateTimeKind.Utc.

    Below is the NancyFx code that serializes DateTime values, as it looks as of v0.23.0 (after that commit). From https://github.com/NancyFx/Nancy/blob/v0.23.0/src/Nancy/Json/JsonSerializer.cs, lines 480-518:

        void WriteValue (StringBuilder output, DateTime value)
        {
            if (this.iso8601DateFormat)
            {
                if (value.Kind == DateTimeKind.Unspecified)
                {
                    // To avoid confusion, treat "Unspecified" datetimes as Local -- just like the WCF datetime format does as well.
                    value = new DateTime(value.Ticks, DateTimeKind.Local);
                }
                StringBuilderExtensions.AppendCount(output, maxJsonLength, string.Concat("\"", value.ToString("o", CultureInfo.InvariantCulture), "\""));
            }
            else
            {
                DateTime time = value.ToUniversalTime();
    
                string suffix = "";
                if (value.Kind != DateTimeKind.Utc)
                {
                    TimeSpan localTZOffset;
                    if (value >= time)
                    {
                        localTZOffset = value - time;
                        suffix = "+";
                    }
                    else
                    {
                        localTZOffset = time - value;
                        suffix = "-";
                    }
                    suffix += localTZOffset.ToString("hhmm");
                }
    
                if (time < MinimumJavaScriptDate)
                    time = MinimumJavaScriptDate;
    
                long ticks = (time.Ticks - InitialJavaScriptDateTicks)/(long)10000;
                StringBuilderExtensions.AppendCount(output, maxJsonLength, "\"\\/Date(" + ticks + suffix + ")\\/\"");
            }
        }
    

    As you can see, requesting ISO 8601 date format will get you the 2014-07-30T18:34:45 format rather than the number of milliseconds since the epoch, but it will assume local times if the value being serialized has a Kind equal to DateTimeKind.Local.

    So I have two suggestions for you: upgrade to v0.23 of NancyFx if you're still on v0.22 or earlier (v0.22 used the "s" date format, which does not include timezone info, for serializing DateTime values). And if the DateTime objects you're serializing aren't explicitly set to DateTimeKind.Utc, then make sure you specify Utc (since the default is Unspecified, which NancyFx treats as equivalent to Local).