懒得勤快 2 年 前
コミット
e1ffce4da9

+ 620 - 620
Masuit.Tools.Abstractions/DateTimeExt/DateTimeHelper.cs

@@ -7,624 +7,624 @@ using System.Runtime.InteropServices;
 
 namespace Masuit.Tools.DateTimeExt
 {
-    /// <summary>
-    /// 日期操作工具类
-    /// </summary>
-    public static class DateUtil
-    {
-        /// <summary>
-        /// 获取某一年有多少周
-        /// </summary>
-        /// <param name="_"></param>
-        /// <param name="year">年份</param>
-        /// <returns>该年周数</returns>
-        public static int GetWeekAmount(this DateTime _, int year)
-        {
-            var end = new DateTime(year, 12, 31); //该年最后一天
-            var gc = new GregorianCalendar();
-            return gc.GetWeekOfYear(end, CalendarWeekRule.FirstDay, DayOfWeek.Monday); //该年星期数
-        }
-
-        /// <summary>
-        /// 返回年度第几个星期   默认星期日是第一天
-        /// </summary>
-        /// <param name="date">时间</param>
-        /// <returns>第几周</returns>
-        public static int WeekOfYear(this in DateTime date)
-        {
-            var gc = new GregorianCalendar();
-            return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
-        }
-
-        /// <summary>
-        /// 返回年度第几个星期
-        /// </summary>
-        /// <param name="date">时间</param>
-        /// <param name="week">一周的开始日期</param>
-        /// <returns>第几周</returns>
-        public static int WeekOfYear(this in DateTime date, DayOfWeek week)
-        {
-            var gc = new GregorianCalendar();
-            return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, week);
-        }
-
-        /// <summary>
-        /// 得到一年中的某周的起始日和截止日
-        /// 年 nYear
-        /// 周数 nNumWeek
-        /// 周始 out dtWeekStart
-        /// 周终 out dtWeekeEnd
-        /// </summary>
-        /// <param name="_"></param>
-        /// <param name="nYear">年份</param>
-        /// <param name="nNumWeek">第几周</param>
-        /// <param name="dtWeekStart">开始日期</param>
-        /// <param name="dtWeekeEnd">结束日期</param>
-        public static void GetWeekTime(this DateTime _, int nYear, int nNumWeek, out DateTime dtWeekStart, out DateTime dtWeekeEnd)
-        {
-            var dt = new DateTime(nYear, 1, 1);
-            dt += new TimeSpan((nNumWeek - 1) * 7, 0, 0, 0);
-            dtWeekStart = dt.AddDays(-(int)dt.DayOfWeek + (int)DayOfWeek.Monday);
-            dtWeekeEnd = dt.AddDays((int)DayOfWeek.Saturday - (int)dt.DayOfWeek + 1);
-        }
-
-        /// <summary>
-        /// 得到一年中的某周的起始日和截止日    周一到周五  工作日
-        /// </summary>
-        /// <param name="_"></param>
-        /// <param name="nYear">年份</param>
-        /// <param name="nNumWeek">第几周</param>
-        /// <param name="dtWeekStart">开始日期</param>
-        /// <param name="dtWeekeEnd">结束日期</param>
-        public static void GetWeekWorkTime(this DateTime _, int nYear, int nNumWeek, out DateTime dtWeekStart, out DateTime dtWeekeEnd)
-        {
-            var dt = new DateTime(nYear, 1, 1);
-            dt += new TimeSpan((nNumWeek - 1) * 7, 0, 0, 0);
-            dtWeekStart = dt.AddDays(-(int)dt.DayOfWeek + (int)DayOfWeek.Monday);
-            dtWeekeEnd = dt.AddDays((int)DayOfWeek.Saturday - (int)dt.DayOfWeek + 1).AddDays(-2);
-        }
-
-        #region P/Invoke 设置本地时间
-
-        [DllImport("kernel32.dll")]
-        private static extern bool SetLocalTime(ref SystemTime time);
-
-        [StructLayout(LayoutKind.Sequential)]
-        private struct SystemTime
-        {
-            public short year;
-            public short month;
-            public short dayOfWeek;
-            public short day;
-            public short hour;
-            public short minute;
-            public short second;
-            public short milliseconds;
-        }
-
-        /// <summary>
-        /// 设置本地计算机系统时间,仅支持Windows系统
-        /// </summary>
-        /// <param name="dt">DateTime对象</param>
-        public static void SetLocalTime(this in DateTime dt)
-        {
-            SystemTime st;
-            st.year = (short)dt.Year;
-            st.month = (short)dt.Month;
-            st.dayOfWeek = (short)dt.DayOfWeek;
-            st.day = (short)dt.Day;
-            st.hour = (short)dt.Hour;
-            st.minute = (short)dt.Minute;
-            st.second = (short)dt.Second;
-            st.milliseconds = (short)dt.Millisecond;
-            SetLocalTime(ref st);
-        }
-
-        #endregion P/Invoke 设置本地时间
-
-        /// <summary>
-        /// 返回相对于当前时间的相对天数
-        /// </summary>
-        /// <param name="dt"></param>
-        /// <param name="relativeday">相对天数</param>
-        public static string GetDateTime(this in DateTime dt, int relativeday)
-        {
-            return dt.AddDays(relativeday).ToString("yyyy-MM-dd HH:mm:ss");
-        }
-
-        /// <summary>
-        /// 返回标准时间格式string
-        /// </summary>
-        public static string GetDateTimeF(this in DateTime dt) => dt.ToString("yyyy-MM-dd HH:mm:ss:fffffff");
-
-        /// <summary>
-        /// 获取该时间相对于1970-01-01T00:00:00Z的秒数
-        /// </summary>
-        /// <param name="dt"></param>
-        /// <returns></returns>
-        public static long GetTotalSeconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10_000_000L - 62135596800L;
-
-        /// <summary>
-        /// 获取该时间相对于1970-01-01T00:00:00Z的毫秒数
-        /// </summary>
-        /// <param name="dt"></param>
-        /// <returns></returns>
-        public static long GetTotalMilliseconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10000L - 62135596800000L;
-
-        /// <summary>
-        /// 获取该时间相对于1970-01-01T00:00:00Z的微秒时间戳
-        /// </summary>
-        /// <param name="dt"></param>
-        /// <returns></returns>
-        public static long GetTotalMicroseconds(this in DateTime dt) => (new DateTimeOffset(dt).UtcTicks - 621355968000000000) / 10;
-
-        [DllImport("Kernel32.dll")]
-        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
-
-        /// <summary>
-        /// 获取该时间相对于1970-01-01T00:00:00Z的纳秒时间戳
-        /// </summary>
-        /// <param name="dt"></param>
-        /// <returns></returns>
-        public static long GetTotalNanoseconds(this in DateTime dt)
-        {
-            var ticks = (new DateTimeOffset(dt).UtcTicks - 621355968000000000) * 100;
-            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
-            {
-                QueryPerformanceCounter(out var timestamp);
-                return ticks + timestamp % 100;
-            }
-
-            return ticks + Stopwatch.GetTimestamp() % 100;
-        }
-
-        /// <summary>
-        /// 获取该时间相对于1970-01-01T00:00:00Z的分钟数
-        /// </summary>
-        /// <param name="dt"></param>
-        /// <returns></returns>
-        public static double GetTotalMinutes(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalMinutes;
-
-        /// <summary>
-        /// 获取该时间相对于1970-01-01T00:00:00Z的小时数
-        /// </summary>
-        /// <param name="dt"></param>
-        /// <returns></returns>
-        public static double GetTotalHours(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalHours;
-
-        /// <summary>
-        /// 获取该时间相对于1970-01-01T00:00:00Z的天数
-        /// </summary>
-        /// <param name="dt"></param>
-        /// <returns></returns>
-        public static double GetTotalDays(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalDays;
-
-        /// <summary>
-        /// 返回本年有多少天
-        /// </summary>
-        /// <param name="_"></param>
-        /// <param name="iYear">年份</param>
-        /// <returns>本年的天数</returns>
-        public static int GetDaysOfYear(this DateTime _, int iYear)
-        {
-            return IsRuYear(iYear) ? 366 : 365;
-        }
-
-        /// <summary>本年有多少天</summary>
-        /// <param name="dt">日期</param>
-        /// <returns>本天在当年的天数</returns>
-        public static int GetDaysOfYear(this in DateTime dt)
-        {
-            //取得传入参数的年份部分,用来判断是否是闰年
-            int n = dt.Year;
-            return IsRuYear(n) ? 366 : 365;
-        }
-
-        /// <summary>本月有多少天</summary>
-        /// <param name="_"></param>
-        /// <param name="iYear">年</param>
-        /// <param name="month">月</param>
-        /// <returns>天数</returns>
-        public static int GetDaysOfMonth(this DateTime _, int iYear, int month)
-        {
-            return month switch
-            {
-                1 => 31,
-                2 => (IsRuYear(iYear) ? 29 : 28),
-                3 => 31,
-                4 => 30,
-                5 => 31,
-                6 => 30,
-                7 => 31,
-                8 => 31,
-                9 => 30,
-                10 => 31,
-                11 => 30,
-                12 => 31,
-                _ => 0
-            };
-        }
-
-        /// <summary>本月有多少天</summary>
-        /// <param name="dt">日期</param>
-        /// <returns>天数</returns>
-        public static int GetDaysOfMonth(this in DateTime dt)
-        {
-            //--利用年月信息,得到当前月的天数信息。
-            return dt.Month switch
-            {
-                1 => 31,
-                2 => (IsRuYear(dt.Year) ? 29 : 28),
-                3 => 31,
-                4 => 30,
-                5 => 31,
-                6 => 30,
-                7 => 31,
-                8 => 31,
-                9 => 30,
-                10 => 31,
-                11 => 30,
-                12 => 31,
-                _ => 0
-            };
-        }
-
-        /// <summary>返回当前日期的星期名称</summary>
-        /// <param name="idt">日期</param>
-        /// <returns>星期名称</returns>
-        public static string GetWeekNameOfDay(this in DateTime idt)
-        {
-            return idt.DayOfWeek switch
-            {
-                DayOfWeek.Monday => "星期一",
-                DayOfWeek.Tuesday => "星期二",
-                DayOfWeek.Wednesday => "星期三",
-                DayOfWeek.Thursday => "星期四",
-                DayOfWeek.Friday => "星期五",
-                DayOfWeek.Saturday => "星期六",
-                DayOfWeek.Sunday => "星期日",
-                _ => ""
-            };
-        }
-
-        /// <summary>判断当前年份是否是闰年,私有函数</summary>
-        /// <param name="iYear">年份</param>
-        /// <returns>是闰年:True ,不是闰年:False</returns>
-        private static bool IsRuYear(int iYear)
-        {
-            //形式参数为年份
-            //例如:2003
-            var n = iYear;
-            return n % 400 == 0 || n % 4 == 0 && n % 100 != 0;
-        }
-
-        /// <summary>
-        /// 判断是否为合法日期,必须大于1800年1月1日
-        /// </summary>
-        /// <param name="strDate">输入日期字符串</param>
-        /// <returns>True/False</returns>
-        public static bool IsDateTime(this string strDate)
-        {
-            DateTime.TryParse(strDate, out var result);
-            return result.CompareTo(DateTime.Parse("1800-1-1")) > 0;
-        }
-
-        /// <summary>
-        /// 判断时间是否在区间内
-        /// </summary>
-        /// <param name="this"></param>
-        /// <param name="start">开始</param>
-        /// <param name="end">结束</param>
-        /// <param name="mode">模式</param>
-        /// <returns></returns>
-        public static bool In(this in DateTime @this, DateTime start, DateTime end, RangeMode mode = RangeMode.Close)
-        {
-            return mode switch
-            {
-                RangeMode.Open => start < @this && end > @this,
-                RangeMode.Close => start <= @this && end >= @this,
-                RangeMode.OpenClose => start < @this && end >= @this,
-                RangeMode.CloseOpen => start <= @this && end > @this,
-                _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
-            };
-        }
-
-        /// <summary>
-        ///  返回每月的第一天和最后一天
-        /// </summary>
-        /// <param name="_"></param>
-        /// <param name="month">月份</param>
-        /// <param name="firstDay">第一天</param>
-        /// <param name="lastDay">最后一天</param>
-        public static void ReturnDateFormat(this DateTime _, int month, out string firstDay, out string lastDay)
-        {
-            int year = DateTime.Now.Year + month / 12;
-            if (month != 12)
-            {
-                month %= 12;
-            }
-
-            switch (month)
-            {
-                case 1:
-                    firstDay = DateTime.Now.ToString($"{year}-0{month}-01");
-                    lastDay = DateTime.Now.ToString($"{year}-0{month}-31");
-                    break;
-
-                case 2:
-                    firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
-                    lastDay = DateTime.IsLeapYear(DateTime.Now.Year) ? DateTime.Now.ToString(year + "-0" + month + "-29") : DateTime.Now.ToString(year + "-0" + month + "-28");
-                    break;
-
-                case 3:
-                    firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
-                    lastDay = DateTime.Now.ToString("yyyy-0" + month + "-31");
-                    break;
-
-                case 4:
-                    firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
-                    lastDay = DateTime.Now.ToString(year + "-0" + month + "-30");
-                    break;
-
-                case 5:
-                    firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
-                    lastDay = DateTime.Now.ToString(year + "-0" + month + "-31");
-                    break;
-
-                case 6:
-                    firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
-                    lastDay = DateTime.Now.ToString(year + "-0" + month + "-30");
-                    break;
-
-                case 7:
-                    firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
-                    lastDay = DateTime.Now.ToString(year + "-0" + month + "-31");
-                    break;
-
-                case 8:
-                    firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
-                    lastDay = DateTime.Now.ToString(year + "-0" + month + "-31");
-                    break;
-
-                case 9:
-                    firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
-                    lastDay = DateTime.Now.ToString(year + "-0" + month + "-30");
-                    break;
-
-                case 10:
-                    firstDay = DateTime.Now.ToString(year + "-" + month + "-01");
-                    lastDay = DateTime.Now.ToString(year + "-" + month + "-31");
-                    break;
-
-                case 11:
-                    firstDay = DateTime.Now.ToString(year + "-" + month + "-01");
-                    lastDay = DateTime.Now.ToString(year + "-" + month + "-30");
-                    break;
-
-                default:
-                    firstDay = DateTime.Now.ToString(year + "-" + month + "-01");
-                    lastDay = DateTime.Now.ToString(year + "-" + month + "-31");
-                    break;
-            }
-        }
-
-        /// <summary>
-        /// 返回某年某月最后一天
-        /// </summary>
-        /// <param name="_"></param>
-        /// <param name="year">年份</param>
-        /// <param name="month">月份</param>
-        /// <returns>日</returns>
-        public static int GetMonthLastDate(this DateTime _, int year, int month)
-        {
-            DateTime lastDay = new DateTime(year, month, new GregorianCalendar().GetDaysInMonth(year, month));
-            int day = lastDay.Day;
-            return day;
-        }
-
-        /// <summary>
-        /// 获得一段时间内有多少小时
-        /// </summary>
-        /// <param name="dtStar">起始时间</param>
-        /// <param name="dtEnd">终止时间</param>
-        /// <returns>小时差</returns>
-        public static string GetTimeDelay(this in DateTime dtStar, DateTime dtEnd)
-        {
-            long lTicks = (dtEnd.Ticks - dtStar.Ticks) / 10000000;
-            string sTemp = (lTicks / 3600).ToString().PadLeft(2, '0') + ":";
-            sTemp += (lTicks % 3600 / 60).ToString().PadLeft(2, '0') + ":";
-            sTemp += (lTicks % 3600 % 60).ToString().PadLeft(2, '0');
-            return sTemp;
-        }
-
-        /// <summary>
-        /// 获得8位时间整型数字
-        /// </summary>
-        /// <param name="dt">当前的日期时间对象</param>
-        /// <returns>8位时间整型数字</returns>
-        public static string GetDateString(this in DateTime dt)
-        {
-            return dt.Year + dt.Month.ToString().PadLeft(2, '0') + dt.Day.ToString().PadLeft(2, '0');
-        }
-
-        /// <summary>
-        /// 返回时间差
-        /// </summary>
-        /// <param name="dateTime1">时间1</param>
-        /// <param name="dateTime2">时间2</param>
-        /// <returns>时间差</returns>
-        public static string DateDiff(this in DateTime dateTime1, in DateTime dateTime2)
-        {
-            string dateDiff;
-            var ts = dateTime2 - dateTime1;
-            if (ts.Days >= 1)
-            {
-                dateDiff = dateTime1.Month + "月" + dateTime1.Day + "日";
-            }
-            else
-            {
-                dateDiff = ts.Hours > 1 ? ts.Hours + "小时前" : ts.Minutes + "分钟前";
-            }
-
-            return dateDiff;
-        }
-
-        /// <summary>
-        /// 计算2个时间差
-        /// </summary>
-        /// <param name="beginTime">开始时间</param>
-        /// <param name="endTime">结束时间</param>
-        /// <returns>时间差</returns>
-        public static string GetDiffTime(this in DateTime beginTime, in DateTime endTime)
-        {
-            string strResout = string.Empty;
-
-            //获得2时间的时间间隔秒计算
-            TimeSpan span = endTime.Subtract(beginTime);
-            int sec = Convert.ToInt32(span.TotalSeconds);
-            int minutes = 1 * 60;
-            int hours = minutes * 60;
-            int day = hours * 24;
-            int month = day * 30;
-            int year = month * 12;
-
-            //提醒时间,到了返回1,否则返回0
-            if (sec > year)
-            {
-                strResout += sec / year + "年";
-                sec %= year; //剩余
-            }
-
-            if (sec > month)
-            {
-                strResout += sec / month + "月";
-                sec %= month;
-            }
-
-            if (sec > day)
-            {
-                strResout += sec / day + "天";
-                sec %= day;
-            }
-
-            if (sec > hours)
-            {
-                strResout += sec / hours + "小时";
-                sec %= hours;
-            }
-
-            if (sec > minutes)
-            {
-                strResout += sec / minutes + "分";
-                sec %= minutes;
-            }
-
-            strResout += sec + "秒";
-            return strResout;
-        }
-
-        /// <summary>
-        /// 时间段并集
-        /// </summary>
-        /// <param name="destination"></param>
-        /// <param name="sources"></param>
-        /// <typeparam name="T"></typeparam>
-        /// <returns></returns>
-        public static ICollection<T> GetUnionSet<T>(this T destination, List<T> sources) where T : ITimePeriod, new()
-        {
-            var result = true;
-            ICollection<T> frames = new List<T>();
-
-            var timeFrames = sources.Where(frame =>
-                !(destination.StartTime > frame.EndTime || destination.EndTime < frame.StartTime)).ToList();
-            if (timeFrames.Any())
-                foreach (var frame in timeFrames)
-                {
-                    frames.Add(frame);
-                    sources.Remove(frame);
-                }
-
-            if (!frames.Any()) return frames;
-            var timePeriod = new T()
-            {
-                EndTime = frames.OrderBy(frame => frame.EndTime).Max(frame => frame.EndTime),
-                StartTime = frames.OrderBy(frame => frame.StartTime).Min(frame => frame.StartTime)
-            };
-
-            while (result)
-            {
-                var maxTimeFrame = GetUnionSet<T>(timePeriod, sources);
-                if (!maxTimeFrame.Any())
-                    result = false;
-                else
-                    foreach (var frame in maxTimeFrame)
-                        frames.Add(frame);
-            }
-
-            return frames;
-        }
-
-        /// <summary>
-        /// 获取一批时间段内存在相互重叠的最大时间段
-        /// </summary>
-        /// <param name="destination">基础时间段</param>
-        /// <param name="sources">一批时间段</param>
-        /// <typeparam name="T"></typeparam>
-        /// <returns></returns>
-        /// <remarks>源数据sources 会受到影响</remarks>
-        public static T GetMaxTimePeriod<T>(this T destination, List<T> sources) where T : ITimePeriod, new()
-        {
-            var list = sources.Select(period => new T()
-            {
-                EndTime = period.EndTime,
-                StartTime = period.StartTime,
-            }).ToList();
-
-            var timePeriods = GetUnionSet(destination, list);
-            return new T()
-            {
-                EndTime = timePeriods.OrderBy(period => period.EndTime).Max(period => period.EndTime),
-                StartTime = timePeriods.OrderBy(period => period.StartTime).Min(period => period.StartTime)
-            };
-        }
-    }
-
-    public interface ITimePeriod
-    {
-        /// <summary>
-        /// 起始时间
-        /// </summary>
-        public DateTime StartTime { get; set; }
-
-        /// <summary>
-        /// 终止时间
-        /// </summary>
-        public DateTime EndTime { get; set; }
-    }
-
-    /// <summary>
-    /// 区间模式
-    /// </summary>
-    public enum RangeMode
-    {
-        /// <summary>
-        /// 开区间
-        /// </summary>
-        Open,
-
-        /// <summary>
-        /// 闭区间
-        /// </summary>
-        Close,
-
-        /// <summary>
-        /// 左开右闭区间
-        /// </summary>
-        OpenClose,
-
-        /// <summary>
-        /// 左闭右开区间
-        /// </summary>
-        CloseOpen
-    }
+	/// <summary>
+	/// 日期操作工具类
+	/// </summary>
+	public static class DateTimeHelper
+	{
+		/// <summary>
+		/// 获取某一年有多少周
+		/// </summary>
+		/// <param name="_"></param>
+		/// <param name="year">年份</param>
+		/// <returns>该年周数</returns>
+		public static int GetWeekAmount(this DateTime _, int year)
+		{
+			var end = new DateTime(year, 12, 31); //该年最后一天
+			var gc = new GregorianCalendar();
+			return gc.GetWeekOfYear(end, CalendarWeekRule.FirstDay, DayOfWeek.Monday); //该年星期数
+		}
+
+		/// <summary>
+		/// 返回年度第几个星期   默认星期日是第一天
+		/// </summary>
+		/// <param name="date">时间</param>
+		/// <returns>第几周</returns>
+		public static int WeekOfYear(this in DateTime date)
+		{
+			var gc = new GregorianCalendar();
+			return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
+		}
+
+		/// <summary>
+		/// 返回年度第几个星期
+		/// </summary>
+		/// <param name="date">时间</param>
+		/// <param name="week">一周的开始日期</param>
+		/// <returns>第几周</returns>
+		public static int WeekOfYear(this in DateTime date, DayOfWeek week)
+		{
+			var gc = new GregorianCalendar();
+			return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, week);
+		}
+
+		/// <summary>
+		/// 得到一年中的某周的起始日和截止日
+		/// 年 nYear
+		/// 周数 nNumWeek
+		/// 周始 out dtWeekStart
+		/// 周终 out dtWeekeEnd
+		/// </summary>
+		/// <param name="_"></param>
+		/// <param name="nYear">年份</param>
+		/// <param name="nNumWeek">第几周</param>
+		/// <param name="dtWeekStart">开始日期</param>
+		/// <param name="dtWeekeEnd">结束日期</param>
+		public static void GetWeekTime(this DateTime _, int nYear, int nNumWeek, out DateTime dtWeekStart, out DateTime dtWeekeEnd)
+		{
+			var dt = new DateTime(nYear, 1, 1);
+			dt += new TimeSpan((nNumWeek - 1) * 7, 0, 0, 0);
+			dtWeekStart = dt.AddDays(-(int)dt.DayOfWeek + (int)DayOfWeek.Monday);
+			dtWeekeEnd = dt.AddDays((int)DayOfWeek.Saturday - (int)dt.DayOfWeek + 1);
+		}
+
+		/// <summary>
+		/// 得到一年中的某周的起始日和截止日    周一到周五  工作日
+		/// </summary>
+		/// <param name="_"></param>
+		/// <param name="nYear">年份</param>
+		/// <param name="nNumWeek">第几周</param>
+		/// <param name="dtWeekStart">开始日期</param>
+		/// <param name="dtWeekeEnd">结束日期</param>
+		public static void GetWeekWorkTime(this DateTime _, int nYear, int nNumWeek, out DateTime dtWeekStart, out DateTime dtWeekeEnd)
+		{
+			var dt = new DateTime(nYear, 1, 1);
+			dt += new TimeSpan((nNumWeek - 1) * 7, 0, 0, 0);
+			dtWeekStart = dt.AddDays(-(int)dt.DayOfWeek + (int)DayOfWeek.Monday);
+			dtWeekeEnd = dt.AddDays((int)DayOfWeek.Saturday - (int)dt.DayOfWeek + 1).AddDays(-2);
+		}
+
+		#region P/Invoke 设置本地时间
+
+		[DllImport("kernel32.dll")]
+		private static extern bool SetLocalTime(ref SystemTime time);
+
+		[StructLayout(LayoutKind.Sequential)]
+		private struct SystemTime
+		{
+			public short year;
+			public short month;
+			public short dayOfWeek;
+			public short day;
+			public short hour;
+			public short minute;
+			public short second;
+			public short milliseconds;
+		}
+
+		/// <summary>
+		/// 设置本地计算机系统时间,仅支持Windows系统
+		/// </summary>
+		/// <param name="dt">DateTime对象</param>
+		public static void SetLocalTime(this in DateTime dt)
+		{
+			SystemTime st;
+			st.year = (short)dt.Year;
+			st.month = (short)dt.Month;
+			st.dayOfWeek = (short)dt.DayOfWeek;
+			st.day = (short)dt.Day;
+			st.hour = (short)dt.Hour;
+			st.minute = (short)dt.Minute;
+			st.second = (short)dt.Second;
+			st.milliseconds = (short)dt.Millisecond;
+			SetLocalTime(ref st);
+		}
+
+		#endregion P/Invoke 设置本地时间
+
+		/// <summary>
+		/// 返回相对于当前时间的相对天数
+		/// </summary>
+		/// <param name="dt"></param>
+		/// <param name="relativeday">相对天数</param>
+		public static string GetDateTime(this in DateTime dt, int relativeday)
+		{
+			return dt.AddDays(relativeday).ToString("yyyy-MM-dd HH:mm:ss");
+		}
+
+		/// <summary>
+		/// 返回标准时间格式string
+		/// </summary>
+		public static string GetDateTimeF(this in DateTime dt) => dt.ToString("yyyy-MM-dd HH:mm:ss:fffffff");
+
+		/// <summary>
+		/// 获取该时间相对于1970-01-01T00:00:00Z的秒数
+		/// </summary>
+		/// <param name="dt"></param>
+		/// <returns></returns>
+		public static long GetTotalSeconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10_000_000L - 62135596800L;
+
+		/// <summary>
+		/// 获取该时间相对于1970-01-01T00:00:00Z的毫秒数
+		/// </summary>
+		/// <param name="dt"></param>
+		/// <returns></returns>
+		public static long GetTotalMilliseconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10000L - 62135596800000L;
+
+		/// <summary>
+		/// 获取该时间相对于1970-01-01T00:00:00Z的微秒时间戳
+		/// </summary>
+		/// <param name="dt"></param>
+		/// <returns></returns>
+		public static long GetTotalMicroseconds(this in DateTime dt) => (new DateTimeOffset(dt).UtcTicks - 621355968000000000) / 10;
+
+		[DllImport("Kernel32.dll")]
+		private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
+
+		/// <summary>
+		/// 获取该时间相对于1970-01-01T00:00:00Z的纳秒时间戳
+		/// </summary>
+		/// <param name="dt"></param>
+		/// <returns></returns>
+		public static long GetTotalNanoseconds(this in DateTime dt)
+		{
+			var ticks = (new DateTimeOffset(dt).UtcTicks - 621355968000000000) * 100;
+			if (Environment.OSVersion.Platform == PlatformID.Win32NT)
+			{
+				QueryPerformanceCounter(out var timestamp);
+				return ticks + timestamp % 100;
+			}
+
+			return ticks + Stopwatch.GetTimestamp() % 100;
+		}
+
+		/// <summary>
+		/// 获取该时间相对于1970-01-01T00:00:00Z的分钟数
+		/// </summary>
+		/// <param name="dt"></param>
+		/// <returns></returns>
+		public static double GetTotalMinutes(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalMinutes;
+
+		/// <summary>
+		/// 获取该时间相对于1970-01-01T00:00:00Z的小时数
+		/// </summary>
+		/// <param name="dt"></param>
+		/// <returns></returns>
+		public static double GetTotalHours(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalHours;
+
+		/// <summary>
+		/// 获取该时间相对于1970-01-01T00:00:00Z的天数
+		/// </summary>
+		/// <param name="dt"></param>
+		/// <returns></returns>
+		public static double GetTotalDays(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalDays;
+
+		/// <summary>
+		/// 返回本年有多少天
+		/// </summary>
+		/// <param name="_"></param>
+		/// <param name="iYear">年份</param>
+		/// <returns>本年的天数</returns>
+		public static int GetDaysOfYear(this DateTime _, int iYear)
+		{
+			return IsRuYear(iYear) ? 366 : 365;
+		}
+
+		/// <summary>本年有多少天</summary>
+		/// <param name="dt">日期</param>
+		/// <returns>本天在当年的天数</returns>
+		public static int GetDaysOfYear(this in DateTime dt)
+		{
+			//取得传入参数的年份部分,用来判断是否是闰年
+			int n = dt.Year;
+			return IsRuYear(n) ? 366 : 365;
+		}
+
+		/// <summary>本月有多少天</summary>
+		/// <param name="_"></param>
+		/// <param name="iYear">年</param>
+		/// <param name="month">月</param>
+		/// <returns>天数</returns>
+		public static int GetDaysOfMonth(this DateTime _, int iYear, int month)
+		{
+			return month switch
+			{
+				1 => 31,
+				2 => (IsRuYear(iYear) ? 29 : 28),
+				3 => 31,
+				4 => 30,
+				5 => 31,
+				6 => 30,
+				7 => 31,
+				8 => 31,
+				9 => 30,
+				10 => 31,
+				11 => 30,
+				12 => 31,
+				_ => 0
+			};
+		}
+
+		/// <summary>本月有多少天</summary>
+		/// <param name="dt">日期</param>
+		/// <returns>天数</returns>
+		public static int GetDaysOfMonth(this in DateTime dt)
+		{
+			//--利用年月信息,得到当前月的天数信息。
+			return dt.Month switch
+			{
+				1 => 31,
+				2 => (IsRuYear(dt.Year) ? 29 : 28),
+				3 => 31,
+				4 => 30,
+				5 => 31,
+				6 => 30,
+				7 => 31,
+				8 => 31,
+				9 => 30,
+				10 => 31,
+				11 => 30,
+				12 => 31,
+				_ => 0
+			};
+		}
+
+		/// <summary>返回当前日期的星期名称</summary>
+		/// <param name="idt">日期</param>
+		/// <returns>星期名称</returns>
+		public static string GetWeekNameOfDay(this in DateTime idt)
+		{
+			return idt.DayOfWeek switch
+			{
+				DayOfWeek.Monday => "星期一",
+				DayOfWeek.Tuesday => "星期二",
+				DayOfWeek.Wednesday => "星期三",
+				DayOfWeek.Thursday => "星期四",
+				DayOfWeek.Friday => "星期五",
+				DayOfWeek.Saturday => "星期六",
+				DayOfWeek.Sunday => "星期日",
+				_ => ""
+			};
+		}
+
+		/// <summary>判断当前年份是否是闰年,私有函数</summary>
+		/// <param name="iYear">年份</param>
+		/// <returns>是闰年:True ,不是闰年:False</returns>
+		private static bool IsRuYear(int iYear)
+		{
+			//形式参数为年份
+			//例如:2003
+			var n = iYear;
+			return n % 400 == 0 || n % 4 == 0 && n % 100 != 0;
+		}
+
+		/// <summary>
+		/// 判断是否为合法日期,必须大于1800年1月1日
+		/// </summary>
+		/// <param name="strDate">输入日期字符串</param>
+		/// <returns>True/False</returns>
+		public static bool IsDateTime(this string strDate)
+		{
+			DateTime.TryParse(strDate, out var result);
+			return result.CompareTo(DateTime.Parse("1800-1-1")) > 0;
+		}
+
+		/// <summary>
+		/// 判断时间是否在区间内
+		/// </summary>
+		/// <param name="this"></param>
+		/// <param name="start">开始</param>
+		/// <param name="end">结束</param>
+		/// <param name="mode">模式</param>
+		/// <returns></returns>
+		public static bool In(this in DateTime @this, DateTime start, DateTime end, RangeMode mode = RangeMode.Close)
+		{
+			return mode switch
+			{
+				RangeMode.Open => start < @this && end > @this,
+				RangeMode.Close => start <= @this && end >= @this,
+				RangeMode.OpenClose => start < @this && end >= @this,
+				RangeMode.CloseOpen => start <= @this && end > @this,
+				_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
+			};
+		}
+
+		/// <summary>
+		///  返回每月的第一天和最后一天
+		/// </summary>
+		/// <param name="_"></param>
+		/// <param name="month">月份</param>
+		/// <param name="firstDay">第一天</param>
+		/// <param name="lastDay">最后一天</param>
+		public static void ReturnDateFormat(this DateTime _, int month, out string firstDay, out string lastDay)
+		{
+			int year = DateTime.Now.Year + month / 12;
+			if (month != 12)
+			{
+				month %= 12;
+			}
+
+			switch (month)
+			{
+				case 1:
+					firstDay = DateTime.Now.ToString($"{year}-0{month}-01");
+					lastDay = DateTime.Now.ToString($"{year}-0{month}-31");
+					break;
+
+				case 2:
+					firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
+					lastDay = DateTime.IsLeapYear(DateTime.Now.Year) ? DateTime.Now.ToString(year + "-0" + month + "-29") : DateTime.Now.ToString(year + "-0" + month + "-28");
+					break;
+
+				case 3:
+					firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
+					lastDay = DateTime.Now.ToString("yyyy-0" + month + "-31");
+					break;
+
+				case 4:
+					firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
+					lastDay = DateTime.Now.ToString(year + "-0" + month + "-30");
+					break;
+
+				case 5:
+					firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
+					lastDay = DateTime.Now.ToString(year + "-0" + month + "-31");
+					break;
+
+				case 6:
+					firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
+					lastDay = DateTime.Now.ToString(year + "-0" + month + "-30");
+					break;
+
+				case 7:
+					firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
+					lastDay = DateTime.Now.ToString(year + "-0" + month + "-31");
+					break;
+
+				case 8:
+					firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
+					lastDay = DateTime.Now.ToString(year + "-0" + month + "-31");
+					break;
+
+				case 9:
+					firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
+					lastDay = DateTime.Now.ToString(year + "-0" + month + "-30");
+					break;
+
+				case 10:
+					firstDay = DateTime.Now.ToString(year + "-" + month + "-01");
+					lastDay = DateTime.Now.ToString(year + "-" + month + "-31");
+					break;
+
+				case 11:
+					firstDay = DateTime.Now.ToString(year + "-" + month + "-01");
+					lastDay = DateTime.Now.ToString(year + "-" + month + "-30");
+					break;
+
+				default:
+					firstDay = DateTime.Now.ToString(year + "-" + month + "-01");
+					lastDay = DateTime.Now.ToString(year + "-" + month + "-31");
+					break;
+			}
+		}
+
+		/// <summary>
+		/// 返回某年某月最后一天
+		/// </summary>
+		/// <param name="_"></param>
+		/// <param name="year">年份</param>
+		/// <param name="month">月份</param>
+		/// <returns>日</returns>
+		public static int GetMonthLastDate(this DateTime _, int year, int month)
+		{
+			DateTime lastDay = new DateTime(year, month, new GregorianCalendar().GetDaysInMonth(year, month));
+			int day = lastDay.Day;
+			return day;
+		}
+
+		/// <summary>
+		/// 获得一段时间内有多少小时
+		/// </summary>
+		/// <param name="dtStar">起始时间</param>
+		/// <param name="dtEnd">终止时间</param>
+		/// <returns>小时差</returns>
+		public static string GetTimeDelay(this in DateTime dtStar, DateTime dtEnd)
+		{
+			long lTicks = (dtEnd.Ticks - dtStar.Ticks) / 10000000;
+			string sTemp = (lTicks / 3600).ToString().PadLeft(2, '0') + ":";
+			sTemp += (lTicks % 3600 / 60).ToString().PadLeft(2, '0') + ":";
+			sTemp += (lTicks % 3600 % 60).ToString().PadLeft(2, '0');
+			return sTemp;
+		}
+
+		/// <summary>
+		/// 获得8位时间整型数字
+		/// </summary>
+		/// <param name="dt">当前的日期时间对象</param>
+		/// <returns>8位时间整型数字</returns>
+		public static string GetDateString(this in DateTime dt)
+		{
+			return dt.Year + dt.Month.ToString().PadLeft(2, '0') + dt.Day.ToString().PadLeft(2, '0');
+		}
+
+		/// <summary>
+		/// 返回时间差
+		/// </summary>
+		/// <param name="dateTime1">时间1</param>
+		/// <param name="dateTime2">时间2</param>
+		/// <returns>时间差</returns>
+		public static string DateDiff(this in DateTime dateTime1, in DateTime dateTime2)
+		{
+			string dateDiff;
+			var ts = dateTime2 - dateTime1;
+			if (ts.Days >= 1)
+			{
+				dateDiff = dateTime1.Month + "月" + dateTime1.Day + "日";
+			}
+			else
+			{
+				dateDiff = ts.Hours > 1 ? ts.Hours + "小时前" : ts.Minutes + "分钟前";
+			}
+
+			return dateDiff;
+		}
+
+		/// <summary>
+		/// 计算2个时间差
+		/// </summary>
+		/// <param name="beginTime">开始时间</param>
+		/// <param name="endTime">结束时间</param>
+		/// <returns>时间差</returns>
+		public static string GetDiffTime(this in DateTime beginTime, in DateTime endTime)
+		{
+			string strResout = string.Empty;
+
+			//获得2时间的时间间隔秒计算
+			TimeSpan span = endTime.Subtract(beginTime);
+			int sec = Convert.ToInt32(span.TotalSeconds);
+			int minutes = 1 * 60;
+			int hours = minutes * 60;
+			int day = hours * 24;
+			int month = day * 30;
+			int year = month * 12;
+
+			//提醒时间,到了返回1,否则返回0
+			if (sec > year)
+			{
+				strResout += sec / year + "年";
+				sec %= year; //剩余
+			}
+
+			if (sec > month)
+			{
+				strResout += sec / month + "月";
+				sec %= month;
+			}
+
+			if (sec > day)
+			{
+				strResout += sec / day + "天";
+				sec %= day;
+			}
+
+			if (sec > hours)
+			{
+				strResout += sec / hours + "小时";
+				sec %= hours;
+			}
+
+			if (sec > minutes)
+			{
+				strResout += sec / minutes + "分";
+				sec %= minutes;
+			}
+
+			strResout += sec + "秒";
+			return strResout;
+		}
+
+		/// <summary>
+		/// 根据某个时间段查找在某批时间段中的最大并集
+		/// </summary>
+		/// <param name="destination"></param>
+		/// <param name="sources"></param>
+		/// <typeparam name="T"></typeparam>
+		/// <returns></returns>
+		public static ICollection<T> GetUnionSet<T>(this T destination, List<T> sources) where T : ITimePeriod, new()
+		{
+			var result = true;
+			ICollection<T> frames = new List<T>();
+
+			var timeFrames = sources.Where(frame =>
+				!(destination.Start > frame.End || destination.End < frame.Start)).ToList();
+			if (timeFrames.Any())
+				foreach (var frame in timeFrames)
+				{
+					frames.Add(frame);
+					sources.Remove(frame);
+				}
+
+			if (!frames.Any()) return frames;
+			var timePeriod = new T()
+			{
+				End = frames.OrderBy(frame => frame.End).Max(frame => frame.End),
+				Start = frames.OrderBy(frame => frame.Start).Min(frame => frame.Start)
+			};
+
+			while (result)
+			{
+				var maxTimeFrame = GetUnionSet<T>(timePeriod, sources);
+				if (!maxTimeFrame.Any())
+					result = false;
+				else
+					foreach (var frame in maxTimeFrame)
+						frames.Add(frame);
+			}
+
+			return frames;
+		}
+
+		/// <summary>
+		/// 获取一批时间段内存在相互重叠的最大时间段
+		/// </summary>
+		/// <param name="destination">基础时间段</param>
+		/// <param name="sources">一批时间段</param>
+		/// <typeparam name="T"></typeparam>
+		/// <returns></returns>
+		/// <remarks>源数据sources 会受到影响</remarks>
+		public static T GetMaxTimePeriod<T>(this T destination, List<T> sources) where T : ITimePeriod, new()
+		{
+			var list = sources.Select(period => new T()
+			{
+				End = period.End,
+				Start = period.Start,
+			}).ToList();
+
+			var timePeriods = GetUnionSet(destination, list);
+			return new T()
+			{
+				End = timePeriods.OrderBy(period => period.End).Max(period => period.End),
+				Start = timePeriods.OrderBy(period => period.Start).Min(period => period.Start)
+			};
+		}
+	}
+
+	public interface ITimePeriod
+	{
+		/// <summary>
+		/// 起始时间
+		/// </summary>
+		public DateTime Start { get; set; }
+
+		/// <summary>
+		/// 终止时间
+		/// </summary>
+		public DateTime End { get; set; }
+	}
+
+	/// <summary>
+	/// 区间模式
+	/// </summary>
+	public enum RangeMode
+	{
+		/// <summary>
+		/// 开区间
+		/// </summary>
+		Open,
+
+		/// <summary>
+		/// 闭区间
+		/// </summary>
+		Close,
+
+		/// <summary>
+		/// 左开右闭区间
+		/// </summary>
+		OpenClose,
+
+		/// <summary>
+		/// 左闭右开区间
+		/// </summary>
+		CloseOpen
+	}
 }

+ 157 - 153
Masuit.Tools.Abstractions/Models/DateTimeRange.cs

@@ -4,156 +4,160 @@ using System.Collections.Generic;
 
 namespace Masuit.Tools.Models
 {
-    /// <summary>
-    /// 时间段
-    /// </summary>
-    public class DateTimeRange
-    {
-        public DateTimeRange(DateTime start, DateTime end)
-        {
-            if (start > end)
-            {
-                throw new Exception("开始时间不能大于结束时间");
-            }
-
-            Start = start;
-            End = end;
-        }
-
-        /// <summary>
-        /// 起始时间
-        /// </summary>
-        public DateTime Start { get; }
-
-        /// <summary>
-        /// 结束时间
-        /// </summary>
-        public DateTime End { get; }
-
-        /// <summary>
-        /// 是否相交
-        /// </summary>
-        /// <param name="start"></param>
-        /// <param name="end"></param>
-        /// <returns></returns>
-        public bool HasIntersect(DateTime start, DateTime end)
-        {
-            return HasIntersect(new DateTimeRange(start, end));
-        }
-
-        /// <summary>
-        /// 是否相交
-        /// </summary>
-        /// <param name="range"></param>
-        /// <returns></returns>
-        public bool HasIntersect(DateTimeRange range)
-        {
-            return Start.In(range.Start, range.End) || End.In(range.Start, range.End);
-        }
-
-        /// <summary>
-        /// 相交时间段
-        /// </summary>
-        /// <param name="range"></param>
-        /// <returns></returns>
-        public (bool intersected, DateTimeRange range) Intersect(DateTimeRange range)
-        {
-            if (HasIntersect(range.Start, range.End))
-            {
-                var list = new List<DateTime>() { Start, range.Start, End, range.End };
-                list.Sort();
-                return (true, new DateTimeRange(list[1], list[2]));
-            }
-
-            return (false, null);
-        }
-
-        /// <summary>
-        /// 相交时间段
-        /// </summary>
-        /// <param name="start"></param>
-        /// <param name="end"></param>
-        /// <returns></returns>
-        public (bool intersected, DateTimeRange range) Intersect(DateTime start, DateTime end)
-        {
-            return Intersect(new DateTimeRange(start, end));
-        }
-
-        /// <summary>
-        /// 是否包含时间段
-        /// </summary>
-        /// <param name="range"></param>
-        /// <returns></returns>
-        public bool Contains(DateTimeRange range)
-        {
-            return range.Start.In(Start, End) && range.End.In(Start, End);
-        }
-
-        /// <summary>
-        /// 是否包含时间段
-        /// </summary>
-        /// <param name="start"></param>
-        /// <param name="end"></param>
-        /// <returns></returns>
-        public bool Contains(DateTime start, DateTime end)
-        {
-            return Contains(new DateTimeRange(start, end));
-        }
-
-        /// <summary>
-        /// 是否在时间段内
-        /// </summary>
-        /// <param name="range"></param>
-        /// <returns></returns>
-        public bool In(DateTimeRange range)
-        {
-            return Start.In(range.Start, range.End) && End.In(range.Start, range.End);
-        }
-
-        /// <summary>
-        /// 是否在时间段内
-        /// </summary>
-        /// <param name="start"></param>
-        /// <param name="end"></param>
-        /// <returns></returns>
-        public bool In(DateTime start, DateTime end)
-        {
-            return In(new DateTimeRange(start, end));
-        }
-
-        /// <summary>
-        /// 合并时间段
-        /// </summary>
-        /// <param name="range"></param>
-        /// <returns></returns>
-        public DateTimeRange Union(DateTimeRange range)
-        {
-            if (HasIntersect(range))
-            {
-                var list = new List<DateTime>() { Start, range.Start, End, range.End };
-                list.Sort();
-                return new DateTimeRange(list[0], list[3]);
-            }
-
-            throw new Exception("不相交的时间段不能合并");
-        }
-
-        /// <summary>
-        /// 合并时间段
-        /// </summary>
-        /// <param name="start"></param>
-        /// <param name="end"></param>
-        /// <returns></returns>
-        public DateTimeRange Union(DateTime start, DateTime end)
-        {
-            return Union(new DateTimeRange(start, end));
-        }
-
-        /// <summary>返回一个表示当前对象的 string。</summary>
-        /// <returns>表示当前对象的字符串。</returns>
-        public override string ToString()
-        {
-            return $"{Start:yyyy-MM-dd HH:mm:ss}~{End:yyyy-MM-dd HH:mm:ss}";
-        }
-    }
-}
+	/// <summary>
+	/// 时间段
+	/// </summary>
+	public class DateTimeRange : ITimePeriod
+	{
+		public DateTimeRange(DateTime start, DateTime end)
+		{
+			if (start > end)
+			{
+				throw new Exception("开始时间不能大于结束时间");
+			}
+
+			Start = start;
+			End = end;
+		}
+
+		public DateTimeRange()
+		{
+		}
+
+		/// <summary>
+		/// 起始时间
+		/// </summary>
+		public DateTime Start { get; set; }
+
+		/// <summary>
+		/// 结束时间
+		/// </summary>
+		public DateTime End { get; set; }
+
+		/// <summary>
+		/// 是否相交
+		/// </summary>
+		/// <param name="start"></param>
+		/// <param name="end"></param>
+		/// <returns></returns>
+		public bool HasIntersect(DateTime start, DateTime end)
+		{
+			return HasIntersect(new DateTimeRange(start, end));
+		}
+
+		/// <summary>
+		/// 是否相交
+		/// </summary>
+		/// <param name="range"></param>
+		/// <returns></returns>
+		public bool HasIntersect(DateTimeRange range)
+		{
+			return Start.In(range.Start, range.End) || End.In(range.Start, range.End);
+		}
+
+		/// <summary>
+		/// 相交时间段
+		/// </summary>
+		/// <param name="range"></param>
+		/// <returns></returns>
+		public (bool intersected, DateTimeRange range) Intersect(DateTimeRange range)
+		{
+			if (HasIntersect(range.Start, range.End))
+			{
+				var list = new List<DateTime>() { Start, range.Start, End, range.End };
+				list.Sort();
+				return (true, new DateTimeRange(list[1], list[2]));
+			}
+
+			return (false, null);
+		}
+
+		/// <summary>
+		/// 相交时间段
+		/// </summary>
+		/// <param name="start"></param>
+		/// <param name="end"></param>
+		/// <returns></returns>
+		public (bool intersected, DateTimeRange range) Intersect(DateTime start, DateTime end)
+		{
+			return Intersect(new DateTimeRange(start, end));
+		}
+
+		/// <summary>
+		/// 是否包含时间段
+		/// </summary>
+		/// <param name="range"></param>
+		/// <returns></returns>
+		public bool Contains(DateTimeRange range)
+		{
+			return range.Start.In(Start, End) && range.End.In(Start, End);
+		}
+
+		/// <summary>
+		/// 是否包含时间段
+		/// </summary>
+		/// <param name="start"></param>
+		/// <param name="end"></param>
+		/// <returns></returns>
+		public bool Contains(DateTime start, DateTime end)
+		{
+			return Contains(new DateTimeRange(start, end));
+		}
+
+		/// <summary>
+		/// 是否在时间段内
+		/// </summary>
+		/// <param name="range"></param>
+		/// <returns></returns>
+		public bool In(DateTimeRange range)
+		{
+			return Start.In(range.Start, range.End) && End.In(range.Start, range.End);
+		}
+
+		/// <summary>
+		/// 是否在时间段内
+		/// </summary>
+		/// <param name="start"></param>
+		/// <param name="end"></param>
+		/// <returns></returns>
+		public bool In(DateTime start, DateTime end)
+		{
+			return In(new DateTimeRange(start, end));
+		}
+
+		/// <summary>
+		/// 合并时间段
+		/// </summary>
+		/// <param name="range"></param>
+		/// <returns></returns>
+		public DateTimeRange Union(DateTimeRange range)
+		{
+			if (HasIntersect(range))
+			{
+				var list = new List<DateTime>() { Start, range.Start, End, range.End };
+				list.Sort();
+				return new DateTimeRange(list[0], list[3]);
+			}
+
+			throw new Exception("不相交的时间段不能合并");
+		}
+
+		/// <summary>
+		/// 合并时间段
+		/// </summary>
+		/// <param name="start"></param>
+		/// <param name="end"></param>
+		/// <returns></returns>
+		public DateTimeRange Union(DateTime start, DateTime end)
+		{
+			return Union(new DateTimeRange(start, end));
+		}
+
+		/// <summary>返回一个表示当前对象的 string。</summary>
+		/// <returns>表示当前对象的字符串。</returns>
+		public override string ToString()
+		{
+			return $"{Start:yyyy-MM-dd HH:mm:ss}~{End:yyyy-MM-dd HH:mm:ss}";
+		}
+	}
+}

+ 37 - 16
README.md

@@ -495,18 +495,7 @@ var str="html".RemoveHtmlTag(); // 去除html标签
 ...
 ```
 
-### 18.DateTime扩展
-
-```csharp
-double milliseconds = DateTime.Now.GetTotalMilliseconds();// 获取毫秒级时间戳
-double microseconds = DateTime.Now.GetTotalMicroseconds();// 获取微秒级时间戳
-double nanoseconds = DateTime.Now.GetTotalNanoseconds();// 获取纳秒级时间戳
-double seconds = DateTime.Now.GetTotalSeconds();// 获取秒级时间戳
-double minutes = DateTime.Now.GetTotalMinutes();// 获取分钟级时间戳
-...
-```
-
-### 19.IP地址和URL
+### 18.IP地址和URL
 
 ```csharp
 bool inRange = "192.168.2.2".IpAddressInRange("192.168.1.1","192.168.3.255");// 判断IP地址是否在这个地址段里
@@ -519,6 +508,32 @@ PhysicsAddress physicsAddress = "114.114.114.114".GetPhysicsAddressInfo().Result
 Tuple<string, List<string>> ipAddressInfo = "114.114.114.114".GetIPAddressInfo().Result;// 获取详细地理信息集合
 ```
 
+### 19.对象属性值合并
+```csharp
+public class MyClass
+{
+    public string A { get; set; }
+
+    public bool? B { get; set; }
+
+    public int? C { get; set; }
+}
+
+var a = new MyClass()
+{
+    A = "aa"
+};
+var b = new MyClass()
+{
+    B = true
+};
+var c = new MyClass()
+{
+    C = 3
+};
+var merge = a.Merge(b, c); // 合并后对象:A = "aa",B = true,C = 3
+```
+
 ### 20.元素去重
 
 ```csharp
@@ -804,10 +819,13 @@ var mime = mimeMapper.GetMimeFromExtension(".jpg"); // image/jpeg
 ### 32.日期时间扩展
 
 ```csharp
-DateTime.Now.GetTotalSeconds(); // 获取该时间相对于1970-01-01 00:00:00的秒数
-DateTime.Now.GetTotalMilliseconds(); // 获取该时间相对于1970-01-01 00:00:00的毫秒数
-DateTime.Now.GetTotalMicroseconds(); // 获取该时间相对于1970-01-01 00:00:00的微秒数
-DateTime.Now.GetTotalNanoseconds(); // 获取该时间相对于1970-01-01 00:00:00的纳秒数
+
+double milliseconds = DateTime.Now.GetTotalMilliseconds();// 获取毫秒级时间戳
+double microseconds = DateTime.Now.GetTotalMicroseconds();// 获取微秒级时间戳
+double nanoseconds = DateTime.Now.GetTotalNanoseconds();// 获取纳秒级时间戳
+double seconds = DateTime.Now.GetTotalSeconds();// 获取秒级时间戳
+double minutes = DateTime.Now.GetTotalMinutes();// 获取分钟级时间戳
+
 var indate=DateTime.Parse("2020-8-3").In(DateTime.Parse("2020-8-2"),DateTime.Parse("2020-8-4"));//true
 DateTime time="2021-1-1 8:00:00".ToDateTime(); //字符串转DateTime
 
@@ -817,6 +835,9 @@ range.Union(DateTime.Parse("2020-8-4"), DateTime.Parse("2020-8-6")); //连接两
 range.In(DateTime.Parse("2020-8-3"), DateTime.Parse("2020-8-6"));//判断是否在某个时间段内,true
 var (intersected,range2) = range.Intersect(DateTime.Parse("2020-8-4"), DateTime.Parse("2020-8-6"));//两个时间段是否相交,(true,2020-8-3~2020-8-4)
 range.Contains(DateTime.Parse("2020-8-3"), DateTime.Parse("2020-8-4"));//判断是否包含某个时间段,true
+
+range.GetUnionSet(List<DateTimeRange>); // 根据某个时间段查找在某批时间段中的最大并集
+range.GetMaxTimePeriod(List<DateTimeRange>); // 获取一批时间段内存在相互重叠的最大时间段
 ...
 ```