5

今後の誕生日をグリッドで表示したいと思います。ランダムな順序の値のリストがあり、 LINQを使用して今日の日付(現在の日付が3月1日であると想定)からの日付の順序を取得したいと思います。

List<DateTime> dtlist = new List<DateTime>();
リスト1の値="1985年7月25日"
リスト2の値="1956年12月31日"       
リスト3の値="1978年2月21日"
リスト4の値="2005年3月18日"

出力順序は次のようになります。

3月18日
7月25日
12月31日
2月21日

注:ここでは、値を取得するためにDBを使用していません。

4

8 に答える 8

7

(たとえば)飛躍日のために、すべての日付を現在の年の日付に単純に予測することはできないことに注意してください。作るのは間違っFebruary 29th, 2000February 29th, 2013いるでしょう。

まず、月と日だけで日付を並べ替えましょう。

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

次に、(年に関係なく)現在の日付より前のすべての日付を検索する必要があります。

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

私の最初の提案は、必要な日付をスキップ/取得して、それらを連結することでした。

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);

ただし、ユーザーRawlingは、このコードが日付のリストを2回並べ替えることを正しく指摘しました。1回は評価されたとき、もう1回は評価されたときです。日付を並べ替えるという彼の提案は、スキップ/テイクして連結する必要がないため、さらにエレガントです。以前のコードブロックは不要になり、LINQクエリ部分は次のようになります。afterNowbeforeNowIsBeforeNow

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

そしてbirthdays、あなたの結果です。これは、以下のコードに組み込まれています。


完全なコード:

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);
}

プリント:

18-mrt
7月25日
12月31日
2月21日
于 2013-03-01T14:37:17.270 に答える
2
//A mock up value for comparison (as DayOfYear has a leap year issue)
int doy = DateTime.Today.Month*31 + DateTime.Today.Day;

var results = dtlist.OrderBy( a => 
              (a.DateOfBirth.Month * 31 + a.DateOfBirth.Day) +
              (a.DateOfBirth.Month * 31 + a.DateOfBirth.Day > doy ? 0 : 400 ))
             .ToList();
于 2013-03-01T15:45:49.740 に答える
1

このカスタム比較担当者は、次のようにList.Sort機能します。

DateTime now = DateTime.Today;
dtlist.Sort((d1, d2) =>
    {
        if (DateTime.IsLeapYear(d1.Year) && d1.Month == 2 && d1.Day == 29)
            d1 = d1.Date.AddMilliseconds(-1);
        if (DateTime.IsLeapYear(d2.Year) && d2.Month == 2 && d2.Day == 29)
            d2 = d2.Date.AddMilliseconds(-1);
        var dtTrunc1 = new DateTime(now.Year, d1.Month, d1.Day, d1.Hour, d1.Minute, d1.Second, d1.Millisecond);
        var dtTrunc2 = new DateTime(now.Year, d2.Month, d2.Day, d2.Hour, d2.Minute, d2.Second, d2.Millisecond);
        TimeSpan diff1 = dtTrunc1 - now;
        TimeSpan diff2 = dtTrunc2 - now;
        if (diff1.Ticks >= 0 && diff2.Ticks >= 0 || diff1.Ticks < 0 && diff2.Ticks < 0)
            return diff1.Ticks.CompareTo(diff2.Ticks);
        else if (diff1.Ticks < 0 && diff2.Ticks >= 0)
            return int.MaxValue;
        else
            return int.MinValue;
    });

1.デモ(サンプルデータ)

2.デモ(うるう年を含む、動作していることを示すための1000を超えるランダムな日付)

このアプローチでは、新しいリストを作成する必要はなく、元のリストを並べ替えます。また、DateTime変数にそれが含まれている場合は、時間順に並べ替えます。

于 2013-03-01T14:44:02.090 に答える
1

あらゆる範囲とすべてのケースのソリューション:

        static void Main(string[] args)
        {
            List<DateTime> birthdays = new List<DateTime>() {
            new DateTime(1977,1,29),
            new DateTime(1977,1,30),
            new DateTime(1977,1,31)
            };

            var daysFrom = 30;
            var start = new DateTime(DateTime.Now.Year,DateTime.Now.Month,DateTime.Now.Day);
            start = new DateTime(2020, 12, 31); // border test case
            var last = start.AddDays(daysFrom);
            var yearSwitch = last.Year - start.Year;
            var res = birthdays.Where(bday =>
                {
                    var bn = new DateTime(start.Year, bday.Month, bday.Day);
                    if (bday.DayOfYear < daysFrom)
                    {
                        bn = bn.AddYears(yearSwitch);
                    }
                    return bn >= start && bn <= last;
                }
            ).ToList();
            Console.WriteLine("List:{0}", string.Join(",", res));
        }

