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



    转自:http://www.cnblogs.com/yuan-chong/p/HolidayHelper.html

    一、开发背景:

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

    二、定义:

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

    三、实践:

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

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

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

        

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

        

      具体代码:

    public class HolidayHelper
        {
            #region 字段属性
            private static object _syncObj = new object();
            private static HolidayHelper _instance { get; set; }
            private static List<DateModel> cacheDateList { get; set; }
            private HolidayHelper() { }
            /// <summary>
            /// 获得单例对象,使用懒汉式(双重锁定)
            /// </summary>
            /// <returns></returns>
            public static HolidayHelper GetInstance()
            {
                if (_instance == null)
                {
                    lock (_syncObj)
                    {
                        if (_instance == null)
                        {
                            _instance = new HolidayHelper();
                        }
                    }
                }
                return _instance;
            }
            #endregion
    
            #region 私有方法
            /// <summary>
            /// 读取文件
            /// </summary>
            /// <param name="filePath"></param>
            /// <returns></returns>
            private string GetFileContent(string filePath)
            {
                string result = "";
                if (File.Exists(filePath))
                {
                    result = File.ReadAllText(filePath);
                }
                return result;
            }
            /// <summary>
            /// 获取配置的Json文件
            /// </summary>
            /// <returns>经过反序列化之后的对象集合</returns>
            private List<DateModel> GetConfigList()
            {
                string path = string.Format("{0}/../../Config/holidayConfig.json", System.AppDomain.CurrentDomain.BaseDirectory);
                string fileContent = GetFileContent(path);
                if (!string.IsNullOrWhiteSpace(fileContent))
                {
                    cacheDateList = Newtonsoft.Json.JsonConvert.DeserializeObject<List<DateModel>>(fileContent);
                }
                return cacheDateList;
            }
            /// <summary>
            /// 获取指定年份的数据
            /// </summary>
            /// <param name="year"></param>
            /// <returns></returns>
            private DateModel GetConfigDataByYear(int year)
            {
                if (cacheDateList == null)//取配置数据
                    GetConfigList();
                DateModel result = cacheDateList.FirstOrDefault(m => m.Year == year);
                return result;
            }
            /// <summary>
            /// 判断是否为工作日
            /// </summary>
            /// <param name="currDate">要判断的时间</param>
            /// <param name="thisYearData">当前的数据</param>
            /// <returns></returns>
            private bool IsWorkDay(DateTime currDate, DateModel thisYearData)
            {
                if (currDate.Year != thisYearData.Year)//跨年重新读取数据
                {
                    thisYearData = GetConfigDataByYear(currDate.Year);
                }
                if (thisYearData.Year > 0)
                {
                    string date = currDate.ToString("MMdd");
                    int week = (int)currDate.DayOfWeek;
    
                    if (thisYearData.Work.IndexOf(date) >= 0)
                    {
                        return true;
                    }
    
                    if (thisYearData.Holiday.IndexOf(date) >= 0)
                    {
                        return false;
                    }
    
                    if (week != 0 && week != 6)
                    {
                        return true;
                    }
                }
                return false;
            }
    
            #endregion
    
            #region 公共方法
            public void CleraCacheData()
            {
                if (cacheDateList != null)
                {
                    cacheDateList.Clear();
                }
            }
            /// <summary>
            /// 根据传入的工作日天数,获得计算后的日期,可传负数
            /// </summary>
            /// <param name="day">天数</param>
            /// <param name="isContainToday">当天是否算工作日(默认:true)</param>
            /// <returns></returns>
            public DateTime GetReckonDate(int day, bool isContainToday = true)
            {
                DateTime currDate = DateTime.Now;
                int addDay = day >= 0 ? 1 : -1;
    
                if (isContainToday)
                    currDate = currDate.AddDays(-addDay);
    
                DateModel thisYearData = GetConfigDataByYear(currDate.Year);
                if (thisYearData.Year > 0)
                {
                    int sumDay = Math.Abs(day);
                    int workDayNum = 0;
                    while (workDayNum < sumDay)
                    {
                        currDate = currDate.AddDays(addDay);
                        if (IsWorkDay(currDate, thisYearData))
                            workDayNum++;
                    }
                }
                return currDate;
            }
            /// <summary>
            /// 根据传入的时间,计算工作日天数
            /// </summary>
            /// <param name="date">带计算的时间</param>
            /// <param name="isContainToday">当天是否算工作日(默认:true)</param>
            /// <returns></returns>
            public int GetWorkDayNum(DateTime date, bool isContainToday = true)
            {
                var currDate = DateTime.Now;
    
                int workDayNum = 0;
                int addDay = date.Date > currDate.Date ? 1 : -1;
    
                if (isContainToday)
                {
                    currDate = currDate.AddDays(-addDay);
                }
    
                DateModel thisYearData = GetConfigDataByYear(currDate.Year);
                if (thisYearData.Year > 0)
                {
                    bool isEnd = false;
                    do
                    {
                        currDate = currDate.AddDays(addDay);
                        if (IsWorkDay(currDate, thisYearData))
                            workDayNum += addDay;
                        isEnd = addDay > 0 ? (date.Date > currDate.Date) : (date.Date < currDate.Date);
                    } while (isEnd);
                }
                return workDayNum;
            }
            #endregion
        }
    
        public struct DateModel
        {
            public int Year { get; set; }
    
            public List<string> Work { get; set; }
    
            public List<string> Holiday { get; set; }
        }
    

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

    [
      {
        "Year": "2015",
        "Work": [ "0104", "0215", "0228", "0906", "1010" ],
        "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" ]
      },
      {
        "Year": "2016",
        "Work": [ "0206", "0214", "0612", "0918", "1008", "1009" ],
        "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" ]
      },
      {
        "Year": "2017",
        "Work": [ "0122", "0204", "0401", "0527", "0930" ],
        "Holiday": [ "0101", "0102", "0127", "0128", "0129", "0130", "0201", "0202", "0501", "0529", "0530", "1001", "1002", "1003", "1004", "1005", "1006" ]
      }
    ]
    
    holidayConfig.json
    

      

  • 相关阅读:
    使用 Fiddler2 进行接口测试的方法
    [cpyhon源代码]dict对象原理学习
    python PEPs-KEY
    python PEP1-总纲
    Python 容器的可变性
    Python源码分析:PyObject对象的起源与多态的实现
    python源码分析:dict对象的实现
    【ShadowsSocks】相关知识汇总
    python str函数isdigit、isdecimal、isnumeric的区别
    python 核心编程 第七章习题
  • 原文地址:https://www.cnblogs.com/zhao-yi/p/7809011.html
Copyright © 2020-2023  润新知