Thursday, 23 October 2014

Date operations with Noda Time

I was looking at a coding exercise recently that consisted of source code that needed refactoring. Embedded in part of the code was a check to see if someone was 21 or over. I thought this was a great chance to use the Noda Time library created by Jon Skeet to create nicely understandable code. Noda Time is described in the following terms:

“Noda Time is an alternative date and time API for .NET. It helps you to think about your data more clearly, and express operations on that data more precisely.” [1]

So, here’s a quick piece of playful code using Noda Time to check a person’s age in years. Firstly, let’s create a service interface.

using System;

namespace AgeService
{
    public interface IAgeService
    {
        bool IsOfAgeAtDate(DateTime dateOfTest, DateTime dateOfBirth, int expectedAgeInYears);
    }
}

Now let’s implement the interface.

using System;
using NodaTime;

namespace AgeService
{
    public class AgeService : IAgeService
    {
        public bool IsOfAgeAtDate(DateTime dateOfTest, DateTime dateOfBirth, int expectedAgeInYears)
        {
            var localDateOfTest = new LocalDate(dateOfTest.Year, dateOfTest.Month, dateOfTest.Day);
            var localDateOfBirth = new LocalDate(dateOfBirth.Year, dateOfBirth.Month, dateOfBirth.Day);

            Period age = Period.Between(localDateOfBirth, localDateOfTest, PeriodUnits.Years);

            return age.Years >= expectedAgeInYears;
        }
    }
}

A LocalDate is the date portion of a Noda Time LocalDateTime that has no concept of the time of day; it's just a date. A Period described as follows:

“A Period is a number of years, months, weeks, days, hours and so on, which can be added to (or subtracted from) a LocalDateTime, LocalDate or LocalTime. The amount of elapsed time represented by a Period isn't fixed: a period of "one month" is effectively longer when added to January 1st than when added to February 1st, because February is always shorter than January.” [2]

Finally here are a couple of simple tests to demonstrate it works.

using System;
using NUnit.Framework;

namespace AgeServiceTests
{
    [TestFixture]
    public class AgeServiceTests
    {
        [Test]
        public void IsOfAgeAtDate_WhenOfCorrectAge_ReturnsTrue()
        {
            // Arrange
            var ageService = new AgeService.AgeService();
            var dateOfTest = new DateTime(2014, 10, 23);
            var dateOfBirth = new DateTime(1993, 10, 23);

            // Act
            var result = ageService.IsOfAgeAtDate(dateOfTest, dateOfBirth, 21);

            // Assert
            Assert.That(result, Is.True);
        }

        [Test]
        public void IsOfAgeAtDate_WhenUnderAge_ReturnsFalse()
        {
            // Arrange
            var ageService = new AgeService.AgeService();
            var dateOfTest = new DateTime(2014, 10, 23);
            var dateOfBirth = new DateTime(1993, 10, 24);

            // Act
            var result = ageService.IsOfAgeAtDate(dateOfTest, dateOfBirth, 21);

            // Assert
            Assert.That(result, Is.False);
        }
    }
}

There’s lots more to Noda Time than that but it’s a start! Happy coding.

References

[1] http://nodatime.org

[2] http://nodatime.org/1.3.x/userguide/core-types.html

Thursday, 23 October 2014