Best way to compare Periods in using NodaTime (or alternative)

I have a requirement to have a relative min/max date validation able to be stored in a database to customize an application per customer. I decided that the NodaTime.Period due to it's capability to specify years was the best choice. However, NodaTime.Period does not offer a way to compare itself against another period.

Example data provided for this validation:

  • Minimum Age of 18 years old.
  • Maximum Age o 100 years old.
  • Minimum sale duration of 1 month
  • Maximum sale duration of 3 months
  • Minimum advertising campaign 7 days

(Note: Current requirements are that Year / Month / Day will not be combined in validations)

The validations are:

public Period RelativeMinimum { get; set; }
public Period RelativeMaximum { get; set; }

Given a user entered date (and now):

var now = new LocalDate(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
var userValue = new LocalDate(date.Year, date.Month, date.Day);
var difference = Period.Between(userValue, now);

I have a comparison of:

if(RelativeMinimum != null && difference.IsLessThan(RelativeMinimum))))
{
    response.IsValid = false;
    response.Errors.Add(MinimumErrorMessage);
}

Which is consuming an extensions class:

public static class PeriodExtensions
{
    public static bool IsLessThan(this Period p, Period p2)
    {
        return (p.Years < p2.Years) || (p.Years == p2.Years && p.Months < p2.Months) || (p.Years == p2.Years && p.Months == p2.Months && p.Days < p2.Days);
    }

    public static bool IsGreaterThan(this Period p, Period p2)
    {
        return (p.Years > p2.Years) || (p.Years == p2.Years && p.Months > p2.Months) || (p.Years == p2.Years && p.Months == p2.Months && p.Days > p2.Days);
    }
}

While this approach works, given the test conditions I have, I have to wonder why @jon-skeet didn't implement this, and immediately have to worry over what am I missing and what alternative should I be using instead?

Jon Skeet
people
quotationmark

Just as an additional point to Matt's already-excellent answer, we provide an option for creating an IComparer<Period> with a specific anchor point, e.g.

var febComparer = Period.CreateComparer(new LocalDate(2015, 2, 1).AtMidnight());
var marchComparer = Period.CreateComparer(new LocalDate(2015, 3, 1).AtMidnight());
var oneMonth = Period.FromMonths(1);
var twentyNineDays = Period.FromDays(29);

// -1: Feb 1st + 1 month is earlier than Feb 1st + 29 days
Console.WriteLine(febComparer.Compare(oneMonth, twentyNineDays));
// 1: March 1st + 1 month is later than March 1st + 29 days
Console.WriteLine(marchComparer.Compare(oneMonth, twentyNineDays));

people

See more on this question at Stackoverflow