BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage News .NET 6: Date and Time Structures

.NET 6: Date and Time Structures

This item in japanese

Bookmarks

A long-standing problem with .NET’s Base Class Library is the inability to separately represent date and time values. As part of .NET 6, the new DateOnly and TimeOnly classes seek to correct this oversight.

Since the 90’s, Windows programmers have been dealing with a less than optimal story around date and time values. In Visual Basic there was just the Date class, which was used for dates, times, date+time, and duration values. This often led to problems such as values that were only meant to have a date component also picking up a time component or vise-versa.

With .NET 1 (VB 7), the Date structure was renamed to DateTime. A better name, but it still carried the same problem of needing to represent both date-only and date+time values. This was especially problematic when performing time zone conversions. A date-only value stored in a DateTime structure could easily pick up a 1 am or 11 pm time component. In the latter case, the one-hour offset would change the date component to the previous day.

Also in .NET 1 was the introduction of the TimeSpan structure. This was designed to store time durations, but was often leveraged as a makeshift time-only structure. Again, there were problems with this technique. For example, adding 3 hours to 10 pm results in “1 day, 2 hours” rather than the expected 1 am. Often this goes unnoticed until the value is inserted into a time-only column in the database, resulting in an overflow error.

In .NET 2, the DateTimeOffset structure was added to address some time zone scenarios by including a field for the offset from UTC. However, the other issues remained unresolved.

DateOnly and TimeOnly

Fast forward to .NET 6 and we get the DateOnly and TimeOnly proposal. As the names imply, these structures are designed to only hold a date or only hold a time. In a rare example of correctly applying the Single Responsibility Principal, they aren’t being asked to serve multiple roles like the previous structures we discussed.

As mentioned before, the original name of the DateTime structure was just Date. This continues in VB, where the Date keyword continues to refer to DateTime. Thus, the name DateOnly was chosen to avoid confusion.

Another reason the name DateOnly was chosen is that DateTime.Date already returns a DateTime value. This can’t be changed, but new programmers would expect it to return a Date. By calling the type DateOnly, the new matching property can be called DateTime.DateOnly.

Similarly, TimeOfDay is problematic as many properties and methods use it to refer to a DateTime or TimeSpan value.

Serialization

DateOnly and TimeOnly will not be implementing the Serializable attribute. In .NET Core and later, this attribute is considered to be deprecated, as are the serialization libraries that rely on it.

There has been a request for the IXmlSerializable attribute and its JSON equivalent. However, DateOnly and TimeOnly are to be placed in one of NET’s lowest level libraries, where such attributes are not available. So serializers will need to include their own custom handling for these new types. And then there are the fully custom serializers built into things like database drivers to consider.

This may result in a period of time after .NET 6 is released where DateOnly and TimeOnly can’t be used in some scenarios until the serializers catch up.

DateTimeOnly

In an attempt to solve the time zone problem, .NET 2 introduced the DateTime.Kind property. This meant to give developers the option to annotate their date+time values with a flag indicating whether the value represents local, UTC, or an unspecified time zone. What really happened is that developers were forced to specify this information or face unexpected bugs.

To make matters worse, the Kind property was inconsistently honored. For example, many serializers will see it and include an offset when exporting the value. This leads to hard-to-diagnose bugs, as only some dates (e.g. coming from DateTime.Now) will be serialized with the offset. But comparison operators ignore Kind entirely. The developer is expected to check the Kind property themselves and perform the time zone conversion if necessary.

In order to eliminate these issues, there was a request for a new DateTimeOnly structure that basically works like DateTime did in .NET 1.

Advanced Scenarios

At this time there are no plans to address advanced scenarios such as actual time zone handling and period vs duration. This role is largely handled by the Noda Time library. Based on Java’s Joda Time, it also allows you to represent partial dates such as “January 3” or “March 2021”, where the year or day or month are intentionally missing.

Rate this Article

Adoption
Style

BT