【问题标题】:How can I make my IsItAHoliday function more efficient?如何让我的 IsItAHoliday 功能更高效?
【发布时间】:2024-04-25 09:35:01
【问题描述】:

如果是周末或以下假期之一,我有一个方法应该返回 1:元旦、阵亡将士纪念日、独立日、劳动节、感恩节和圣诞节,否则返回 0。我选择使用 DateTime.DayOfYear 和 DateTime.IsLeapYear 的组合来实现代码,但我觉得这确实效率低下,而且还使得像阵亡将士纪念日(5 月的最后一个星期一!)这样的假期变得更加困难。我如何才能有效地实现我的目标,并且不对每年更改日期的假期进行硬编码?

这是我当前的实现:

    private int IsItAHoliday(DateTime time)
    {
        // Assume it is not a weekend
        int isWeekendOrHoliday = 0;

        // TODO: Memorial Day, Labor Day, Thanksgiving
        // Declare holidays
        int christmasDayNonLeapYearIndex = 359;
        int christmasDayLeapYearIndex = 360;
        int newYearsDayIndex = 1;
        int independenceDayNonLeapYearIndex = 185;
        int independenceDayLeapYearIndex = 186;

        // Find out if time falls on a weekend
        if (time.DayOfWeek == DayOfWeek.Saturday || time.DayOfWeek == DayOfWeek.Sunday)
        {
            isWeekendOrHoliday = 1;
        }
        // Find out if time falls on a holiday
        // Leap year
        if (DateTime.IsLeapYear(time.Year))
        {
            if ((time.DayOfYear == christmasDayLeapYearIndex) || (time.DayOfYear == independenceDayLeapYearIndex) || (time.DayOfYear == newYearsDayIndex))
            {
                isWeekendOrHoliday = 1;
            }
        }
        // Non-leap year
        else if (!DateTime.IsLeapYear(time.Year))
        {
            if ((time.DayOfYear == christmasDayNonLeapYearIndex) || (time.DayOfYear == independenceDayNonLeapYearIndex) || (time.DayOfYear == newYearsDayIndex))
            {
                isWeekendOrHoliday = 1;
            }
        }

        return isWeekendOrHoliday;
    }

【问题讨论】:

    标签: c# function datetime performance


    【解决方案1】:

    常见的方法是定义一组描述假日日期的规则:

    public interface IHoliday
    {
      bool isHoliday(DateTime date);
    }
    

    然后实现不同假期模式的接口。例如,确切的日期假期:

    public class ExactDayHoliday : IHoliday
    {
      public int Day {get; set;}
      public int Month {get; set;}
    
      public bool isHoliday(DateTime date)
      {
        return date.Month == month && date.Day == day;
      }
    }
    

    另一个规则示例:一个月中第 n 周的某一天。

    public class DayOfWeekHoliday : IHoliday
    {
        public int Nth { get; set; }
        public DayOfWeek DayOfWeek { get; set; }
        public int Month { get; set; }
    
        public bool isHoliday(DateTime date)
        {
            return date.Month == Month && date.DayOfWeek == DayOfWeek && (date.Day - 1) / 7 == (Nth - 1);
        }
    } 
    

    为所有假期创建规则列表。例如:

    List<IHoliday> holidays = new List<IHoliday> {new ExactDayHoliday {Month = 12, Day = 25}, new DayOfWeekHoliday {Nth = 1, DayOfWeek = DayOfWeek.Monday, Month = 9}};
    

    最后一步(检查给定日期):

    bool isHoliday = holidays.Any(rule => rule.IsHoliday(date));
    

    【讨论】:

    • 这很有帮助,谢谢。如何将此解决方案扩展到每年不在同一天的假期?
    • 我的解决方案不适用于动态假期。 Dmitriy 的回答很好且可扩展。您应该查看以下动态假期链接,因为规则定义并不容易。 http://www.codeproject.com/Articles/11666/Dynamic-Holiday-Date-Calculator 谢谢。
    • 添加另一个 IHoliday 规则。希望你能理解一个想法
    • 谢谢,我会试试这个
    【解决方案2】:

    如果您要对每个需要应用程序支持的事件进行硬编码,我建议您使用枚举:

        public enum Holidays{
        newYearsDayIndex = 1,
        independenceDayNonLeapYearIndex = 185,
            independenceDayLeapYearIndex = 186,
        christmasDayNonLeapYearIndex = 359,
            christmasDayLeapYearIndex = 360
        }
    

    然后用每个的值作为假期的日期索引和名称来解释为什么这一天是假期

        Enum.GetValues(typeof(Holidays)); --> For values which you will use as days Index
        Enum.GetNames(typeof(Holidays)); --> For Names which you will use to describe the picked date
    

    您也可以考虑使用命名约定将闰假期和非闰假期分别放在单独的枚举中

    【讨论】:

      【解决方案3】:

      您不需要在假期检查 YEAR,因为它们每年都出现在相同的日期,例如 12 月 25 日、1 月 1 日等。

      我快速编译了一些代码:

      public class Holiday
      {
          public int Day {get; private set;}
          public int Month {get; private set;}
          public Holiday(int day, int month)
          {
              this.Day = day;
              this.Month = month;
          }
      
          public bool IsEqual(DateTime dateTime)
          {
              return ((this.Day == dateTime.Day) && (this.Month == dateTime.Month));
          }
      }
      
      public static class DateTimeExtensions
      {
          private static List<Holiday> holidays = new List<Holiday>();
      
          public static void RegisterHoliday(Holiday holiday)
          {
              holidays.Add(holiday);
          }
      
          public static bool IsHoliday(this DateTime dateTime)
          {
              foreach (Holiday holiday in holidays)
              {
                  if (holiday.IsEqual(dateTime)) return true;
              }
              return false;
          }
      
          public static bool IsWeekend(this DateTime dateTime)
          {
              return (dateTime.DayOfWeek == DayOfWeek.Sunday) || (dateTime.DayOfWeek == DayOfWeek.Saturday);
          }
      }
      

      现在使用Register 方法添加假期并使用扩展。 DateTimeExtensions.RegisterHoliday(new Holiday(25, 12));

      DateTime dateTimeToCheck = new DateTime(2013, 12, 25); return dateTimeToCheck.IsHoliday() || dateTimeToCheck.IsWeekend();

      【讨论】: