我想在网格中显示即将到来的生日。我有随机顺序的值列表,我希望使用LINQ从今天的日期(假设当前日期 3月1日)获取订单。
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"
输出顺序应为:
18-Mar 25-July 31-Dec 21-Feb
注意:我在这里没有使用任何数据库来获取值。
答案 0 :(得分:7)
请注意,由于(例如)闰日,我们无法将所有日期投影到当前年份的日期。将February 29th, 2000
变为February 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正确pointed out此代码将订购您的日期列表两次:评估afterNow
时为一次,beforeNow
时为评估。他建议按IsBeforeNow
订购日期更为优雅,因为它不需要跳过/取走和连接。不再需要先前的代码块,然后LINQ查询部分变为:
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 25-jul 31-dec 21-feb
答案 1 :(得分: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. Demo(您的样本数据)
2. Demo(&gt; 1000个随机日期,证明它有效,包括闰年)
此方法不需要创建新列表,它会对原始列表进行排序。如果DateTime
变量也包含它,它也会按时间排序。
答案 2 :(得分:1)
//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();
答案 3 :(得分: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));
}
答案 4 :(得分:0)
有很多方法可以解决错误!这是我的尝试:
示例代码:
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)
答案 5 :(得分:0)
其他人表示DayOfYear
有一个leap year issue
。实际上我找不到任何问题:2月29日有DayOfYear == 60
而3月1日有DayOfYear == 60
非闰年和DayOfYear == 61
闰年,两者都是正确的。因此,您可以使用DayOfYear
进行过滤,但是您不能将其用于排序,因为这会混淆2月29日与3月1日,3月1日和3月2日等等。但是按月和日排序是可以的:
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日的非闰年出现,它将在闰年的那一天出现。这就是我在生日日历中的期望。
<强>更新强>
我的实施仍然存在一个问题:它不会显示明年即将到来的生日。
由于丢失的生日恰好是原始列表的其余部分,您可以为它们执行相同的操作,然后将结果连接起来:
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);
这样,结果始终按照即将到来的生日顺序包含原始列表的所有条目。
答案 6 :(得分:-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();
答案 7 :(得分:-1)
我认为您不能按原样收集日期。诀窍在于,出生日期是过去的具体日期,但您希望能够确定该日期何时会在来年重现。这意味着您需要首先将每个生日(例如25-七月 - 1985 )转换为该月/日组合的下一次出现(例如25-七月 - 2013 ) 。只有这样,其他答案中提到的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);
}
此方法确定接下来每个生日的时间,然后命令那些日期。