C#实现工作日和休息日(包括法定节假日)的计算

  先介绍下本人的基本情况,本人今年刚毕业,坐标上海,.NET 菜鸟一枚,第一次写博客,有意见欢迎大家提出,大神轻喷!

  好了,废话不多说,开门见山。

一、开发背景:

  最近在公司开发的系统中,需要计算工作日,就是给出一个采购周期(n天),我需要计算出在n个工作日之后的日期。开始准备去调接口(ps:找了半天发现没有太合适的,还有吐槽下国家政府单位都没有官方接口的),但是负责这个项目的大佬说,万一别个的接口崩了,会影响我们自己的系统的正常运行,自己开发还是稳点,我就写了这个功能,特此记录下实现这个功能的思路。

二、定义:

  工作日想必大家都知道,就是除去周末和每年国务院颁布的节假日放假安排(例如:2017年部分节假日安排),其他就都是工作日(对了,差点忘记补班,这也算是工作日哦)。

三、实践:

  “废话”说的够多了,下面撸起袖子开干吧,代码都写了注释。

  提供了两个公共方法,先给大家看下简单测试的运行结果:

    (1).根据传入的工作日天数,获得计算后的日期

    

    (2).根据传入的时间,计算工作日天数;

    

  具体代码:

  1 public class HolidayHelper
  2     {
  3         #region 字段属性
  4         private static object _syncObj = new object();
  5         private static HolidayHelper _instance { get; set; }
  6         private static List<DateModel> cacheDateList { get; set; }
  7         private HolidayHelper() { }
  8         /// <summary>
  9         /// 获得单例对象,使用懒汉式(双重锁定)
 10         /// </summary>
 11         /// <returns></returns>
 12         public static HolidayHelper GetInstance()
 13         {
 14             if (_instance == null)
 15             {
 16                 lock (_syncObj)
 17                 {
 18                     if (_instance == null)
 19                     {
 20                         _instance = new HolidayHelper();
 21                     }
 22                 }
 23             }
 24             return _instance;
 25         }
 26         #endregion
 27 
 28         #region 私有方法
 29         /// <summary>
 30         /// 读取文件
 31         /// </summary>
 32         /// <param name="filePath"></param>
 33         /// <returns></returns>
 34         private string GetFileContent(string filePath)
 35         {
 36             string result = "";
 37             if (File.Exists(filePath))
 38             {
 39                 result = File.ReadAllText(filePath);
 40             }
 41             return result;
 42         }
 43         /// <summary>
 44         /// 获取配置的Json文件
 45         /// </summary>
 46         /// <returns>经过反序列化之后的对象集合</returns>
 47         private List<DateModel> GetConfigList()
 48         {
 49             string path = string.Format("{0}/../../Config/holidayConfig.json", System.AppDomain.CurrentDomain.BaseDirectory);
 50             string fileContent = GetFileContent(path);
 51             if (!string.IsNullOrWhiteSpace(fileContent))
 52             {
 53                 cacheDateList = Newtonsoft.Json.JsonConvert.DeserializeObject<List<DateModel>>(fileContent);
 54             }
 55             return cacheDateList;
 56         }
 57         /// <summary>
 58         /// 获取指定年份的数据
 59         /// </summary>
 60         /// <param name="year"></param>
 61         /// <returns></returns>
 62         private DateModel GetConfigDataByYear(int year)
 63         {
 64             if (cacheDateList == null)//取配置数据
 65                 GetConfigList();
 66             DateModel result = cacheDateList.FirstOrDefault(m => m.Year == year);
 67             return result;
 68         }
 69         /// <summary>
 70         /// 判断是否为工作日
 71         /// </summary>
 72         /// <param name="currDate">要判断的时间</param>
 73         /// <param name="thisYearData">当前的数据</param>
 74         /// <returns></returns>
 75         private bool IsWorkDay(DateTime currDate, DateModel thisYearData)
 76         {
 77             if (currDate.Year != thisYearData.Year)//跨年重新读取数据
 78             {
 79                 thisYearData = GetConfigDataByYear(currDate.Year);
 80             }
 81             if (thisYearData.Year > 0)
 82             {
 83                 string date = currDate.ToString("MMdd");
 84                 int week = (int)currDate.DayOfWeek;
 85 
 86                 if (thisYearData.Work.IndexOf(date) >= 0)
 87                 {
 88                     return true;
 89                 }
 90 
 91                 if (thisYearData.Holiday.IndexOf(date) >= 0)
 92                 {
 93                     return false;
 94                 }
 95 
 96                 if (week != 0 && week != 6)
 97                 {
 98                     return true;
 99                 }
100             }
101             return false;
102         }
103 
104         #endregion
105 
106         #region 公共方法
107         public void CleraCacheData()
108         {
109             if (cacheDateList != null)
110             {
111                 cacheDateList.Clear();
112             }
113         }
114         /// <summary>
115         /// 根据传入的工作日天数,获得计算后的日期,可传负数
116         /// </summary>
117         /// <param name="day">天数</param>
118         /// <param name="isContainToday">当天是否算工作日(默认:true)</param>
119         /// <returns></returns>
120         public DateTime GetReckonDate(int day, bool isContainToday = true)
121         {
122             DateTime currDate = DateTime.Now;
123             int addDay = day >= 0 ? 1 : -1;
124 
125             if (isContainToday)
126                 currDate = currDate.AddDays(-addDay);
127 
128             DateModel thisYearData = GetConfigDataByYear(currDate.Year);
129             if (thisYearData.Year > 0)
130             {
131                 int sumDay = Math.Abs(day);
132                 int workDayNum = 0;
133                 while (workDayNum < sumDay)
134                 {
135                     currDate = currDate.AddDays(addDay);
136                     if (IsWorkDay(currDate, thisYearData))
137                         workDayNum++;
138                 }
139             }
140             return currDate;
141         }
142         /// <summary>
143         /// 根据传入的时间,计算工作日天数
144         /// </summary>
145         /// <param name="date">带计算的时间</param>
146         /// <param name="isContainToday">当天是否算工作日(默认:true)</param>
147         /// <returns></returns>
148         public int GetWorkDayNum(DateTime date, bool isContainToday = true)
149         {
150             var currDate = DateTime.Now;
151 
152             int workDayNum = 0;
153             int addDay = date.Date > currDate.Date ? 1 : -1;
154 
155             if (isContainToday)
156             {
157                 currDate = currDate.AddDays(-addDay);
158             }
159 
160             DateModel thisYearData = GetConfigDataByYear(currDate.Year);
161             if (thisYearData.Year > 0)
162             {
163                 bool isEnd = false;
164                 do
165                 {
166                     currDate = currDate.AddDays(addDay);
167                     if (IsWorkDay(currDate, thisYearData))
168                         workDayNum += addDay;
169                     isEnd = addDay > 0 ? (date.Date > currDate.Date) : (date.Date < currDate.Date);
170                 } while (isEnd);
171             }
172             return workDayNum;
173         }
174         #endregion
175     }
176 
177     public struct DateModel
178     {
179         public int Year { get; set; }
180 
181         public List<string> Work { get; set; }
182 
183         public List<string> Holiday { get; set; }
184     }
View Code

  说明下,法定节假日我是自己用json来配置的,大家可以自己维护,或者做成自己的接口。下面展示下json的格式,这是我自己配置的(2015-2017年),大家可以按照自己的需求来修改。

 1 [
 2   {
 3     "Year": "2015",
 4     "Work": [ "0104", "0215", "0228", "0906", "1010" ],
 5     "Holiday": [ "0101", "0102", "0103", "0218", "0219", "0220", "0221", "0222", "0223", "0224", "0404", "0405", "0406", "0501", "0502", "0503", "0620", "0621", "0622", "0903", "0904", "0905", "0927", "1001", "1002", "1003", "1004", "1005", "1006", "1007" ]
 6   },
 7   {
 8     "Year": "2016",
 9     "Work": [ "0206", "0214", "0612", "0918", "1008", "1009" ],
10     "Holiday": [ "0101", "0207", "0208", "0209", "0210", "0211", "0212", "0213", "0404", "0501", "0502", "0609", "0610", "0611", "0915", "0916", "0917", "1001", "1002", "1003", "1004", "1005", "1006", "1007" ]
11   },
12   {
13     "Year": "2017",
14     "Work": [ "0122", "0204", "0401", "0527", "0930" ],
15     "Holiday": [ "0101", "0102", "0127", "0128", "0129", "0130", "0201", "0202", "0501", "0529", "0530", "1001", "1002", "1003", "1004", "1005", "1006" ]
16   }
17 ]
holidayConfig.json

  好了,就说这么多,由于能力有限,有写得不好的地方,欢迎指正、补充。如果对您有帮助,请帮忙点个赞,谢谢!

原文地址:https://www.cnblogs.com/yuan-chong/p/HolidayHelper.html