DateTimeHelper.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Globalization;
  5. using System.Linq;
  6. using System.Runtime.InteropServices;
  7. using Masuit.Tools.Models;
  8. namespace Masuit.Tools.DateTimeExt
  9. {
  10. /// <summary>
  11. /// 日期操作工具类
  12. /// </summary>
  13. public static class DateTimeHelper
  14. {
  15. /// <summary>
  16. /// 获取某一年有多少周
  17. /// </summary>
  18. /// <param name="now"></param>
  19. /// <returns>该年周数</returns>
  20. public static int GetWeekAmount(this in DateTime now)
  21. {
  22. var end = new DateTime(now.Year, 12, 31); //该年最后一天
  23. var gc = new GregorianCalendar();
  24. return gc.GetWeekOfYear(end, CalendarWeekRule.FirstDay, DayOfWeek.Monday); //该年星期数
  25. }
  26. /// <summary>
  27. /// 返回年度第几个星期 默认星期日是第一天
  28. /// </summary>
  29. /// <param name="date">时间</param>
  30. /// <returns>第几周</returns>
  31. public static int WeekOfYear(this in DateTime date)
  32. {
  33. var gc = new GregorianCalendar();
  34. return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
  35. }
  36. /// <summary>
  37. /// 返回年度第几个星期
  38. /// </summary>
  39. /// <param name="date">时间</param>
  40. /// <param name="week">一周的开始日期</param>
  41. /// <returns>第几周</returns>
  42. public static int WeekOfYear(this in DateTime date, DayOfWeek week)
  43. {
  44. var gc = new GregorianCalendar();
  45. return gc.GetWeekOfYear(date, CalendarWeekRule.FirstDay, week);
  46. }
  47. /// <summary>
  48. /// 得到一年中的某周的起始日和截止日
  49. /// 周数 nNumWeek
  50. /// </summary>
  51. /// <param name="now"></param>
  52. /// <param name="nNumWeek">第几周</param>
  53. public static DateTimeRange GetWeekTime(this DateTime now, int nNumWeek)
  54. {
  55. var dt = new DateTime(now.Year, 1, 1);
  56. dt += new TimeSpan((nNumWeek - 1) * 7, 0, 0, 0);
  57. return new DateTimeRange(dt.AddDays(-(int)dt.DayOfWeek + (int)DayOfWeek.Monday), dt.AddDays((int)DayOfWeek.Saturday - (int)dt.DayOfWeek + 1));
  58. }
  59. #region P/Invoke 设置本地时间
  60. [DllImport("kernel32.dll")]
  61. private static extern bool SetLocalTime(ref SystemTime time);
  62. [StructLayout(LayoutKind.Sequential)]
  63. private record struct SystemTime
  64. {
  65. public short year;
  66. public short month;
  67. public short dayOfWeek;
  68. public short day;
  69. public short hour;
  70. public short minute;
  71. public short second;
  72. public short milliseconds;
  73. }
  74. /// <summary>
  75. /// 设置本地计算机系统时间,仅支持Windows系统
  76. /// </summary>
  77. /// <param name="dt">DateTime对象</param>
  78. public static void SetLocalTime(this in DateTime dt)
  79. {
  80. SystemTime st;
  81. st.year = (short)dt.Year;
  82. st.month = (short)dt.Month;
  83. st.dayOfWeek = (short)dt.DayOfWeek;
  84. st.day = (short)dt.Day;
  85. st.hour = (short)dt.Hour;
  86. st.minute = (short)dt.Minute;
  87. st.second = (short)dt.Second;
  88. st.milliseconds = (short)dt.Millisecond;
  89. SetLocalTime(ref st);
  90. }
  91. #endregion P/Invoke 设置本地时间
  92. /// <summary>
  93. /// 返回相对于当前时间的相对天数
  94. /// </summary>
  95. /// <param name="dt"></param>
  96. /// <param name="relativeday">相对天数</param>
  97. public static string GetDateTime(this in DateTime dt, int relativeday)
  98. {
  99. return dt.AddDays(relativeday).ToString("yyyy-MM-dd HH:mm:ss");
  100. }
  101. /// <summary>
  102. /// 获取该时间相对于1970-01-01T00:00:00Z的秒数
  103. /// </summary>
  104. /// <param name="dt"></param>
  105. /// <returns></returns>
  106. public static long GetTotalSeconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10_000_000L - 62135596800L;
  107. /// <summary>
  108. /// 获取该时间相对于1970-01-01T00:00:00Z的毫秒数
  109. /// </summary>
  110. /// <param name="dt"></param>
  111. /// <returns></returns>
  112. public static long GetTotalMilliseconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10000L - 62135596800000L;
  113. /// <summary>
  114. /// 获取该时间相对于1970-01-01T00:00:00Z的微秒时间戳
  115. /// </summary>
  116. /// <param name="dt"></param>
  117. /// <returns></returns>
  118. public static long GetTotalMicroseconds(this in DateTime dt) => (new DateTimeOffset(dt).UtcTicks - 621355968000000000) / 10;
  119. [DllImport("Kernel32.dll")]
  120. private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
  121. /// <summary>
  122. /// 获取该时间相对于1970-01-01T00:00:00Z的纳秒时间戳
  123. /// </summary>
  124. /// <param name="dt"></param>
  125. /// <returns></returns>
  126. public static long GetTotalNanoseconds(this in DateTime dt)
  127. {
  128. var ticks = (new DateTimeOffset(dt).UtcTicks - 621355968000000000) * 100;
  129. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  130. {
  131. QueryPerformanceCounter(out var timestamp);
  132. return ticks + timestamp % 100;
  133. }
  134. return ticks + Stopwatch.GetTimestamp() % 100;
  135. }
  136. /// <summary>
  137. /// 获取该时间相对于1970-01-01T00:00:00Z的分钟数
  138. /// </summary>
  139. /// <param name="dt"></param>
  140. /// <returns></returns>
  141. public static double GetTotalMinutes(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalMinutes;
  142. /// <summary>
  143. /// 获取该时间相对于1970-01-01T00:00:00Z的小时数
  144. /// </summary>
  145. /// <param name="dt"></param>
  146. /// <returns></returns>
  147. public static double GetTotalHours(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalHours;
  148. /// <summary>
  149. /// 获取该时间相对于1970-01-01T00:00:00Z的天数
  150. /// </summary>
  151. /// <param name="dt"></param>
  152. /// <returns></returns>
  153. public static double GetTotalDays(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalDays;
  154. /// <summary>本年有多少天</summary>
  155. /// <param name="dt">日期</param>
  156. /// <returns>本天在当年的天数</returns>
  157. public static int GetDaysOfYear(this in DateTime dt)
  158. {
  159. //取得传入参数的年份部分,用来判断是否是闰年
  160. int n = dt.Year;
  161. return DateTime.IsLeapYear(n) ? 366 : 365;
  162. }
  163. /// <summary>本月有多少天</summary>
  164. /// <param name="now"></param>
  165. /// <returns>天数</returns>
  166. public static int GetDaysOfMonth(this DateTime now)
  167. {
  168. return now.Month switch
  169. {
  170. 1 => 31,
  171. 2 => DateTime.IsLeapYear(now.Year) ? 29 : 28,
  172. 3 => 31,
  173. 4 => 30,
  174. 5 => 31,
  175. 6 => 30,
  176. 7 => 31,
  177. 8 => 31,
  178. 9 => 30,
  179. 10 => 31,
  180. 11 => 30,
  181. 12 => 31,
  182. _ => 0
  183. };
  184. }
  185. /// <summary>返回当前日期的星期名称</summary>
  186. /// <param name="now">日期</param>
  187. /// <returns>星期名称</returns>
  188. public static string GetWeekNameOfDay(this in DateTime now)
  189. {
  190. return now.DayOfWeek switch
  191. {
  192. DayOfWeek.Monday => "星期一",
  193. DayOfWeek.Tuesday => "星期二",
  194. DayOfWeek.Wednesday => "星期三",
  195. DayOfWeek.Thursday => "星期四",
  196. DayOfWeek.Friday => "星期五",
  197. DayOfWeek.Saturday => "星期六",
  198. DayOfWeek.Sunday => "星期日",
  199. _ => ""
  200. };
  201. }
  202. /// <summary>
  203. /// 判断时间是否在区间内
  204. /// </summary>
  205. /// <param name="this"></param>
  206. /// <param name="start">开始</param>
  207. /// <param name="end">结束</param>
  208. /// <param name="mode">模式</param>
  209. /// <returns></returns>
  210. public static bool In(this in DateTime @this, DateTime start, DateTime end, RangeMode mode = RangeMode.Close)
  211. {
  212. return mode switch
  213. {
  214. RangeMode.Open => start < @this && end > @this,
  215. RangeMode.Close => start <= @this && end >= @this,
  216. RangeMode.OpenClose => start < @this && end >= @this,
  217. RangeMode.CloseOpen => start <= @this && end > @this,
  218. _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
  219. };
  220. }
  221. /// <summary>
  222. /// 返回某年某月最后一天
  223. /// </summary>
  224. /// <param name="now"></param>
  225. /// <returns>日</returns>
  226. public static int GetMonthLastDate(this DateTime now)
  227. {
  228. DateTime lastDay = new DateTime(now.Year, now.Month, new GregorianCalendar().GetDaysInMonth(now.Year, now.Month));
  229. return lastDay.Day;
  230. }
  231. /// <summary>
  232. /// 获得一段时间内有多少小时
  233. /// </summary>
  234. /// <param name="start">起始时间</param>
  235. /// <param name="end">终止时间</param>
  236. /// <returns>小时差</returns>
  237. public static string GetTimeDelay(this in DateTime start, DateTime end)
  238. {
  239. return (end - start).ToString("c");
  240. }
  241. /// <summary>
  242. /// 返回时间差
  243. /// </summary>
  244. /// <param name="dateTime1">时间1</param>
  245. /// <param name="dateTime2">时间2</param>
  246. /// <returns>时间差</returns>
  247. public static string DateDiff(this in DateTime dateTime1, in DateTime dateTime2)
  248. {
  249. string dateDiff;
  250. var ts = dateTime2 - dateTime1;
  251. if (ts.TotalDays >= 1)
  252. {
  253. dateDiff = ts.TotalDays >= 30 ? (ts.TotalDays / 30) + "个月前" : ts.TotalDays + "天前";
  254. }
  255. else
  256. {
  257. dateDiff = ts.Hours > 1 ? ts.Hours + "小时前" : ts.Minutes + "分钟前";
  258. }
  259. return dateDiff;
  260. }
  261. /// <summary>
  262. /// 计算2个时间差
  263. /// </summary>
  264. /// <param name="beginTime">开始时间</param>
  265. /// <param name="endTime">结束时间</param>
  266. /// <returns>时间差</returns>
  267. public static string GetDiffTime(this in DateTime beginTime, in DateTime endTime)
  268. {
  269. string strResout = string.Empty;
  270. //获得2时间的时间间隔秒计算
  271. TimeSpan span = endTime.Subtract(beginTime);
  272. if (span.Days >= 365)
  273. {
  274. strResout += span.Days / 365 + "年";
  275. }
  276. if (span.Days >= 30)
  277. {
  278. strResout += span.Days % 365 / 30 + "个月";
  279. }
  280. if (span.Days >= 1)
  281. {
  282. strResout += (int)(span.TotalDays % 30.42) + "天";
  283. }
  284. if (span.Hours >= 1)
  285. {
  286. strResout += span.Hours + "小时";
  287. }
  288. if (span.Minutes >= 1)
  289. {
  290. strResout += span.Minutes + "分钟";
  291. }
  292. if (span.Seconds >= 1)
  293. {
  294. strResout += span.Seconds + "秒";
  295. }
  296. return strResout;
  297. }
  298. /// <summary>
  299. /// 根据某个时间段查找在某批时间段中的最大并集
  300. /// </summary>
  301. /// <param name="destination"></param>
  302. /// <param name="sources"></param>
  303. /// <typeparam name="T"></typeparam>
  304. /// <returns></returns>
  305. public static ICollection<T> GetUnionSet<T>(this T destination, List<T> sources) where T : ITimePeriod, new()
  306. {
  307. var result = true;
  308. ICollection<T> frames = new List<T>();
  309. var timeFrames = sources.Where(frame =>
  310. !(destination.Start > frame.End || destination.End < frame.Start)).ToList();
  311. if (timeFrames.Any())
  312. foreach (var frame in timeFrames)
  313. {
  314. frames.Add(frame);
  315. sources.Remove(frame);
  316. }
  317. if (!frames.Any()) return frames;
  318. var timePeriod = new T()
  319. {
  320. End = frames.OrderBy(frame => frame.End).Max(frame => frame.End),
  321. Start = frames.OrderBy(frame => frame.Start).Min(frame => frame.Start)
  322. };
  323. while (result)
  324. {
  325. var maxTimeFrame = GetUnionSet<T>(timePeriod, sources);
  326. if (!maxTimeFrame.Any())
  327. result = false;
  328. else
  329. foreach (var frame in maxTimeFrame)
  330. frames.Add(frame);
  331. }
  332. return frames;
  333. }
  334. /// <summary>
  335. /// 获取一批时间段内存在相互重叠的最大时间段
  336. /// </summary>
  337. /// <param name="destination">基础时间段</param>
  338. /// <param name="sources">一批时间段</param>
  339. /// <typeparam name="T"></typeparam>
  340. /// <returns></returns>
  341. /// <remarks>源数据sources 会受到影响</remarks>
  342. public static T GetMaxTimePeriod<T>(this T destination, List<T> sources) where T : ITimePeriod, new()
  343. {
  344. var list = sources.Select(period => new T()
  345. {
  346. End = period.End,
  347. Start = period.Start,
  348. }).ToList();
  349. var timePeriods = GetUnionSet(destination, list);
  350. return new T()
  351. {
  352. End = timePeriods.OrderBy(period => period.End).Max(period => period.End),
  353. Start = timePeriods.OrderBy(period => period.Start).Min(period => period.Start)
  354. };
  355. }
  356. }
  357. /// <summary>
  358. ///
  359. /// </summary>
  360. public interface ITimePeriod
  361. {
  362. /// <summary>
  363. /// 起始时间
  364. /// </summary>
  365. public DateTime Start { get; set; }
  366. /// <summary>
  367. /// 终止时间
  368. /// </summary>
  369. public DateTime End { get; set; }
  370. }
  371. /// <summary>
  372. /// 区间模式
  373. /// </summary>
  374. public enum RangeMode
  375. {
  376. /// <summary>
  377. /// 开区间
  378. /// </summary>
  379. Open,
  380. /// <summary>
  381. /// 闭区间
  382. /// </summary>
  383. Close,
  384. /// <summary>
  385. /// 左开右闭区间
  386. /// </summary>
  387. OpenClose,
  388. /// <summary>
  389. /// 左闭右开区间
  390. /// </summary>
  391. CloseOpen
  392. }
  393. }