如何在C#中判断一个DateTime是否在DateRange范围内

94

我需要知道一个日期是否在日期范围内。我有三个日期:

// The date range
DateTime startDate;
DateTime endDate;

DateTime dateToCheck;

简单的解决方法是进行比较,但有没有更聪明的方法来做到这一点?


1
比只检查日期是否在这些日期之间更聪明的方法是什么? - Tomas Jansson
5
那么什么比简单的解决方案更聪明呢? - Polyfun
7个回答

157

不,对我来说进行简单的比较看起来不错:

return dateToCheck >= startDate && dateToCheck < endDate;

不过需要考虑以下几点:

  • DateTime 类型在时区方面有些奇怪。它可能是 UTC 时间,也可能是“本地”时间,还可能是模棱两可的时间。确保比较的是同一种类型的时间。
  • 考虑起始点和结束点应该包含还是排除在外。上述代码将其作为包含下限和排除上限的方式处理。

是的。确保您比较的 DateTime 是相同类型(UTC/本地时间)很重要。如果类型不同,则会比较原始时间而不是将两者转换为公共类型。 - CodesInChaos
11
return startDate <= dateToCheck && dateToCheck < endDate 看起来更易读。 - Mauricio Morales
10
@MauricioMorales:这取决于个人;有些人发现左侧保持一致(日期检查位)更容易阅读。这是我倾向于做的。我也能看出“按时间顺序”的方法的优点,但我认为我个人更喜欢我现在的方式。 - Jon Skeet
@MauricioMorales 在决定先写哪一边的比较时,我总是选择被检查的对象,而不是它被比较的值。这对我来说似乎最合理。因此,在这种情况下,dateToCheck是被检查的对象,我会像@JonSkeet那样首先写它(在&&的两侧)。 - Zero3

72
通常我会为这种情况创建Fowler的范围实现。
public interface IRange<T>
{
    T Start { get; }
    T End { get; }
    bool Includes(T value);
    bool Includes(IRange<T> range);
}

public class DateRange : IRange<DateTime>         
{
    public DateRange(DateTime start, DateTime end)
    {
        Start = start;
        End = end;
    }

    public DateTime Start { get; private set; }
    public DateTime End { get; private set; }

    public bool Includes(DateTime value)
    {
        return (Start <= value) && (value <= End);
    }

    public bool Includes(IRange<DateTime> range)
    {
        return (Start <= range.Start) && (range.End <= End);
    }
}

使用方法非常简单:

DateRange range = new DateRange(startDate, endDate);
range.Includes(date)

3
只是一个愚蠢的观察,构造函数需确保起始时间小于结束时间,否则会破坏逻辑...尤其是在Includes(IRange<DateTime> range)方法中。 - Adrian Hum
2
这是一个非常好的解决方案,它甚至可以与LINQ一起使用。就像这样: var valueFromVacationsList = vacationBookingsForThisMonth.FirstOrDefault(s => (s.Id == currentUser.Id) && new DateRange(s.StartDateTime, s.EndDateTime).Includes(itemDate)); - Kadaj

57

您可以使用扩展方法使代码更易读:

public static class DateTimeExtensions
{
    public static bool InRange(this DateTime dateToCheck, DateTime startDate, DateTime endDate)
    {
        return dateToCheck >= startDate && dateToCheck < endDate;
    }
}

现在,您可以编写:

dateToCheck.InRange(startDate, endDate)

34
不错的方法,不过我会把函数名改为“IsInRange()”。 - Andrew

9

您可以使用:

return (dateTocheck >= startDate && dateToCheck <= endDate);

5

我同意。我无法相信.NET没有本地的日期计算库。感谢分享这个库。 - BoundForGlory
我在一个项目中使用了时间段库,为电子标志应该显示图像的时间表创建了计划,这真是省了不少时间。天哪,如果没有时间段库,我无法想象如何完成那个项目。 - PoorInRichfield

3
继 Sergey 的 答案 后,我认为这个更通用的版本更符合 Fowler 的 Range 概念,并解决了该答案的一些问题,例如通过将 T 约束为 IComparable<T> 以在通用类中使用 Includes 方法。它也像扩展其他值类型功能的类型(如 DateTime)一样是不可变的。
public struct Range<T> where T : IComparable<T>
{
    public Range(T start, T end)
    {
        Start = start;
        End = end;
    }

    public T Start { get; }

    public T End { get; }

    public bool Includes(T value) => Start.CompareTo(value) <= 0 && End.CompareTo(value) >= 0;

    public bool Includes(Range<T> range) => Start.CompareTo(range.Start) <= 0 && End.CompareTo(range.End) >= 0;
}

0

如果有人想要它作为验证器

using System;
using System.ComponentModel.DataAnnotations;

namespace GROOT.Data.Validation;

internal class DateRangeAttribute : ValidationAttribute
{
    public string EndDate;
    public string StartDate;

    public override bool IsValid(object value)
    {
        return (DateTime)value >= DateTime.Parse(StartDate) && (DateTime)value <= DateTime.Parse(EndDate);
    }
}

用法

[DateRange(
    StartDate = "01/01/2020",
    EndDate = "01/01/9999",
    ErrorMessage = "Property is outside of range")
    ]

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