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:
(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?
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));
See more on this question at Stackoverflow