View on GitHub

anthonysteele.github.io

Dates: ISO8601 or pain

When sending date-time data between systems, your options fall into two general categories:

  1. ISO8601 with an explicit timezone, or
  2. A world of pain.

A world of pain

Do you want a world of pain? I didn’t think so.

ISO8601

{
  "OrderId": 123456,
  "OrderDue": "2017-04-05T14:30Z"
}

The order is due at 2:30PM UTC on the 5th of April, 2017.

If you can keep the time zone in which something happened, then keep it, it’s valuable information.

Field ordering

ISO8601 is a standard format where all parts of the date or datetime are laid out in a structured way, largest to smallest. It is human readable and can be unambiguously parsed by code.

Largest to smallest is also a logical ordering: When measuring duration, would you say “seven hours, three days and fifteen minutes”? Of course not, the order largest to smallest makes the most logical sense, so you’d say “three days, seven hours and fifteen minutes”.

When measuring distance, would you say “Two feet, three yards and an inch”? Of course not, and not just because you should use the metric system.

Largest to smallest ordering has the pleasing side-effect that an alphabetic sort of these values is also a chronological sort.

Using in .Net code

For .Net code, an ISO8601 Datetime maps most closely to the DateTimeOffset structure. You should stop using DateTime and use DateTimeOffset almost everywhere - The docs say “DateTimeOffset should be considered the default date and time type for application development”.

What does this code output?
Console.WriteLine(new DateTime(1970, 1, 1).ToUniversalTime());
I am in the UK, and I get: 01/01/1970 00:00:00
Someone in Australia gets: 31/12/1969 14:00:00
And someone running .Net Core on Linux in the UK gets 31/12/1969 23:00:00 because that system knows about the permanent summer time of 1968-1971. This code is ambiguous, and your results may vary.

While these details are interesting, I am not suggesting that you cultivate a John-Skeet-like knowledge of the esoteric details of time zones in order to work out how your code will behave in production. I am suggesting that you instead write simple and unambiguous code by using an explicit time zone in a DateTimeOffset. e.g. Console.WriteLine(new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero));.

Read the excellent Wikipedia page, the RFC, or get the specification itself.

There is a lot more to ISO8601, e.g there’s also a syntax for expressing durations. I am not going to define it all here, just convince you that it is the right way.

Other options

And why they suck.

UK or US date format

These are the twins: DD/MM/YYYY and MM/DD/YYYY. You might be used to reading one of them, but they are terrible for data interchange as they look the same but mean different things. Use one of these formats only if you want these problems caused by confusing them:

In short, unless you want a world of pain, avoid these date formats for data interchange and storage.

Epoch seconds

Unix time (also known as POSIX time or Epoch time) is defined as the number of seconds that have elapsed since 00:00:00 UTC, Thursday, 1 January 1970

This format may be fine for system administration uses, or e.g. timestamping log entries. But it is not suitable for recording most business date and time values because:

The common theme is that there are implicit decisions and ways to miscommunicate that the single integer value of the epoch date does not capture.

Summary

Save yourself a whole lot of trouble - insist that data is sent in ISO8601 format with an explicit time zone.