Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Order the dates by upcoming birthdays in LINQ

Tags:

c#

linq

I want to show upcoming birthdays in grid. I have list of values with random order and I want to get the order the dates from today's date (assume current date is March 1st) using LINQ.

List<DateTime> dtlist = new List<DateTime>();
List 1 value = "25-July-1985"
List 2 value = "31-Dec-1956"       
List 3 value = "21-Feb-1978"
List 4 value = "18-Mar-2005"

Output order should be:

18-Mar
25-July
31-Dec
21-Feb

Note: I am not using any DB over here to get values.

like image 951
dotnetmirror.com Avatar asked Mar 01 '13 14:03

dotnetmirror.com


1 Answers

Note that we cannot just project all dates to a date with the current year, because of (for example) leap days. Making February 29th, 2000 into February 29th, 2013 would be wrong.

Let's first order the dates by only their month and day:

var ordered = from dt in dtlist
              orderby dt.Month, dt.Day
              select dt;

Now we need to find all dates that are before the current date (regardless of year):

private static bool IsBeforeNow(DateTime now, DateTime dateTime)
{
    return dateTime.Month < now.Month
        || (dateTime.Month == now.Month && dateTime.Day < now.Day);
}

My original suggestion was to skip/take the dates we want and concatenate them together:

var now = DateTime.Now;
var afterNow = ordered.SkipWhile(dt => IsBeforeNow(now, dt));
var beforeNow = ordered.TakeWhile(dt => IsBeforeNow(now, dt));

var birthdays = Enumerable.Concat(afterNow, beforeNow);

However, user Rawling correctly pointed out that this code will order your list of dates twice: once when afterNow is evaluated, and once when beforeNow is evaluated. His suggestion to order the dates by IsBeforeNow is even more elegant, because it removes the need to skip/take and concat. The previous code block is no longer necessary and the LINQ query part then becomes:

var now = DateTime.Now;
var birthdays = from dt in dtlist
                orderby IsBeforeNow(now, dt), dt.Month, dt.Day
                select dt;

And birthdays is your result. That has been incorporated in the code below:


The full code:

static void Main(string[] args)
{
    var dtlist = new[]{
        DateTime.Parse("25-July-1985"),
        DateTime.Parse("31-Dec-1956"),
        DateTime.Parse("21-Feb-1978"),
        DateTime.Parse("18-Mar-2005")
    };

    var now = DateTime.Now;
    var birthdays = from dt in dtlist
                    orderby IsBeforeNow(now, dt), dt.Month, dt.Day
                    select dt;

    foreach (var dt in birthdays)
    {
        Console.WriteLine(dt.ToString("dd-MMM"));
    }
    Console.ReadLine();
}

private static bool IsBeforeNow(DateTime now, DateTime dateTime)
{
    return dateTime.Month < now.Month
        || (dateTime.Month == now.Month && dateTime.Day < now.Day);
}

Prints:

18-mrt
25-jul
31-dec
21-feb
like image 72
Daniel A.A. Pelsmaeker Avatar answered Oct 12 '22 01:10

Daniel A.A. Pelsmaeker