5

我想知道是否有更好/更简单的方法来按生日加载用户。

我有一个用户实例,它具有属性,包括这个:

public DateTime? BirthDate { get; set; }

我想要做的是加载从一个日期到另一个日期的用户:

public IEnumerable<User> LoadUsersByBirthday(DateTime from, DateTime into)

我不关心生日年份,例如,如果我有 3 个生日用户:

  1. 日期时间(1991, 3, 20);
  2. 日期时间(1990, 4, 25);
  3. 日期时间(1989, 3, 10);

LoadUsersByBirthday(new DateTime(2990, 3, 1), new DateTime(3013, 4, 15))应该返回 2 个用户 - 第一个和第三个。

我的方法如下所示:

public IEnumerable<User> LoadUsersByBirthday(DateTime from, DateTime into)
    {
        var days = DateTime.DaysInMonth(from.Year, from.Month);

        var u1 = _unit.User.Load(u => ((DateTime) (u.BirthDate)).Month == from.Month
                                      && ((DateTime) (u.BirthDate)).Day >= from.Day
                                      && ((DateTime) (u.BirthDate)).Day <= days);

        var u2 = _unit.User.Load(u => ((DateTime) (u.BirthDate)).Month > from.Month
                                      && ((DateTime) (u.BirthDate)).Month < into.Month);

        var u3 = _unit.User.Load(u => ((DateTime) (u.BirthDate)).Month == into.Month 
                                      && ((DateTime) (u.BirthDate)).Day <= into.Day);

        return u1.Concat(u2).Concat(u3);
    }

它有效,但有没有更好/更简单的方法来做到这一点?

4

4 回答 4

3

由于闰年和人们的生日实际上是闰日,这比预期更难解决。

最后,我记得我已经有一种方法可以准确地计算某人的年龄,包括闰年和闰年的生日。

给定这样的方法,确定某人是否有生日并给出一个日期范围是微不足道的:

首先,在范围开始前一天计算他们的年龄。接下来,计算他们在范围末端的年龄。

如果计算出的年龄不同,那么他们的生日一定在指定范围内。

把它们放在一起:

public IEnumerable<User> UsersByBirthday(IEnumerable<User> users, DateTime from, DateTime to)
{
    if (to < from)
        throw new ArgumentException("'from' must be at or before 'to'.");

    return users.Where(user => IsBirthdayInRange(user.BirthDate, from, to));
}

public bool IsBirthdayInRange(DateTime birthday, DateTime from, DateTime to)
{
    if (to < from)
        throw new ArgumentException("'from' must be at or before 'to'.");

    if (birthday > from)
        throw new ArgumentException("'from' must be after 'birthday'");

    return AgeInYears(birthday, from.AddDays(-1)) < AgeInYears(birthday, to);
}

/// <summary>Returns a person's age in years, accounting for leap years.</summary>

public static int AgeInYears(DateTime birthday, DateTime today)
{
    // See here for why this works:
    // http://social.msdn.microsoft.com/Forums/en-US/ba4a98af-aab3-4c59-bdee-611334e502f2/calculate-age-with-a-single-line-of-code-c

    return ((today.Year - birthday.Year) * 372 + (today.Month - birthday.Month) * 31 + (today.Day - birthday.Day)) / 372;
}

这是闰日生日的小测试:

DateTime test = new DateTime(2004, 2, 29); // Leap day.

var start = new DateTime(2005, 3, 1);
var end = new DateTime(2005, 6, 7);

Console.WriteLine(IsBirthdayInRange(test, start, end));

使用你的_unit.User.Load()

public IEnumerable<User> LoadUsersByBirthday(DateTime from, DateTime into)
{
    return _unit.User.Load(u => IsBirthdayInRange(u.Birthdate, from, into));
}

[编辑]

在非闰年处理闰日生日有两种可能性:将其视为 2 月 28 日,或将其视为 3 月 1 日。

上面的代码将其视为 3 月 1 日(根据英国法规)。如果你想参数化处理,你可以添加一个leapDayMapsToFeb28参数IsBirthdayInRange()如下:

public bool IsBirthdayInRange(DateTime birthday, DateTime from, DateTime to, bool leapDayMapsToFeb28)
{
    if (to < from)
        throw new ArgumentException("'from' must be at or before 'to'.");

    if (birthday > from)
        throw new ArgumentException("'from' must be after 'birthday'");

    if (leapDayMapsToFeb28 && (birthday.Month == 2) && (birthday.Day == 29) && !DateTime.IsLeapYear(from.Year))
        birthday = new DateTime(birthday.Year, birthday.Month, 28);

    return AgeInYears(birthday, from.AddDays(-1)) < AgeInYears(birthday, to);
}
于 2013-08-01T07:56:17.363 回答
2

我认为这应该有效:

public IEnumerable<User> UsersByBirthday(IEnumerable<User> users, DateTime from, DateTime to)
{
    var fromNormalized = NormalizeDate(from);
    var intoNormalized = NormalizeDate(into);

    return users.Where(u => fromNormalized <= NormalizeDate(u.BirthDate) && NormalizeDate(u.BirthDate) <= intoNormalized);
}

public int NormalizeDate(DateTime date)
{
    return date.Month * 100 + date.Day;
}

NormalizeDate()方法返回一个整数值,表示易于比较的月份和日期。9 月 3 日返回 903,4 月 24 日返回 424。

if 没有错误处理from > to

于 2013-08-01T08:47:00.487 回答
1

假设第一天是生日1(y1,m1,d1),第二天是生日2(y2,m2,d2),那么你可以这样使用:

diff = 31 * (m2 - m1) + (d2 - d1)

因为-31 < d2 - d1 < 31, 所以如果m2 - m1 > 0, 那么无论 d2 或 d1 是什么, 结果都是 diff > 0; 当m2 - m1 < 0, diff < 0;时也一样 当m2 == m1, diff 的值范围只依赖于 d2 和 d1。事实上,31 可以替换为任何大于 31 的数字,例如 32。

于 2013-08-01T08:14:58.773 回答
1

您可以将 DateTime 转换为 Ticks 并进行比较。

类似该代码的东西(附近没有 VS,所以可能是语法错误)

   DateTime from=new DateTime(...); 
   DateTime into=new DateTime(...); 

   IList<User> allUsers=...

固定的:

IList<User> foundedUsers=allUsers.Where(user=> (new DateTime(1900, user.Birthday.Month,  user.Birthday.Day)>new DateTime(1900,from.Month,from,Day)) && ...).ToList();
于 2013-08-01T07:56:50.800 回答