如何确定生日或纪念日是否在日期范围内发生

27

给定一个生日/纪念日的日期时间,如何确定该日期是否发生在特定的日期范围内?例如,

生日日期 = 2000年1月2日
日期范围 = 2008年12月25日 - 2009年1月3日

我需要一种方法来确定这个人的生日是否发生在该日期范围内,最好是用C#。

我最初尝试将生日日期的年份更改为与日期范围相同的年份,然后只需检查"新"生日日期是否在日期范围的开始和结束日期之间......但当日期范围跨越不同的年份时,就像上面的例子一样,我不得不添加一个复杂的 if 语句。难道没有更好的方法吗?


4
+1,因为这个问题比它看起来要棘手得多! - NibblyPig
3
请不要忘记,考虑到闰年的影响,事情会变得更加棘手。 - Jon Skeet
实际上闰年不会有任何影响 :) - NibblyPig
4
不要忘记,人们只有在出生后才庆祝他们的生日。 :) - João Angelo
13个回答

13

好的,这是我的看法

public static bool IsBirthDayInRange(DateTime birthday, DateTime start, DateTime end)
{
    DateTime temp = birthday.AddYears(start.Year - birthday.Year);

    if (temp < start)
        temp = temp.AddYears(1);

    return birthday <= end && temp >= start && temp <= end;
}

2
我假设你的日期是存储在DateTime变量中的?如果是这样,比较就很简单:
if (Birthday > DateRangeLower && Birthday < DateRangeUpper) {
    // it's your birthday!
}

你可以将其封装在扩展方法中,如果你喜欢的话:
public static bool Between(this DateTime compareDate, DateTime startDate, DateTime endDate) {
    return compareDate > startDate && compareDate < endDate;
}

