工作日计算器

Often times in development shops which focus on line-of-business software, the need for determining next and previous working days--that is, days which are neither weekends nor observed holidays--exists. Developers may need to know the next day that a banking institution will process a payment, or the next business day for their company, or the most recent working day that has already passed. None of these are available in libraries of the Framework. However, with a few lines of code we can create our own such library for determining these days.

通常,在专注于业务线软件的开发商店中,存在确定下一个和上一个工作日(即既不是周末也不是法定假日的日子)的需求。 开发人员可能需要知道银行机构将在第二天处理付款,其公司的下一个工作日或已经过去的最近工作日。 这些都没有在框架的库中提供。 但是,通过几行代码,我们可以创建自己的此类库来确定这些日子。

The code samples and the Visual Studio project referenced throughout this article will be written in C#. It is left to the reader as an exercise to convert the code to Visual Basic .NET should they desire to do so. The code is not exotic, so it should be easily converted using one of the online conversion tools such as Telerik's Code Converter. The complete project will be linked at the end of this article.

本文中引用的代码示例和Visual Studio项目将用C#编写。 读者可以根据需要练习将代码转换为Visual Basic .NET。 该代码不是稀奇古怪的,因此应该使用一种在线转换工具(例如Telerik的Code Converter)轻松地对其进行转换 。 完整的项目将在本文结尾处链接。

The two primary elements of determining whether or not an arbitrary day is a working day are determining whether or not we are dealing with a weekend or a holiday. Weekends are easy:  That bit actually is built into the Framework via the DateTime structure's DayOfWeek property. How do we deal with holidays, then? This part we must write ourselves.

确定任意一天是否为工作日的两个主要要素是确定我们是否要处理周末或假日。 周末很容易:实际上是通过DateTime结构的DayOfWeek属性将其内置到Framework中的。 那我们该如何处理假期? 这部分我们必须写自己。

The first step in determining how to calculate holidays would be to determine what holidays do we actually care about? For simplicity, I am going to stick with those dates which are commonly observed within the United States (U.S.):

确定假期的计算的第一步将是确定我们实际上关心的假期是什么? 为简单起见,我将坚持在美国(US)中通常观察到的那些日期:

  • New Year's Day元旦
  • Martin Luther King, Jr. Day小马丁·路德·金
  • President's Day总统日
  • Memorial Day纪念日
  • Independence Day独立日
  • Labor Day劳动节
  • Columbus Day哥伦布日
  • Veteran's Day退伍军人节
  • Thanksgiving Day感恩节
  • Christmas Day圣诞节

A couple of these days, depending on the company's holiday policy, may actually enjoy additional days over the actual observed day. This will be discussed later in the article. A quick search of the Internet will tell us on which day each of the above holidays fall. What we notice is that some holidays always fall on the same numeric day of the same month (e.g. Christmas is always the 25th of December), and some holidays fall on the an ordinal (e.g. Labor Day is the first Monday of September). The former will be easy to craft:  Just create a new instance of a DateTime structure, and pass it the numeric day. For the latter, though, we will need a way of calculating ordinals. First, let us define an enum structure to define what ordinals we care about:

