A breakdown of the "Noda Time' date and time library for .NET platforms.
In the ever-expanding realm of globalised software development, effective date and time management is a key cornerstone of success. Yet, many developers consider the temporal domain to be a tricky beast, which is exacerbated by the need to consider times on a global scale. Supporting the many time zones and their daylight savings rules around the world is already quite a colossal task, which is made all the more difficult given these rules are determined by governments worldwide and can change on a whim. Unfortunately, and perhaps perplexingly, the built-in .NET types for date and time management are just not up to this challenge. Luckily, NodaTime has emerged as a stalwart alternative and offers developers a powerful toolkit to conquer the temporal landscape.
NodaTime is an open-source date and time library that was created by Jon Skeet to address the limitations and complexities of the built-in DateTime structure in .NET. Its internals were originally based on Joda-Time for Java, from where it sought its inspiration. Thankfully though, it was designed with an idiomatic .NET API, which makes it feel very natural and easy to use. NodaTime is an open-source date and time library that was created by Jon Skeet to address the limitations and complexities of the built-in DateTime structure in .NET. Its internals were originally based on Joda-Time for Java, from where it sought its inspiration. Thankfully though, it was designed with an idiomatic .NET API, which makes it feel very natural and easy to use.
The built-in .NET type DateTime has a number of shortcomings:
NodaTime offers several advantages over the built-in DateTime type:
NodaTime’s core types can be divided into a few different groups. These are better depicted using the following infographic.
The Instant type serves as a universal reference point, representing a precise moment in time across the globe, independent of any time zone or calendar system. This is akin to capturing the current moment, which happens at exactly the same point in time for everyone in the universe. That makes it a perfect type for storing timestamps and can also refer to an exact moment in the future.
NodaTime excels in providing clarity and explicitness about what a particular date and/or time represents. For example, the LocalDateTime, LocalDate, and LocalTime variants is assumed to be local to its user so there is no option for a time zone or offset. Conversely, times can be accompanied by either a time zone or just a fixed offset. The OffsetDateTime type, along with its date and time only variants, allows for fixed offset specification, which is the closest to the built-in DateTimeOffset.
Alternatively, the ZonedDateTime type pairs a specific time zone with a date and time, offering precision localisation and accounting for daylight savings transitions. Unlike the other types, there are no date and time only variants here because there are several instances where a single date or single time could have two different offsets in the same time zone due to daylight savings transitions.
The easiest way to use time zones with NodaTime is to use the IANA Time Zone database (also known as the tz database) as the time zone data source. This database comes integrated with NodaTime, so it’s good to keep it up to date, but there are also ways to download the data separately and load that into NodaTime. Time zones are identified by area and location, for example Australia/Brisbane or America/Los_Angeles, which is also consistent across many other systems.
Converting from Instant or ZonedDateTime to a LocalDateTime is always unambiguous because they refer to a specific point in time, whereas converting from a LocalDateTime to a ZonedDateTime may be ambiguous in some scenarios. For instance, a single local time may map to two different points in time—such as at the end of daylight savings, where 2am shifts back to 1am, resulting in two occurrences of 1:30am. On the other hand, a single local time may not map to any point in time—when turning the clocks forward at the start of daylight savings, 1am skips to 2am, so 1:30am didn’t occur at all.
These ambiguities are easily managed using NodaTime’s dedicated conversion methods, that allow either a lenient or strict conversion. Lenient conversions prioritise the earlier option for ambiguous times or automatically shift forward for skipped times, while strict conversions will throw an exception in both scenarios.
NodaTime provides a dedicated testing package, which includes test doubles that are significantly more powerful than simply mocking the relevant interfaces. For example, FakeClock implements IClock but rather than just returning the current time, it can return any time you set it to, and the time returned can even automatically advance each time it is used. There is also a fake time zone source, and you can create fake time zones, including the ability to simulate daylight savings transitions. By decoupling unit tests from real time zones, this prevents your tests being at the mercy of a government who could change the time zone at any time.
Installation is as simple as installing the NodaTime NuGet package, and registering the clock and time zone provider in your dependency injection container:
services.AddSingleton<IClock>(SystemClock.Instance); services.AddSingleton(DateTimeZoneProviders.Tzdb);
There are also serialisation packages available with the NodaTime.Serialization.* prefix and current support is available for Json.Net, System.Text.Json, and Protocol Buffers. Configuring is very easy due to the bundled extension methods, for example configuring for JSON is as easy as:
jsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
There are a number of packages available to add support for NodaTime types to databases. One example for Entity Framework Core and SQL Server is the SimplerSoftware.EntityFrameworkCore.SqlServer.NodaTime package, which not only supports the type conversions, but also provides some database functions so common date and time calculations can be performed server-side.
Time is such a critical component of the globalised software development world we live in. With the complexities of time zones and daylight savings ever-present, relying solely on the built-in .NET date and time types can lead to a myriad of confusion and pitfalls. However, with NodaTime in your toolbelt, these challenges become a walk in the park. If you’re not already leveraging NodaTime in your projects, the time to do so is now. There’s simply no reason not to. NodaTime not only covers all the basic scenarios of DateTime, but does so with unparalleled clarity, elegance, precision, and flexibility not often seen in the software world. Embrace NodaTime and discover how a stitch in time management saves nine headaches down the line!