Clever-1945
12/1/2017 - 4:58 AM

WorkingDateTime Информация о дате и о дне ( рабочий, празднечный, сокращенный ) #CSharp

WorkingDateTime Информация о дате и о дне ( рабочий, празднечный, сокращенный ) #CSharp

/// <summary> Информация о дате и о дне ( рабочий, празднечный, сокращенный ) </summary>
    public class WorkingDateTime
    {
        /// <summary> Информация о дате и о дне ( рабочий, празднечный, сокращенный ) </summary>
        public WorkingDateTime(DateTime date, bool IsWorkDay, bool IsWeekend, bool IsHalfDay)
        {
            private_date = date;
            private_IsWorkDay = IsWorkDay;
            private_IsWeekend = IsWeekend;
            private_IsHalfDay = IsHalfDay;
        }
        /// <summary> Список нестандартных дат календаря рабочего времени, что мы получили с сервисов по API </summary>
        public static List<WorkingDateTime> listWorkingDateTime = new List<WorkingDateTime>();

        /// <summary> Рассматриваемая дата </summary>
        public DateTime date { get { return private_date; } }
        /// <summary> Рассматриваемая дата </summary>
        private DateTime private_date { set; get; }

        /// <summary> Факт того, что текущая дата является рабочим днем календаря </summary>
        public bool IsWorkDay { get { return private_IsWorkDay; } }
        /// <summary> Факт того, что текущая дата является рабочим днем календаря </summary>
        private bool private_IsWorkDay { set; get; }

        /// <summary> Факт того, что текущая дата является выходным или празднечным днем </summary>
        public bool IsWeekend { get { return private_IsWeekend; } }
        /// <summary> Факт того, что текущая дата является выходным или празднечным днем </summary>
        private bool private_IsWeekend { set; get; }

        /// <summary> Факт того, что текущая дата является сокращенным, рабочим днем днем </summary>
        public bool IsHalfDay { get { return private_IsHalfDay; } }
        /// <summary> Факт того, что текущая дата является сокращенным, рабочим днем днем </summary>
        private bool private_IsHalfDay { set; get; }
        /// <summary> Возвращает список дат рабочего календаря в промежутке чисел </summary>
        /// <param name="startDate"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public static List<WorkingDateTime> get(DateTime startDate, DateTime endDate)
        {
            if (startDate > endDate)
                return new List<WorkingDateTime>();
            //У нас есть 2 сервиса, которые могут рассказать о нестандартых датах рабочего календаря
            //Речь идет о празднечных днях, о сокращенных дняи и субботах, воскресениях, которые могуб быть рабочеми!
            //http://basicdata.ru/api/json/calend/          --Получение JSON результата, что вернет нестнандартные даты
            //http://xmlcalendar.ru/data/ru/2018/calendar.xml       --Полчение XML результата, что вернет нестандартные дни
            startDate = startDate.Date;
            endDate = endDate.Date;

            //Сперва мы проверим, а есть ли эти даты уже в списке, что мы получили по API
            lock (listWorkingDateTime)
            {
                if (listWorkingDateTime.Select(x => x.date).Contains(startDate) && listWorkingDateTime.Select(x => x.date).Contains(endDate))
                    return listWorkingDateTime.Where(x => x.date >= startDate && x.date <= endDate).ToList();
            }
            List<WorkingDateTime> listDateTimeApi = new List<WorkingDateTime>();
            //Сюда мы попали если в списке нестандартных дат, не оказалось нашего промежутка!
            WebClient client = new WebClient();
            try
            {
                string json = client.DownloadString("http://basicdata.ru/api/json/calend/");
                var j_object = JObject.Parse(json)["data"].ToObject<JObject>();
                var listYear = j_object.Properties().Select(x => x.Name).ToList();
                foreach (string year in listYear)
                {
                    var listMonth = j_object[year].ToObject<JObject>().Properties().Select(x => x.Name).ToList();
                    foreach (string month in listMonth)
                    {
                        var listDay = j_object[year][month].ToObject<JObject>().Properties().Select(x => x.Name).ToList();
                        foreach (string day in listDay)
                        {
                            int isWorking = j_object[year][month][day]["isWorking"].Value<int>();
                            string monthIndex = month.Length == 2 ? month : ("0" + month);
                            string dayIndex = day.Length == 2 ? day : ("0" + day);
                            DateTime date = Convert.ToDateTime($"{year}-{monthIndex}-{dayIndex}");
                            bool IsWorkDay = isWorking == 0 ? true : false;
                            bool IsWeekend = isWorking == 2 ? true : false;
                            bool IsHalfDay = isWorking == 3 ? true : false;
                            listDateTimeApi.Add(new WorkingDateTime(date, IsWorkDay, IsWeekend, IsHalfDay));
                            /*
                                0 — рабочий день;
                                2 — праздничный/нерабочий день;
                                3 — сокращенный на 1 час рабочий день. 
                            */
                        }
                    }
                }
            }
            catch { }
            var listDateTimeApiYear = listDateTimeApi.Select(x => x.date.Year).Distinct().ToList();
            int startYear = listDateTimeApiYear.First();
            int endYear = listDateTimeApiYear.Last() + 1;
            if (listDateTimeApiYear.Contains(startDate.Year) != true || listDateTimeApiYear.Contains(endDate.Year) != true)
            {
                try
                {
                    //Если первого сервиса не достаточно
                    //Он не выдал календарь рабочего времени за интересующие нас года, то тогда воспользуемся вторым сервсом!
                    //Сперва следует достать все года, которые нас интересуют!
                    List<int> listYear = new List<int>();
                    startYear = startDate.Year;
                    endYear = endDate.Year;
                    for (int i = startYear; i <= endYear; i++)
                        listYear.Add(i);
                    listYear = listYear.Where(x => listDateTimeApiYear.Contains(x) != true).ToList();
                    foreach (int year in listYear)
                    {
                        XmlDocument document = new XmlDocument();
                        document.Load($"http://xmlcalendar.ru/data/ru/{year}/calendar.xml");
                        var listDay = document.GetElementsByTagName("*").Cast<XmlNode>().Where(x => (x.Name ?? "").Trim() == "day").ToList();
                        foreach (XmlNode day in listDay)
                        {
                            //<day d="01.01" t="1" h="1" />
                            string d = day.Attributes.Cast<XmlAttribute>().Where(x => (x.Name ?? "").Trim() == "d").Where(x => !String.IsNullOrWhiteSpace(x.Value)).Select(x => x.Value).FirstOrDefault();
                            string t = day.Attributes.Cast<XmlAttribute>().Where(x => (x.Name ?? "").Trim() == "t").Where(x => !String.IsNullOrWhiteSpace(x.Value)).Select(x => x.Value).FirstOrDefault();
                            if (String.IsNullOrWhiteSpace(d))
                                continue;
                            if (String.IsNullOrWhiteSpace(t))
                                continue;
                            DateTime date = new DateTime();
                            if (DateTime.TryParse($"{year}-" + d.Replace(".", "-"), out date) != true)
                                continue;
                            bool IsWorkDay = t == "3" ? true : false;
                            bool IsWeekend = t == "1" ? true : false;
                            bool IsHalfDay = t == "2" ? true : false;
                            listDateTimeApi.Add(new WorkingDateTime(date, IsWorkDay, IsWeekend, IsHalfDay));
                        }

                        /*
                         t - тип дня: 
                            1 - выходной день, 
                            2 - рабочий и сокращенный (может быть использован для любого дня недели), 
                            3 - рабочий день (суббота/воскресенье)
                         */
                    }
                }
                catch { }
            }
            listDateTimeApiYear = listDateTimeApi.Select(x => x.date.Year).Distinct().ToList();
            //В listDateTimeApi у нас собраны только нестандартные даты. Теперь сформируем массив где будут обсалютно все даты тех лет что есть в listDateTimeApi
            List<WorkingDateTime> listDateTime = new List<WorkingDateTime>();
            startYear = listDateTimeApiYear.Min();
            endYear = listDateTimeApiYear.Max() + 1;
            DateTime startForDate = Convert.ToDateTime($"{startYear}-01-01");
            DateTime endForDate = Convert.ToDateTime($"{endYear}-01-01");
            for (DateTime date = startForDate; date < endForDate; date = date.AddDays(1))
            {
                var workingDate = listDateTimeApi.Where(x => x.date == date).FirstOrDefault();
                if (workingDate != null)
                {
                    listDateTime.Add(workingDate);
                    continue;
                }
                bool IsWorkDay = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday ? false : true;
                bool IsWeekend = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday ? true : false;
                bool IsHalfDay = false;
                listDateTime.Add(new WorkingDateTime(date, IsWorkDay, IsWeekend, IsHalfDay));
            }

            lock (listWorkingDateTime)
            {
                listWorkingDateTime.AddRange(listDateTime.Where(x => listWorkingDateTime.Select(z => z.date).Contains(x.date) != true));
                //Если мы только что обратились к API сервисам и все интересующие нас даты были подгружены от туда!
                listDateTime = listWorkingDateTime.Where(x => x.date >= startDate && x.date <= endDate).ToList();
                if (listWorkingDateTime.Select(x => x.date).Contains(startDate) && listWorkingDateTime.Select(x => x.date).Contains(endDate))
                    return listDateTime;
            }
            //Сюда мы попали если те даты, что нам нужны не удалось найти в сервисах календарей рабочего времени
            //В этом случии недостающие даты следует брать из стандартного правила: ( счуббота и воскресение это выходной а все другие дни это рабочие дни )
            var listCurrentDateTime = listDateTime.Select(x => x.date).ToList();
            for (DateTime date = startDate; date <= endDate; date = date.AddDays(1))
            {
                if (listCurrentDateTime.Contains(date))
                    continue;
                bool IsWorkDay = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday ? false : true;
                bool IsWeekend = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday ? true : false;
                bool IsHalfDay = false;
                listDateTime.Add(new WorkingDateTime(date, IsWorkDay, IsWeekend, IsHalfDay));
            }

            return listDateTime;
        }
    }