Ryan Farley talks about comparing date ranges.  In his post is this phrase “First range represented by r1start to r1end and second range represented by r2start to r2end”.  Aha, a code smell!  2 things that are related should have that relationship represented in code.

 

It’s the Range pattern, which Fowler has discussed before.  Here I get to present my attempt at it, using the latest in C# generics technology.

 

    class Range<T>

    {

        public readonly T Start;

        public readonly T End;

 

        public Range(T start, T end)

        {

            this.Start = start;

            this.End = end;

        }

    }

 

Wow, that’s exciting!

 

Now we need some comparison:

 

    static class Range

    {

        public static bool Overlap<T>(Range<T> left, Range<T> right)

            where T : IComparable<T>

        {

            if (left.Start.CompareTo(left.Start) == 0)

            {

                return true;

            }

            else if (left.Start.CompareTo(right.Start) > 0)

            {

                return left.Start.CompareTo(right.End) <= 0;

            }

            else

            {

                return right.Start.CompareTo(left.End) <= 0;

            }

        }

    }

 

The comparison algorithm is identical to Ryan’s, but written differently.  First, because I can’t constrain type parameters to have comparison operators, I have to use IComparable<T>.  Second, I broke out the expression to use if/else.  I find that much easier to read & debug.

 

Note that I put the Overlap method in a new static class, instead of in Range<T>.  That’s so I can write “Range.Overlap(x, y)” instead of “Range<int>.Overlap (x, y)” – the method will infer the type parameter all by itself.

 

 Here are the tests I used.  

 

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(1), new DateTime(2)),

                new Range<DateTime>(new DateTime(1), new DateTime(2))

                ));

 

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(1), new DateTime(3)),

                new Range<DateTime>(new DateTime(2), new DateTime(4))

                ));

 

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(2), new DateTime(4)),

                new Range<DateTime>(new DateTime(1), new DateTime(3))

                ));

 

 

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(3), new DateTime(4)),

                new Range<DateTime>(new DateTime(1), new DateTime(2))

                ));

            Debug.Assert(Range.Overlap(

                new Range<DateTime>(new DateTime(1), new DateTime(2)),

                new Range<DateTime>(new DateTime(3), new DateTime(4))

                ));