于 2020-09-25T13:27:02.917 に答える
0

エラーを起こすためのいくつかの方法に関する興味深い問題!これが私の試みです:

  • 誕生日ごとに、次の誕生がいつになるかを計算します-これは、今日以降の最初の誕生日(誕生日に1年を連続して追加して得られる日)として定義されます
  • 次の誕生を昇順で並べ替えます
  • リストを返す

サンプルコード:

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

var today = DateTime.Today;

var nextbirthDays = 
    from birthdate in dtlist
    select 
        Enumerable.Range(0, 1000)
        .Select(birthdate.AddYears)
        .First(birthday => birthday >= today);

var ordered = 
    nextBirthdays
    .OrderBy(d => d)
    .ToList();

これにより、次の誕生日の完全なが返されることに注意してください。これは、必要に応じDateTimeてフォーマットできますdd-MMM。.NET 4.0より前は、

.Select(age => birthdate.AddYears(age))

それ以外の

.Select(birthdate.AddYears)
于 2013-03-01T15:01:34.783 に答える
0

DayOfYear他の人はそれを持っていると述べましたleap year issue。実際、問題は見つかりませんでした。2月29日はうるう年ではなくDayOfYear == 60、3月1日はうるう年で、どちらも正しいです。したがって、フィルタリングには使用できますが、2月29日と3月1日、3月1日と3月2日などが混同されるため、並べ替えには使用できません。ただし、月と日で並べ替えてもかまいません。DayOfYear == 60DayOfYear == 61DayOfYear

var upcomingBirthdays = birthdays.Where(dt => dt.DayOfYear >= DateTime.Today.DayOfYear).OrderBy(dt => dt.Month).ThenBy(dt => dt.Day);

また

var upcomingBirthdays = from birthday in birthdays
                        where birthday.DayOfYear >= DateTime.Today.DayOfYear
                        orderby birthday.Month, birthday.Day
                        select birthday;

誰かの誕生日が2月29日である場合、うるう年ではない3月1日に表示され、うるう年ではその日に表示されます。これは私が誕生日カレンダーでそれを期待する方法です。

アップデート:

私の実装にはまだ1つの問題があります。それは、来年の次の誕生日が表示されないということです。

欠落している誕生日はたまたま元のリストの残りであるため、それらに対して同じことを行ってから、結果を連結することができます。

var thisYear = from birthday in birthdays
               where birthday.DayOfYear >= DateTime.Today.DayOfYear
               orderby birthday.Month, birthday.Day
               select birthday;

var nextYear = from birthday in birthdays
               where birthday.DayOfYear < DateTime.Today.DayOfYear
               orderby birthday.Month, birthday.Day
               select birthday;

var upcomingBirthdays = thisYear.Concat(nextYear);

このように、結果には常に元のリストのすべてのエントリが次の誕生日の順序で含まれます。

于 2013-03-01T17:18:30.863 に答える
-1
var currentYear = DateTime.Today.Year;
var birthDays = dtList.Select(d => Tuple.Create(d, new DateTime(currentYear, d.Month, d.Day)))
    .OrderBy(tuple => tuple.Item2)
    .Where(tuple => tuple.Item2 > DateTime.Today)
    .Select(tuple => tuple.Item1)
    .ToList();            
于 2013-03-01T14:22:11.183 に答える
-1

日付のコレクションをそのまま使用してこれを行うことはできないと思います。秘訣は、生年月日はすべて過去の特定の日付ですが、その日付が来年にいつ繰り返されるかを把握できるようにする必要があるということです。つまり、最初に各誕生日(たとえば、 1985年7月25日)をその月/日の組み合わせの次の発生日(たとえば、 2013年7月25日)に変換する必要があります。そうして初めて、他の回答で言及されているOrderByが機能します。

public IEnumerable<DateTime> UpcomingBirthdays(IEnumerable<DateTime> birthDates)
{
    return birthDates.Select(
        bd => new DateTime( 
            ((bd.Month >= DateTime.Today.Month || (bd.Month == DateTime.Today.Month && bd.Day >= DateTime.Today.Day)) ? DateTime.Today.Year : DateTime.Today.Year + 1),
            bd.Month,
            bd.Day)
        ).OrderBy(bd => bd);
}

このメソッドは、各誕生日が次に発生する時期を決定し、それらの日付を注文します。

于 2013-03-01T14:34:58.533 に答える