using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; namespace Masuit.Tools.DateTimeExt { /// /// 日期操作工具类 /// public static class DateTimeHelper { /// /// 获取某一年有多少周 /// /// /// 年份 /// 该年周数 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); //该年星期数 } /// /// 返回年度第几个星期 默认星期日是第一天 /// /// 时间 /// 第几周 public static int WeekOfYear(this in DateTime date) { var gc = new GregorianCalendar(); return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday); } /// /// 返回年度第几个星期 /// /// 时间 /// 一周的开始日期 /// 第几周 public static int WeekOfYear(this in DateTime date, DayOfWeek week) { var gc = new GregorianCalendar(); return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, week); } /// /// 得到一年中的某周的起始日和截止日 /// 年 nYear /// 周数 nNumWeek /// 周始 out dtWeekStart /// 周终 out dtWeekeEnd /// /// /// 年份 /// 第几周 /// 开始日期 /// 结束日期 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); } /// /// 得到一年中的某周的起始日和截止日 周一到周五 工作日 /// /// /// 年份 /// 第几周 /// 开始日期 /// 结束日期 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; } /// /// 设置本地计算机系统时间,仅支持Windows系统 /// /// DateTime对象 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 设置本地时间 /// /// 返回相对于当前时间的相对天数 /// /// /// 相对天数 public static string GetDateTime(this in DateTime dt, int relativeday) { return dt.AddDays(relativeday).ToString("yyyy-MM-dd HH:mm:ss"); } /// /// 返回标准时间格式string /// public static string GetDateTimeF(this in DateTime dt) => dt.ToString("yyyy-MM-dd HH:mm:ss:fffffff"); /// /// 获取该时间相对于1970-01-01T00:00:00Z的秒数 /// /// /// public static long GetTotalSeconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10_000_000L - 62135596800L; /// /// 获取该时间相对于1970-01-01T00:00:00Z的毫秒数 /// /// /// public static long GetTotalMilliseconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10000L - 62135596800000L; /// /// 获取该时间相对于1970-01-01T00:00:00Z的微秒时间戳 /// /// /// 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); /// /// 获取该时间相对于1970-01-01T00:00:00Z的纳秒时间戳 /// /// /// 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; } /// /// 获取该时间相对于1970-01-01T00:00:00Z的分钟数 /// /// /// public static double GetTotalMinutes(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalMinutes; /// /// 获取该时间相对于1970-01-01T00:00:00Z的小时数 /// /// /// public static double GetTotalHours(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalHours; /// /// 获取该时间相对于1970-01-01T00:00:00Z的天数 /// /// /// public static double GetTotalDays(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalDays; /// /// 返回本年有多少天 /// /// /// 年份 /// 本年的天数 public static int GetDaysOfYear(this DateTime _, int iYear) { return IsRuYear(iYear) ? 366 : 365; } /// 本年有多少天 /// 日期 /// 本天在当年的天数 public static int GetDaysOfYear(this in DateTime dt) { //取得传入参数的年份部分,用来判断是否是闰年 int n = dt.Year; return IsRuYear(n) ? 366 : 365; } /// 本月有多少天 /// /// 年 /// 月 /// 天数 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 }; } /// 本月有多少天 /// 日期 /// 天数 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 }; } /// 返回当前日期的星期名称 /// 日期 /// 星期名称 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 => "星期日", _ => "" }; } /// 判断当前年份是否是闰年,私有函数 /// 年份 /// 是闰年:True ,不是闰年:False private static bool IsRuYear(int iYear) { //形式参数为年份 //例如:2003 var n = iYear; return n % 400 == 0 || n % 4 == 0 && n % 100 != 0; } /// /// 判断是否为合法日期,必须大于1800年1月1日 /// /// 输入日期字符串 /// True/False public static bool IsDateTime(this string strDate) { DateTime.TryParse(strDate, out var result); return result.CompareTo(DateTime.Parse("1800-1-1")) > 0; } /// /// 判断时间是否在区间内 /// /// /// 开始 /// 结束 /// 模式 /// 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) }; } /// /// 返回每月的第一天和最后一天 /// /// /// 月份 /// 第一天 /// 最后一天 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; } } /// /// 返回某年某月最后一天 /// /// /// 年份 /// 月份 /// 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; } /// /// 获得一段时间内有多少小时 /// /// 起始时间 /// 终止时间 /// 小时差 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; } /// /// 获得8位时间整型数字 /// /// 当前的日期时间对象 /// 8位时间整型数字 public static string GetDateString(this in DateTime dt) { return dt.Year + dt.Month.ToString().PadLeft(2, '0') + dt.Day.ToString().PadLeft(2, '0'); } /// /// 返回时间差 /// /// 时间1 /// 时间2 /// 时间差 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; } /// /// 计算2个时间差 /// /// 开始时间 /// 结束时间 /// 时间差 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; } /// /// 根据某个时间段查找在某批时间段中的最大并集 /// /// /// /// /// public static ICollection GetUnionSet(this T destination, List sources) where T : ITimePeriod, new() { var result = true; ICollection frames = new List(); 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(timePeriod, sources); if (!maxTimeFrame.Any()) result = false; else foreach (var frame in maxTimeFrame) frames.Add(frame); } return frames; } /// /// 获取一批时间段内存在相互重叠的最大时间段 /// /// 基础时间段 /// 一批时间段 /// /// /// 源数据sources 会受到影响 public static T GetMaxTimePeriod(this T destination, List 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 { /// /// 起始时间 /// public DateTime Start { get; set; } /// /// 终止时间 /// public DateTime End { get; set; } } /// /// 区间模式 /// public enum RangeMode { /// /// 开区间 /// Open, /// /// 闭区间 /// Close, /// /// 左开右闭区间 /// OpenClose, /// /// 左闭右开区间 /// CloseOpen } }