那么你可以这样调用它:
if (Birthday.Between(DateRangeLower, DateRangeUpper) {
    // it's your birthday
}
更新:如果您想忽略生日的年份部分来确定日期周年纪念日是否在范围内,请使用以下方法:
if (DateRangeLower.DayOfYear <= DateRangeUpper.DayOfYear &&  
    Birthday.DayOfYear > DateRangeLower.DayOfYear && Birthday.DayOfYear < DateRangeUpper.DayOfYear) {
  // it's your birthday
  // the days are within the date range (and the range is in a single year)
}
else if (DateRangeLower.DayOfYear > DateRangeUpper.DayOfYear &&
    Birthday.DayOfYear < DateRangeLower.DayOfYear && Birthday.DayOfYear > DateRangeUpper.DayOfYear) {
  //  it's your birthday
  //  note, we're actually checking to see if the date is outside of the
  //  original date's days to handle the case where the dates span a year end boundary
  //  this only works if the dates are not more than 1 year apart
}

2
我认为你的意思是生日 < DateRangeUpper,可能应该是 >= 和 <=。 - Mongus Pong
我不确定这是否是原帖作者所寻找的。他的情况涉及将生日值移入范围(基于年份)以查看其是否落在范围内或外部。 - LBushkin
糟糕.. @Mongus 发现了一个问题。如果需要的话,@davekaro 可以通过更改为 >= 和 <=(可能使用 .Date 忽略任何时间部分)来使比较日期包含在内。 - Dexter
@LBushkin... 如果OP没有使用DateTime变量来设置上下限,我建议他先将变量解析为DateTimes,然后再使用这种方法。也许他正在寻找更复杂的东西! - Dexter
1
然而,你不能如此轻易地忽略年份组件。这个解决方案无法告诉你一个出生于2000年1月25日的人是否在2008年1月和2月之间过生日,这正是他所要求的。 - NibblyPig
显示剩余2条评论

2

更新答案,加入了SLC提到的上限规范化。这对于那些非2月29日出生的人应该有效。

DateTime birthday = new DateTime(2000, 2, 1);

DateTime min = new DateTime(2008, 12, 25);
DateTime max = new DateTime(2009, 3, 1);

DateTime nLower = new DateTime(min.Year, birthday.Month, birthday.Day);
DateTime nUpper = new DateTime(max.Year, birthday.Month, birthday.Day);

if (birthday.Year <= max.Year && 
    ((nLower >= min && nLower <= max) || (nUpper >= min && nUpper <= max)))
{
    // Happy birthday
    Console.WriteLine("Happy birthday");
}

现在有一个版本可以处理在29/02出生的人:

public static bool IsBirthdayInRange(
    DateTime birthday, DateTime min, DateTime max)
{
    var dates = new  DateTime[] { birthday, min };
    for (int i = 0; i < dates.Length; i++)
    {
        if (dates[i].Month == 2 && dates[i].Day == 29)
        {
            dates[i] = dates[i].AddDays(-1);
        }
    }

    birthday = dates[0];
    min = dates[1];

    DateTime nLower = new DateTime(min.Year, birthday.Month, birthday.Day);
    DateTime nUpper = new DateTime(max.Year, birthday.Month, birthday.Day);

    if (birthday.Year <= max.Year &&
        ((nLower >= min && nLower <= max) || (nUpper >= min && nUpper <= max)))
    {
        return true;
    }

    return false;
}

我回来发布这个解决方案,这是做法,基本上将输入的年份设置为搜索下限的年份。你可能还需要为上限设置年份,例如在上述情况下,如果生日是1983年10月5日,范围是1985年10月6日至1986年10月10日,则IF语句将在规范化的>= min部分失败。 - NibblyPig
在闰年中会发生什么?例如,当 birthday29/02/1984min01/10/2005 时会怎样? - LukeH
我认为这种方法不适用于我原问题中提供的日期。使用我的日期,规范化后的日期将是2008年1月2日,而这不大于“最小值”(2008年12月25日)。 - davekaro
@Luke,闰年可能会引发异常。 - João Angelo

1

这是我的解决方案。它使用 DayOfYear 来查找匹配项。但是你必须注意,如果开始日期的 DayOfYear 已经超过了结束日期的 DayOfYear,那么就需要特殊处理了。我假设开始日期早于结束日期:

private static bool HasBirthDay( DateTime birthday, DateTime start, DateTime end )
{
    Debug.Assert( start < end );
    if( start.DayOfYear < end.DayOfYear )
    {
        if( birthday.DayOfYear > start.DayOfYear && birthday.DayOfYear < end.DayOfYear )
        {
            return true;
        }
    }
    else
    {
        if( birthday.DayOfYear < end.DayOfYear || birthday.DayOfYear > start.DayOfYear )
        {
            return true;
        }
    }
    return false;
}

// DayOfYear(start date) > DayOfYear(end date)
var start = new DateTime( 2008, 12, 25 );
var end = new DateTime( 2009, 1, 3 );
Debug.Assert( HasBirthDay( new DateTime( 2000, 1, 2 ), start, end ) );
Debug.Assert( HasBirthDay( new DateTime( 2000, 12, 26), start, end ) );
Debug.Assert( !HasBirthDay( new DateTime( 2000, 1, 5 ), start, end ) );
Debug.Assert( !HasBirthDay( new DateTime( 2000, 12, 24 ), start, end ) );

// DayOfYear(start date) < DayOfYear(end date)
start = new DateTime( 2008, 10, 25 );
end = new DateTime( 2008, 11, 3 );
Debug.Assert( HasBirthDay( new DateTime( 2000, 10, 26 ), start, end ) );
Debug.Assert( !HasBirthDay( new DateTime( 2000, 12, 5 ), start, end ) );
Debug.Assert( !HasBirthDay( new DateTime( 2000, 1, 24 ), start, end ) );

有没有任何理由给我点踩?如果这段代码有 bug,我想知道。请留下评论。 - tanascius

1

你的问题的关键是确定要分配给生日哪一年,以确保您可以执行有效的范围比较。

您需要处理与所需范围相关的两个子情况:

  1. 下限与上限具有相同的年份
  2. 下限与上限具有不同的年份

编辑:没有喝足够的咖啡。请忽略我的先前答案。

您需要根据正在检查的生日的月/日调整日期。

您不能总是使用上限年份,因为生日可能会落在大于上限月份的月份中。一个简单的替代方法是进行两次检查:一次使用上限年份,然后再次使用前一年的年份。这处理了年度边界的情况:

var birthday = DateTime.Parse( "1/2/2000" );
var lowerBound = DateTime.Parse( "12/25/2008" );
var upperBound = DateTime.Parse( "1/3/2009" );

var adjustA = new Birthday( upperBound.Year, birthday.Month, birthday.Day );
var adjustB = adjustA.AddYears( -1 );

var isInBounds = (adjustA >= lowerBound && adjustA <= upperBound) ||
                 (adjustB >= lowerBound && adjustB <= upperBound);

如果这个人出生于2010年,这仍然会显示他在那个范围内有一个生日。我提供了一个可能的解决方案,首先只检查年份,然后将生日标准化以进行比较。 - João Angelo
你的 DateTime 构造函数有误。你的解决方案与 12/26/2000 不匹配。 - tanascius
例如,当 someBirthday29/02/2008upperBoundDate25/03/2009 时会发生什么? - LukeH
@João Angelo:我在那个问题上有点脑抽了。你的观点是正确的,我已经调整了我的解决方案。 - LBushkin

1

我会将所有日期转换为Epoch时间,然后进行直接比较。

我在这里找到了这个转换方法,稍作修改。

int epoch = (int)({Beginning/Ending Date} - new DateTime(1970, 1, 1)).TotalSeconds;

所以你的整个代码集合只需要这样

int StartDateInEpoch = (int)(StartDate - new DateTime(1970, 1, 1)).TotalSeconds;
int EndDateInEpoch = (int)(EndDate - new DateTime(1970, 1, 1)).TotalSeconds;
int TargetDateInEpoch = (int)(TargetDate - new DateTime(1970, 1, 1)).TotalSeconds;

if (StartDateInEpoch < TargetDateInEpoch && TargetDateInEpoch <= EndDateInEpoch)
    return true;

1

另一种答案,将所有日期移动到特定的年份。

public static bool IsBirthDayInRange(DateTime birthday, DateTime start, DateTime end)
{
    // This could be any date...
    var epoch = new DateTime(1970, 1, 1);

    // Start date is always epoch, end date is epoch + range span
    DateTime endDateInEpoch = epoch.AddSeconds((end - start).TotalSeconds);
    // Move the bithday back to epoch.Year
    DateTime birthDayInEpoch = birthday.AddYears(epoch.Year - birthday.Year);

    return birthday <= end && epoch < birthDayInEpoch && birthDayInEpoch <= endDateInEpoch;
}

1
你可以使用 DateTime 对象的 DayOfYear 属性。
if ((birthday.DayOfYear >= start.DayOfYear) && (birthday.DayOfYear <= end.DayOfYear)) {
  ...
}

1
如果范围是01/11/2008 - 01/03/2009,这将会出错。 - Mongus Pong
true - 或许他的情况允许他使用这个,否则看起来他只能回到一个令人讨厌的 if 语句。我认为 tanascius 的想法是正确的。 - Ray
如果被测试的人没有出生在被测试的范围之前,这将失败。请检查我的答案,看是否有可能解决这个问题。 - João Angelo

0
if (Birthday.Month >= DateRangeLower.Month && Birthday.Month <= DateRangeUpper.Month
      && Birthday.Day>= DateRangeLower.Day && Birthday.Day<= DateRangeUpper.Day) {
 //Partytime...
}

等等,我以为这是正确答案,但事实并非如此 - 如果你的日期范围是1月1日到3月3日,而你的生日是2月6日,那么这段代码将无法正常工作。 - NibblyPig
这不是正确的,但接近正确。您需要检查月份,仅当生日月份等于日期范围下限月份或生日月份等于日期范围上限月份时,才需要检查日期。 - Michał Piaskowski

0

将生日设置为2000年,将起始日期设置为2000年,将结束日期设置为2000年。如果结束日期在起始日期之前,则将结束日期设置为2001年。

在完成上述操作后:

if (Birthday > DateRangeLower && Birthday < DateRangeUpper) {
    // it's your birthday!
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接