根据公司的假期政策,这几天可能实际上会比实际观察到的日期多出几天。 这将在本文后面讨论。 快速搜索互联网可以告诉我们以上每个假期的具体日期。 我们注意到的是,某些假期总是落在同一个月的同一数字天( DateTime结构的新实例,然后将其传递给数字日期即可。 但是,对于后者,我们将需要一种计算序数的方法。 首先,让我们定义一个枚举结构来定义我们关心的序数:

namespace WorkingDayCalculator.Core.Enums
{public enum Ordinal{// The library uses these values in mathematical calculations. The values// that are set are important and should not be changed unless the developer// is going to modify the math calculations which rely on these values.Last = -1,First = 0,Second = 1,Third = 2,Fourth = 3,Fifth = 4,}
}

We will use this enum in our calculations for holidays like Labor Day. We have the items Last and Fifth defined, but we won't be using them for our calculations. They are included for those individuals who might like to know what the last Friday of January is or what the fifth Monday of September is.

我们将在劳动节等假期的计算中使用此枚举。 我们已经定义了

Next, we will define a couple of utility functions for calculating ordinal dates, making use of the enum above:

接下来,我们将使用上面的枚举定义几个实用程序函数来计算序数日期:

using System;
using WorkingDayCalculator.Core.Enums;namespace WorkingDayCalculator.Core
{public static class DateHelper{public static DateTime GetOrdinalDate(int year, Month month, Ordinal ordinal, DayOfWeek dayOfWeek){DateTime ordinalDate;if (ordinal == Ordinal.Last){DateTime startDate = (new DateTime(year, (int)month + 1, 1)).AddDays(-1);int offsetFromDayOfWeek = GetPastOffsetFromTarget(startDate.DayOfWeek, dayOfWeek);ordinalDate = startDate.AddDays(-1 * offsetFromDayOfWeek);}else{DateTime startDate = new DateTime(year, (int)month, 1);int offsetFromDayOfWeek = GetFutureOffsetFromTarget(startDate.DayOfWeek, dayOfWeek);int weeksToAdd = (int)ordinal * 7;ordinalDate = startDate.AddDays(offsetFromDayOfWeek + weeksToAdd);if (ordinalDate.Month != (int)month){throw new ArgumentException("Not enough days in month to satisfy ordinal requirement.");}}return ordinalDate;}public static int GetFutureOffsetFromTarget(DayOfWeek day, DayOfWeek target){return ((int)(DayOfWeek.Saturday - day) + (int)target + 1) % 7;}public static int GetPastOffsetFromTarget(DayOfWeek day, DayOfWeek target){return ((int)day + (int)DayOfWeek.Saturday - (int)target + 1) % 7;}}
}

The GetOrdinalDate method will take in the different parts of a date as well as an Ordinal value, and it will calculate the date that matches that criteria. It does this by making use of the other two helper methods:  GetFutureOffsetFromTarget and GetPastOffsetFromTarget. What each of these methods do is perform a mathematical calculation of the difference in days between the current day of week (parameter) and the target date we are trying to reach. This difference is then added to the startDate to arrive at the result date. It is important to be aware of the fact that we are relying on the integer values of the DayOfWeek enum within our mathematical calculations. It is unlikely that Microsoft would change the values of the enum members, but we could certainly define our own enum to get around this concern. Also, if the Ordinal value that we passed in is Last, then our start date is figured by beginning with the first day of the next month, and then subtracting one day. Otherwise, we simply start with the first day of the target month.

数值,并将计算与该条件匹配的日期。 它通过使用其他两个辅助方法来做到这一点: DayOfWeek枚举的整数值。 Microsoft不太可能更改枚举成员的值,但是我们可以定义自己的枚举来解决此问题。 另外,如果我们传入的Ordinal值为

Now that we know how to calculate specific numeric holidays, and now that we have a handy utility function for calculating ordinal dates, we can focus on each individual holiday. Searching the Internet, we can find the rules for each holiday. Then it's simply a matter of writing each of those rules in code. Let us look at New Year's Day and Labor Day as examples:

现在我们知道了如何计算特定的数字假日,并且现在有了用于计算序数日期的便捷实用函数,我们可以专注于每个假日。 搜索互联网,我们可以找到每个假期的规则。 然后,只需在代码中编写每个规则即可。 让我们以元旦和劳动节为例:

public static DateTime GetNewYearsDay(int year, HolidayOptions options = null)
{DateTime holiday = new DateTime(year, (int)Holiday.NewYearsDayMonth, 1);holiday = AdjustIfObserved(holiday, options, Holidays.NewYearsDay);return holiday;
}public static DateTime GetLaborDay(int year, HolidayOptions options = null)
{DateTime holiday = DateHelper.GetOrdinalDate(year, Holiday.LaborDayMonth, Ordinal.First, DayOfWeek.Monday);holiday = AdjustIfObserved(holiday, options, Holidays.LaborDay);return holiday;
}

In the above you can see that because New Year's Day is a specific numeric day, we simply pass that day value (i.e. 1) to the DateTime constructor. For Labor Day, however, we need to determine an ordinal value. We pass in the an Ordinal value of First since that is what the rule for Labor Day specifies. The remaining holidays are calculated in a similar fashion following either of these two approaches.

在上面您可以看到,由于元旦是一个特定的数字日,因此我们只需将该日期值(即1)传递给DateTime构造函数即可。 但是,对于劳动节,我们需要确定一个序数值。 我们输入“ 数值,因为这是劳动节的规则所指定的。 按照这两种方法中的任何一种,剩余假期的计算方式相似。

We next need to define methods that will allow us to test whether or not a date falls on a holiday. For the holidays themselves, we can define additional helper methods. Taking New Year's Day as an example:

接下来,我们需要定义允许我们测试日期是否为假期的方法。 对于假期本身,我们可以定义其他帮助程序方法。 以元旦为例:

public static bool IsNewYearsDay(DateTime date, HolidayOptions options = null)
{DateTime holiday = GetNewYearsDay(date.Year, options);return (holiday == date.Date);
}

...we can see that we call the GetNewYearsDay method that we defined above, and we take the date it returns and compare it to the incoming date. If the date is identical, then we are dealing with New Year's Day. We define similar methods for the remaining holidays. We can then define an additional helper method:

...我们可以看到我们调用了上面定义的

public static bool IsHoliday(DateTime date, HolidayOptions options = null)
{HolidayOptions safeOptions = options ?? new HolidayOptions();Holidays holidaysToCheck = safeOptions.HolidaysToCheck;// Month check saves us a call to the function if we know that we're not in//   the correct month anyway.// It's possible that we may have holidays that are checked for more frequently.//   We could rearrange these in order of precedence/frequency, but that's probably//   a micro-optimization if anything.return (date.Month == (int)Holiday.ChristmasDayMonth && holidaysToCheck.HasFlag(Holidays.ChristmasDay) && IsChristmasDay(date, safeOptions)) ||(date.Month == (int)Holiday.ColumbusDayMonth && holidaysToCheck.HasFlag(Holidays.ColumbusDay) && IsColumbusDay(date, safeOptions)) ||(date.Month == (int)Holiday.IndependenceDayMonth && holidaysToCheck.HasFlag(Holidays.IndependenceDay) && IsIndependenceDay(date, safeOptions)) ||(date.Month == (int)Holiday.LaborDayMonth && holidaysToCheck.HasFlag(Holidays.LaborDay) && IsLaborDay(date, safeOptions)) ||(date.Month == (int)Holiday.MartinLutherKingDayMonth && holidaysToCheck.HasFlag(Holidays.MartinLutherKingDay) && IsMartinLutherKingDay(date, safeOptions)) ||(date.Month == (int)Holiday.MemorialDayMonth && holidaysToCheck.HasFlag(Holidays.MemorialDay) && IsMemorialDay(date, safeOptions)) ||(date.Month == (int)Holiday.NewYearsDayMonth && holidaysToCheck.HasFlag(Holidays.NewYearsDay) && IsNewYearsDay(date, safeOptions)) ||(date.Month == (int)Holiday.PresidentsDayMonth && holidaysToCheck.HasFlag(Holidays.PresidentsDay) && IsPresidentsDay(date, safeOptions)) ||(date.Month == (int)Holiday.ThanksgivingDayMonth && holidaysToCheck.HasFlag(Holidays.ThanksgivingDay) && IsThanksgivingDay(date, safeOptions)) ||(date.Month == (int)Holiday.VeteransDayMonth && holidaysToCheck.HasFlag(Holidays.VeteransDay) && IsVeteransDay(date, safeOptions));
}

...that will check the incoming date against each defined holiday. We can order these methods however we like; short-circuiting in C# will ensure that once we find a match we will exit the conditional test. The month check at the start of each condition is a simple test to bypass going into the IsxxxxxDay methods needlessly. All of these helpers will be combined into our working day calculator.

...将对照每个定义的假日检查输入日期。 我们可以根据需要订购这些方法。 C#中的短路将确保一旦找到匹配项,我们将退出条件测试。 每个条件开始时的月份检查都是一项简单的测试,可以绕过不必要地进入

We defined methods to calculate ordinal dates within a month, and we defined methods to calculate individual holidays. Next we will use these methods to define our working day calculators. We know that working days will not be weekends, and we know that working days will not be holidays. Using what we have already defined, we could construct methods as follows:

我们定义了一种方法来计算一个月内的序数日期,并且定义了一种方法来计算各个假期。 接下来,我们将使用这些方法来定义工作日计算器。 我们知道工作日将不会是周末,并且我们知道工作日将不会是假期。 使用我们已经定义的方法,我们可以构建如下的方法:

using System;namespace WorkingDayCalculator.Core
{public static class WorkingDay{public static DateTime GetNextWorkingDay(DateTime date, HolidayOptions options = null){return GetNextWorkingDay(date, Holiday.IsHoliday, options);}public static DateTime GetNextWorkingDay(DateTime date, Func<DateTime, HolidayOptions, bool> holidayCalculator, HolidayOptions options = null){DateTime workingDay = GetWorkingDayUsingOffset(date, 1, holidayCalculator, options);return workingDay;}public static DateTime GetPreviousWorkingDay(DateTime date, HolidayOptions options = null){return GetPreviousWorkingDay(date, Holiday.IsHoliday, options);}public static DateTime GetPreviousWorkingDay(DateTime date, Func<DateTime, HolidayOptions, bool> holidayCalculator, HolidayOptions options = null){DateTime workingDay = GetWorkingDayUsingOffset(date, -1, holidayCalculator, options);return workingDay;}private static DateTime GetWorkingDayUsingOffset(DateTime date, int offset, Func<DateTime, HolidayOptions, bool> holidayCalculator, HolidayOptions options = null){DateTime workingDay = date.AddDays(offset);while (workingDay.DayOfWeek == DayOfWeek.Saturday || workingDay.DayOfWeek == DayOfWeek.Sunday || holidayCalculator(workingDay, options)){workingDay = workingDay.AddDays(offset);}return workingDay;}}
}

We essentially have two methods:  GetNextWorkingDay and GetPreviousWorkingDay. Each of these call the helper method GetWorkingDayUsingOffset which does the "heavy lifting" of calculating the target working day. Within this helper method, we check the value of workingDay to determine if it is a weekend or a holiday. The holiday check is performed by way of a delegate. This delegate allows us, should we desire, to alter the way holidays are determined. This is the reason for the overloads for GetNextWorkingDay and GetPreviousWorkingDay. Each of the overloads also takes a delegate. So while we have defined a class for all of the U.S. holidays, we could define a second class which dealt with the holidays of another country, or perhaps we have two classes where one class is federal holidays and one class is state holidays. The delegate gives us the flexibility to alter how the GetWorkingDayUsingOffset completes its job.

我们实质上有两种方法:

You probably noticed the HolidayOptions parameter on the GetNextWorkingDay and GetPreviousWorkingDay methods. The intent of this class is to store options that can further affect how we calculate holidays.

您可能已经注意到HolidayOptions参数。 此类的目的是存储可能进一步影响我们计算假期的方式的选项。

Some companies will observe holidays which fall on a weekend on either the previous Friday or the subsequent Monday. Depending on how we set the Observance property, holidays will be adjusted to match this property's value. For example, if we set the value to ObservanceType.PostWeekend and we are trying to determine the next working day after December 23, 2016, because Christmas falls on a Sunday, and because we have a value of PostWeekend, then Monday the 26th will be seen as a holiday, resulting in Tuesday the 27th being returned as the next working day.

一些公司会观察到假日,假日是在上一个星期五或下一个星期一的周末。 根据我们如何设置“

Also, the HolidaysToCheck property presents a bit flag whereby we can skip over certain holidays when performing our holiday check. One could define all the known holidays in one class, and then he could simply create a bit mask to eliminate those holidays for which he did not want to check. It probably makes more sense to classify one's holidays by commonality, but the flexibility of having the bit flag is there for those who would prefer that approach.

另外,

I mentioned earlier that some holidays may actually be observed across multiple days. My organization, for example, treats Thanksgiving Day as a holiday as well as the Friday after. To account for this in the code above, one could define an additional holiday named DayAfterThanksgiving, for example. It would work like any other holiday we have defined. This would be the simplest way to handle such days. A more complicated way would be to modify the HolidayOptions class to include a setting that could be evaluated during the holiday calculations. Such an approach, however, would require additional coding to one or more of our classes.

我之前提到过,实际上可能会在几天内观察到一些假期。 例如,我的组织将感恩节和节假日当作假日。 为了在上面的代码中解决这个HolidayOptions类以包含可以在假日计算期间评估的设置。 但是,这种方法需要对我们的一个或多个类进行附加编码。

As you can see, we can create a working day calculator with just a few classes. In the approach outlined by this article, we have added some additional flexibility that will allow us to extend the calculator to cover other countries' holidays, or to allow us to vary our holiday calculations based on differences between states or jurisdictions. With these classes, we can now determine dates such as when the next bank processing day will be or what day an employee will be off for a holiday.

如您所见,我们可以创建一个只有几节课的工作日计算器。 在本文概述的方法中,我们增加了一些额外的灵活性,这使我们可以将计算器扩展到涵盖其他国家的假期,或者允许我们根据州或辖区之间的差异来更改假期计算。 通过这些课程,我们现在可以确定日期,例如下一个银行处理日是何时或员工休假日的日期。

The complete solution, including unit tests, can be downloaded from the following link:

完整的解决方案,包括单元测试,可以从以下链接下载:

https://filedb.experts-exchange.com/incoming/ee-stuff/8443-WorkingDayCalculator.zip https://filedb.experts-exchange.com/incoming/ee-stuff/8443-WorkingDayCalculator.zip

翻译自: https://www.experts-exchange.com/articles/26839/NET-Working-Day-Calculator.html

工作日计算器

工作日计算器_.NET工作日计算器相关推荐

  1. 定积分计算器_使用科学计算器计算概率分布

    [作者声明] 本文所有文字均为作者原创,所有图片均为作者本人亲自拍摄或制作. 版权所有,仅供阅读欣赏,禁止任何单位或个人以任何形式对本文的文字或图片进行包括但不限于复制.转载.引用.抄袭.截图.模仿. ...

  2. 怎样用计算机算出别人的出生日期,【怀孕出生日期计算器_怀孕出生日期计算器专题】- 天鹅到家...

    很多要想比较技术专业且精确地预测分析自身的排卵期的女性.要想怀孕或避开怀孕的女性,或是要想根据对排卵期的预测分析,根据排卵期時间的不一,对生理学病症做出一些预防的女性,能够运用女性排卵期计算器.女性排 ...

  3. python增加工作日列_将工作日添加到日期字段

    我试图在Django的日期字段中添加一些工作日.这是针对产品订购,我们对不同的产品有不同的交付周期,我们希望为每个产品生成一个目标日期.在 例如,产品X可能需要10个工作日才能交付,如果此产品是在20 ...

  4. java 孕周计算器_周期表孕期计算器

    原标题:周期表孕期计算器 怀孕(或称妊娠.有身),是指哺乳类雌性(包括人类)在体内有一个或多个胎儿或胚胎.人类的妊娠是哺乳动物中研究最详细的. 人类的妊娠从最后一次月经到分娩持续大约40个星期(从排卵 ...

  5. 单片机加减法计算器_十进制加减法计算器单片机设计.doc

    十进制加减法计算器单片机设计 十进制加减法计算器单片机设计 一.设计目的 通过课程设计使学生更进一步掌握单片机原理与应用课程的有关知识,提高用汇编语言编程的能力,并将所学的内容加以综合.通过查阅资料, ...

  6. 涨跌停计算器_在线涨跌计算器

    在线涨跌计算器 众人颔首,都知道这句话是什么意思,也就是说,如果不出意外,盖特应该能够击碎光墙,或者至少在上面留下痕迹. 伊蒂丝觉得,这两年来她压抑了太多的情绪,是这些情绪促使她一往无前地来到威布伦斯 ...

  7. 可以自定义公式的计算器_震惊!计算器竟然可以用来干这个···

    作为一个理工科研狗,不可避免和各种方程打交道,不是正求值就是逆求解.求值还好说,显式函数公式带入即可,甭管是赛因还是抠赛因,老哥还是平方根,科学计算器那么一按,结果美滋滋.然鹅,正如伟大的数学家约翰· ...

  8. c# 算式 计算器_怎么实现计算器中的混合运算+-*/(),用C#语言

    你是想搞个类似计算器的小程序么? 我这边写了个测试版,只负责加减乘除以及括号的算式,你看看是否行,(每个括号中独立算式结果在数组b中),百度有字数限制,其余代码自己看着截图写吧,程序是我这边测试通过了 ...

  9. 木材材积表快速计算器_木材材积计算器

    木材材积计算器 材积表 volume table 木材计量用表.是测树数表的一种.按计量的对象有原木.立木和原 条材积表 : 对于锯材还有计量板方材材积表. 板方材材积表是用长. 宽.厚的乘积制成,比 ...

最新文章

  1. CODING 最佳实践:快课网研发效能提升之路
  2. Linux查看实时带宽流量情况
  3. Android开发中的Handler和多线程
  4. 浪潮POWER 9:英特尔的挑战者
  5. php$_GET 变量
  6. JavaScript多种跨域方式
  7. java随机生成车牌_JDBC:随机生成车牌号,批量插入数据库
  8. 2015/8/26 Python基础(1):基本规则及赋值
  9. 奇门遁甲时家转盘奇门排盘,带八字排盘
  10. bodymovin导出没有html文件,bodymovin导出动画json结果分析
  11. uboot_v2016 版本中fw_printenv的编译问题
  12. 软件测试——透过表象看本质
  13. python中将一个numpy数组转为str,写入文件时会自动换行
  14. stm32用杜邦线与中断模拟led灯开关
  15. 洛谷P1510 精卫填海(简单的dp)
  16. 技术面试(一)认识技术面试
  17. Rivaple 江枫 MC服务器文档(创新生存部分)
  18. windows server 试用激活 及 设置用户数
  19. SQL 取数值小数后两位,但不四舍五入。
  20. Easyweb包括iframe中的页面跳转

热门文章

  1. 菜鸟haqima的Java学习之路第一天
  2. Confluence 6 有关用户的备忘
  3. Linux mysql 内存设置_MYSQL在LINUX机器(4G内存)配置参数
  4. idea使用git推送、下载失败error setting certificate verify locations:
  5. C#使用throw和throw ex的区别
  6. 当代最值得收藏的画家作品_名人名画推荐,值得收藏的当代画家作品
  7. 累加器是寄存器吗?寄存器、累加器、暂存器有什么区别?
  8. 嵌入式设备驱动(实战)
  9. 基于密度的聚类算法(3)——DPC详解
  10. todo清单项目开发,todo清单不止是简单的勾选,还能做更多事情