sebnilsson.com | Liquid Development Is What I Do
Seb Nilsson

C# DateTime to RFC3339/ISO 8601

According to Wikipedia the date and time-format RFC3339/ISO 8601 usages includes:

On the Internet, the World Wide Web Consortium (W3C) uses ISO 8601 in defining a profile of the standard that restricts the supported date and time formats to reduce the chance of error and the complexity of software.

My specific need to format a System.DateTime-value in a RFC3339-format came when I used one of Google's REST APIs and they required a parameter in the RFC3339-format. Here is the code is the extension-method which provides the functionality of formatting a DateTime as a string in RFC3339-format:

public static string ToRfc3339String(this DateTime dateTime)
{
    return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo);
}

Convert C# URI/URL to Absolute or Relative

There are many situations when you handle URLs or URIs in applications today, no matter if it's a web-application or not. Often you need to handle absolute URLs, like https://testsite.com/section/page, and relative URLs, like /section/page.html.

To easily convert URLs between absolute and relative, or just ensure these two formats, I created extension-methods for the type System.Uri, which allows you to write code like this:

var absoluteToRelative = new Uri("https://www.test-relative.com/customers/details.html").ToRelative();
// Outputs: "/customers/details.html"

var relativeToAbsolute = new Uri("/orders/id-123/", UriKind.Relative).ToAbsolute("https://www.test-absolute.com");
// Outputs: "https://www.test-absolute.com/orders/id-123/"

The extension-methods which makes this possible are the following:

public static string ToRelative(this Uri uri)
{
    // Null-check

    return uri.IsAbsoluteUri ? uri.PathAndQuery : uri.OriginalString;
}

public static string ToAbsolute(this Uri uri, string baseUrl)
{
    // Null-checks

    var baseUri = new Uri(baseUrl);

    return uri.ToAbsolute(baseUri);
}

public static string ToAbsolute(this Uri uri, Uri baseUri)
{
    // Null-checks

    var relative = uri.ToRelative();

    if (Uri.TryCreate(baseUri, relative, out var absolute))
    {
        return absolute.ToString();
    }

    return uri.IsAbsoluteUri ? uri.ToString() : null;
}

Display Local DateTime with Moment.js in ASP.NET

Moment.js

Displaying a DateTime in local format in C# is relatively easy, but it will only use the server's settings to tell what "local" is.

For example, you might want 2016-03-07 14:35 UTC to show as 2016-03-07 15:35 for a visitor from a CET-timezone.

If you want to dynamically show the local date and time you can use the web-client's information through JavaScript and format it with Moment.js, for any user, anywhere in the world.

To do this in a way that is fault-tolerant and also SEO-friendly I want the UTC-DateTime to be hard-coded in the HTML and let Moment.js format it on the fly, when the page loads. To do this I need to populate my .cshtml-file with the following:

<span class="local-datetime"
        title="@(Model.DateUtc.ToString("yyyy-MM-dd HH:mm")) UTC"
        data-utc="@(Model.DateUtc.GetEpochTicks())">
    @(Model.DateUtc.ToString("yyyy-MM-dd HH:mm")) UTC
</span>

Make sure you run .ToUniversalTime() on your DateTime first.

Notice the .GetEpochTicks()-extension method. It makes sure the format of the DateTime is passed in a format that Moment.js can handle easily. The implementation looks like this:

private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

public static double GetEpochTicks(this DateTime dateTime)
{
    return dateTime.Subtract(Epoch).TotalMilliseconds;
}

The last step is to tell Moment.js to format our DateTime to a local format:

$('.local-datetime').each(function() {
    var $this = $(this), utcDate = parseInt($this.attr('data-utc'), 10) || 0;

    if (!utcDate) {
        return;
    }

    var local = moment.utc(utcDate).local();
    var formattedDate = local.format('YYYY-MM-DD HH:mm');
    $this.text(formattedDate);
});

If this (or other, unrelated) JavaScript-code would fail for any reason the UTC-DateTime is the actually HTML-content and will still be displayed.

Serialize HtmlString & MvcHtmlString in JSON.NET

JSON

The HtmlString-class (and MvcHtmlString) that is and has been used in the ASP.NET-platform, including WebPages, since the introduction of ASP.NET MVC is basically just a wrapped string, that doesn't gets automatically HTML-encoded when used in Razor-views. Despite this fact, if you want to serialize or deserialize this object in JSON.NET it will come back as null.

To be able to serialize an object containing a property of a type inheriting from IHtmlString, like HtmlString and MvcHtmlString, to then, for instance, cache the serialized object, you need to implement your own Newtonsoft.Json.JsonConverter that handles the serialization and deserialization.

HtmlStringConverter : Newtonsoft.Json.JsonConverter

public override bool CanConvert(Type objectType)
{
    return typeof(IHtmlString).IsAssignableFrom(objectType);
}

public override object ReadJson(
    JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var value = reader.Value as string;
    // Specifically MvcHtmlString
    if (objectType == typeof(MvcHtmlString))
    {
        return new MvcHtmlString(value);
    }
    // Generally HtmlString
    if (objectType == typeof(HtmlString))
    {
        return new HtmlString(value);
    }

    // Fallback for other (future?) implementations of IHtmlString
    return Activator.CreateInstance(objectType, value);
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    var htmlString = value as HtmlString;
    if (htmlString == null)
    {
        return;
    }

    writer.WriteValue(htmlString.ToString());
}

You then have to register this Converter in your JsonSerializerSettings like this:

CurrentConverter.Converters.Add(new HtmlStringConverter());