DateTimeHelper.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  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. namespace Masuit.Tools.DateTimeExt
  8. {
  9. /// <summary>
  10. /// 日期操作工具类
  11. /// </summary>
  12. public static class DateTimeHelper
  13. {
  14. /// <summary>
  15. /// 获取某一年有多少周
  16. /// </summary>
  17. /// <param name="_"></param>
  18. /// <param name="year">年份</param>
  19. /// <returns>该年周数</returns>
  20. public static int GetWeekAmount(this DateTime _, int year)
  21. {
  22. var end = new DateTime(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. /// 年 nYear
  50. /// 周数 nNumWeek
  51. /// 周始 out dtWeekStart
  52. /// 周终 out dtWeekeEnd
  53. /// </summary>
  54. /// <param name="_"></param>
  55. /// <param name="nYear">年份</param>
  56. /// <param name="nNumWeek">第几周</param>
  57. /// <param name="dtWeekStart">开始日期</param>
  58. /// <param name="dtWeekeEnd">结束日期</param>
  59. public static void GetWeekTime(this DateTime _, int nYear, int nNumWeek, out DateTime dtWeekStart, out DateTime dtWeekeEnd)
  60. {
  61. var dt = new DateTime(nYear, 1, 1);
  62. dt += new TimeSpan((nNumWeek - 1) * 7, 0, 0, 0);
  63. dtWeekStart = dt.AddDays(-(int)dt.DayOfWeek + (int)DayOfWeek.Monday);
  64. dtWeekeEnd = dt.AddDays((int)DayOfWeek.Saturday - (int)dt.DayOfWeek + 1);
  65. }
  66. /// <summary>
  67. /// 得到一年中的某周的起始日和截止日 周一到周五 工作日
  68. /// </summary>
  69. /// <param name="_"></param>
  70. /// <param name="nYear">年份</param>
  71. /// <param name="nNumWeek">第几周</param>
  72. /// <param name="dtWeekStart">开始日期</param>
  73. /// <param name="dtWeekeEnd">结束日期</param>
  74. public static void GetWeekWorkTime(this DateTime _, int nYear, int nNumWeek, out DateTime dtWeekStart, out DateTime dtWeekeEnd)
  75. {
  76. var dt = new DateTime(nYear, 1, 1);
  77. dt += new TimeSpan((nNumWeek - 1) * 7, 0, 0, 0);
  78. dtWeekStart = dt.AddDays(-(int)dt.DayOfWeek + (int)DayOfWeek.Monday);
  79. dtWeekeEnd = dt.AddDays((int)DayOfWeek.Saturday - (int)dt.DayOfWeek + 1).AddDays(-2);
  80. }
  81. #region P/Invoke 设置本地时间
  82. [DllImport("kernel32.dll")]
  83. private static extern bool SetLocalTime(ref SystemTime time);
  84. [StructLayout(LayoutKind.Sequential)]
  85. private struct SystemTime
  86. {
  87. public short year;
  88. public short month;
  89. public short dayOfWeek;
  90. public short day;
  91. public short hour;
  92. public short minute;
  93. public short second;
  94. public short milliseconds;
  95. }
  96. /// <summary>
  97. /// 设置本地计算机系统时间,仅支持Windows系统
  98. /// </summary>
  99. /// <param name="dt">DateTime对象</param>
  100. public static void SetLocalTime(this in DateTime dt)
  101. {
  102. SystemTime st;
  103. st.year = (short)dt.Year;
  104. st.month = (short)dt.Month;
  105. st.dayOfWeek = (short)dt.DayOfWeek;
  106. st.day = (short)dt.Day;
  107. st.hour = (short)dt.Hour;
  108. st.minute = (short)dt.Minute;
  109. st.second = (short)dt.Second;
  110. st.milliseconds = (short)dt.Millisecond;
  111. SetLocalTime(ref st);
  112. }
  113. #endregion P/Invoke 设置本地时间
  114. /// <summary>
  115. /// 返回相对于当前时间的相对天数
  116. /// </summary>
  117. /// <param name="dt"></param>
  118. /// <param name="relativeday">相对天数</param>
  119. public static string GetDateTime(this in DateTime dt, int relativeday)
  120. {
  121. return dt.AddDays(relativeday).ToString("yyyy-MM-dd HH:mm:ss");
  122. }
  123. /// <summary>
  124. /// 返回标准时间格式string
  125. /// </summary>
  126. public static string GetDateTimeF(this in DateTime dt) => dt.ToString("yyyy-MM-dd HH:mm:ss:fffffff");
  127. /// <summary>
  128. /// 获取该时间相对于1970-01-01T00:00:00Z的秒数
  129. /// </summary>
  130. /// <param name="dt"></param>
  131. /// <returns></returns>
  132. public static long GetTotalSeconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10_000_000L - 62135596800L;
  133. /// <summary>
  134. /// 获取该时间相对于1970-01-01T00:00:00Z的毫秒数
  135. /// </summary>
  136. /// <param name="dt"></param>
  137. /// <returns></returns>
  138. public static long GetTotalMilliseconds(this in DateTime dt) => new DateTimeOffset(dt).UtcDateTime.Ticks / 10000L - 62135596800000L;
  139. /// <summary>
  140. /// 获取该时间相对于1970-01-01T00:00:00Z的微秒时间戳
  141. /// </summary>
  142. /// <param name="dt"></param>
  143. /// <returns></returns>
  144. public static long GetTotalMicroseconds(this in DateTime dt) => (new DateTimeOffset(dt).UtcTicks - 621355968000000000) / 10;
  145. [DllImport("Kernel32.dll")]
  146. private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
  147. /// <summary>
  148. /// 获取该时间相对于1970-01-01T00:00:00Z的纳秒时间戳
  149. /// </summary>
  150. /// <param name="dt"></param>
  151. /// <returns></returns>
  152. public static long GetTotalNanoseconds(this in DateTime dt)
  153. {
  154. var ticks = (new DateTimeOffset(dt).UtcTicks - 621355968000000000) * 100;
  155. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  156. {
  157. QueryPerformanceCounter(out var timestamp);
  158. return ticks + timestamp % 100;
  159. }
  160. return ticks + Stopwatch.GetTimestamp() % 100;
  161. }
  162. /// <summary>
  163. /// 获取该时间相对于1970-01-01T00:00:00Z的分钟数
  164. /// </summary>
  165. /// <param name="dt"></param>
  166. /// <returns></returns>
  167. public static double GetTotalMinutes(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalMinutes;
  168. /// <summary>
  169. /// 获取该时间相对于1970-01-01T00:00:00Z的小时数
  170. /// </summary>
  171. /// <param name="dt"></param>
  172. /// <returns></returns>
  173. public static double GetTotalHours(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalHours;
  174. /// <summary>
  175. /// 获取该时间相对于1970-01-01T00:00:00Z的天数
  176. /// </summary>
  177. /// <param name="dt"></param>
  178. /// <returns></returns>
  179. public static double GetTotalDays(this in DateTime dt) => new DateTimeOffset(dt).Offset.TotalDays;
  180. /// <summary>
  181. /// 返回本年有多少天
  182. /// </summary>
  183. /// <param name="_"></param>
  184. /// <param name="iYear">年份</param>
  185. /// <returns>本年的天数</returns>
  186. public static int GetDaysOfYear(this DateTime _, int iYear)
  187. {
  188. return IsRuYear(iYear) ? 366 : 365;
  189. }
  190. /// <summary>本年有多少天</summary>
  191. /// <param name="dt">日期</param>
  192. /// <returns>本天在当年的天数</returns>
  193. public static int GetDaysOfYear(this in DateTime dt)
  194. {
  195. //取得传入参数的年份部分,用来判断是否是闰年
  196. int n = dt.Year;
  197. return IsRuYear(n) ? 366 : 365;
  198. }
  199. /// <summary>本月有多少天</summary>
  200. /// <param name="_"></param>
  201. /// <param name="iYear">年</param>
  202. /// <param name="month">月</param>
  203. /// <returns>天数</returns>
  204. public static int GetDaysOfMonth(this DateTime _, int iYear, int month)
  205. {
  206. return month switch
  207. {
  208. 1 => 31,
  209. 2 => (IsRuYear(iYear) ? 29 : 28),
  210. 3 => 31,
  211. 4 => 30,
  212. 5 => 31,
  213. 6 => 30,
  214. 7 => 31,
  215. 8 => 31,
  216. 9 => 30,
  217. 10 => 31,
  218. 11 => 30,
  219. 12 => 31,
  220. _ => 0
  221. };
  222. }
  223. /// <summary>本月有多少天</summary>
  224. /// <param name="dt">日期</param>
  225. /// <returns>天数</returns>
  226. public static int GetDaysOfMonth(this in DateTime dt)
  227. {
  228. //--利用年月信息,得到当前月的天数信息。
  229. return dt.Month switch
  230. {
  231. 1 => 31,
  232. 2 => (IsRuYear(dt.Year) ? 29 : 28),
  233. 3 => 31,
  234. 4 => 30,
  235. 5 => 31,
  236. 6 => 30,
  237. 7 => 31,
  238. 8 => 31,
  239. 9 => 30,
  240. 10 => 31,
  241. 11 => 30,
  242. 12 => 31,
  243. _ => 0
  244. };
  245. }
  246. /// <summary>返回当前日期的星期名称</summary>
  247. /// <param name="idt">日期</param>
  248. /// <returns>星期名称</returns>
  249. public static string GetWeekNameOfDay(this in DateTime idt)
  250. {
  251. return idt.DayOfWeek switch
  252. {
  253. DayOfWeek.Monday => "星期一",
  254. DayOfWeek.Tuesday => "星期二",
  255. DayOfWeek.Wednesday => "星期三",
  256. DayOfWeek.Thursday => "星期四",
  257. DayOfWeek.Friday => "星期五",
  258. DayOfWeek.Saturday => "星期六",
  259. DayOfWeek.Sunday => "星期日",
  260. _ => ""
  261. };
  262. }
  263. /// <summary>判断当前年份是否是闰年,私有函数</summary>
  264. /// <param name="iYear">年份</param>
  265. /// <returns>是闰年:True ,不是闰年:False</returns>
  266. private static bool IsRuYear(int iYear)
  267. {
  268. //形式参数为年份
  269. //例如:2003
  270. var n = iYear;
  271. return n % 400 == 0 || n % 4 == 0 && n % 100 != 0;
  272. }
  273. /// <summary>
  274. /// 判断是否为合法日期,必须大于1800年1月1日
  275. /// </summary>
  276. /// <param name="strDate">输入日期字符串</param>
  277. /// <returns>True/False</returns>
  278. public static bool IsDateTime(this string strDate)
  279. {
  280. DateTime.TryParse(strDate, out var result);
  281. return result.CompareTo(DateTime.Parse("1800-1-1")) > 0;
  282. }
  283. /// <summary>
  284. /// 判断时间是否在区间内
  285. /// </summary>
  286. /// <param name="this"></param>
  287. /// <param name="start">开始</param>
  288. /// <param name="end">结束</param>
  289. /// <param name="mode">模式</param>
  290. /// <returns></returns>
  291. public static bool In(this in DateTime @this, DateTime start, DateTime end, RangeMode mode = RangeMode.Close)
  292. {
  293. return mode switch
  294. {
  295. RangeMode.Open => start < @this && end > @this,
  296. RangeMode.Close => start <= @this && end >= @this,
  297. RangeMode.OpenClose => start < @this && end >= @this,
  298. RangeMode.CloseOpen => start <= @this && end > @this,
  299. _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null)
  300. };
  301. }
  302. /// <summary>
  303. /// 返回每月的第一天和最后一天
  304. /// </summary>
  305. /// <param name="_"></param>
  306. /// <param name="month">月份</param>
  307. /// <param name="firstDay">第一天</param>
  308. /// <param name="lastDay">最后一天</param>
  309. public static void ReturnDateFormat(this DateTime _, int month, out string firstDay, out string lastDay)
  310. {
  311. int year = DateTime.Now.Year + month / 12;
  312. if (month != 12)
  313. {
  314. month %= 12;
  315. }
  316. switch (month)
  317. {
  318. case 1:
  319. firstDay = DateTime.Now.ToString($"{year}-0{month}-01");
  320. lastDay = DateTime.Now.ToString($"{year}-0{month}-31");
  321. break;
  322. case 2:
  323. firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
  324. lastDay = DateTime.IsLeapYear(DateTime.Now.Year) ? DateTime.Now.ToString(year + "-0" + month + "-29") : DateTime.Now.ToString(year + "-0" + month + "-28");
  325. break;
  326. case 3:
  327. firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
  328. lastDay = DateTime.Now.ToString("yyyy-0" + month + "-31");
  329. break;
  330. case 4:
  331. firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
  332. lastDay = DateTime.Now.ToString(year + "-0" + month + "-30");
  333. break;
  334. case 5:
  335. firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
  336. lastDay = DateTime.Now.ToString(year + "-0" + month + "-31");
  337. break;
  338. case 6:
  339. firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
  340. lastDay = DateTime.Now.ToString(year + "-0" + month + "-30");
  341. break;
  342. case 7:
  343. firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
  344. lastDay = DateTime.Now.ToString(year + "-0" + month + "-31");
  345. break;
  346. case 8:
  347. firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
  348. lastDay = DateTime.Now.ToString(year + "-0" + month + "-31");
  349. break;
  350. case 9:
  351. firstDay = DateTime.Now.ToString(year + "-0" + month + "-01");
  352. lastDay = DateTime.Now.ToString(year + "-0" + month + "-30");
  353. break;
  354. case 10:
  355. firstDay = DateTime.Now.ToString(year + "-" + month + "-01");
  356. lastDay = DateTime.Now.ToString(year + "-" + month + "-31");
  357. break;
  358. case 11:
  359. firstDay = DateTime.Now.ToString(year + "-" + month + "-01");
  360. lastDay = DateTime.Now.ToString(year + "-" + month + "-30");
  361. break;
  362. default:
  363. firstDay = DateTime.Now.ToString(year + "-" + month + "-01");
  364. lastDay = DateTime.Now.ToString(year + "-" + month + "-31");
  365. break;
  366. }
  367. }
  368. /// <summary>
  369. /// 返回某年某月最后一天
  370. /// </summary>
  371. /// <param name="_"></param>
  372. /// <param name="year">年份</param>
  373. /// <param name="month">月份</param>
  374. /// <returns>日</returns>
  375. public static int GetMonthLastDate(this DateTime _, int year, int month)
  376. {
  377. DateTime lastDay = new DateTime(year, month, new GregorianCalendar().GetDaysInMonth(year, month));
  378. int day = lastDay.Day;
  379. return day;
  380. }
  381. /// <summary>
  382. /// 获得一段时间内有多少小时
  383. /// </summary>
  384. /// <param name="dtStar">起始时间</param>
  385. /// <param name="dtEnd">终止时间</param>
  386. /// <returns>小时差</returns>
  387. public static string GetTimeDelay(this in DateTime dtStar, DateTime dtEnd)
  388. {
  389. long lTicks = (dtEnd.Ticks - dtStar.Ticks) / 10000000;
  390. string sTemp = (lTicks / 3600).ToString().PadLeft(2, '0') + ":";
  391. sTemp += (lTicks % 3600 / 60).ToString().PadLeft(2, '0') + ":";
  392. sTemp += (lTicks % 3600 % 60).ToString().PadLeft(2, '0');
  393. return sTemp;
  394. }
  395. /// <summary>
  396. /// 获得8位时间整型数字
  397. /// </summary>
  398. /// <param name="dt">当前的日期时间对象</param>
  399. /// <returns>8位时间整型数字</returns>
  400. public static string GetDateString(this in DateTime dt)
  401. {
  402. return dt.Year + dt.Month.ToString().PadLeft(2, '0') + dt.Day.ToString().PadLeft(2, '0');
  403. }
  404. /// <summary>
  405. /// 返回时间差
  406. /// </summary>
  407. /// <param name="dateTime1">时间1</param>
  408. /// <param name="dateTime2">时间2</param>
  409. /// <returns>时间差</returns>
  410. public static string DateDiff(this in DateTime dateTime1, in DateTime dateTime2)
  411. {
  412. string dateDiff;
  413. var ts = dateTime2 - dateTime1;
  414. if (ts.Days >= 1)
  415. {
  416. dateDiff = dateTime1.Month + "月" + dateTime1.Day + "日";
  417. }
  418. else
  419. {
  420. dateDiff = ts.Hours > 1 ? ts.Hours + "小时前" : ts.Minutes + "分钟前";
  421. }
  422. return dateDiff;
  423. }
  424. /// <summary>
  425. /// 计算2个时间差
  426. /// </summary>
  427. /// <param name="beginTime">开始时间</param>
  428. /// <param name="endTime">结束时间</param>
  429. /// <returns>时间差</returns>
  430. public static string GetDiffTime(this in DateTime beginTime, in DateTime endTime)
  431. {
  432. string strResout = string.Empty;
  433. //获得2时间的时间间隔秒计算
  434. TimeSpan span = endTime.Subtract(beginTime);
  435. int sec = Convert.ToInt32(span.TotalSeconds);
  436. int minutes = 1 * 60;
  437. int hours = minutes * 60;
  438. int day = hours * 24;
  439. int month = day * 30;
  440. int year = month * 12;
  441. //提醒时间,到了返回1,否则返回0
  442. if (sec > year)
  443. {
  444. strResout += sec / year + "年";
  445. sec %= year; //剩余
  446. }
  447. if (sec > month)
  448. {
  449. strResout += sec / month + "月";
  450. sec %= month;
  451. }
  452. if (sec > day)
  453. {
  454. strResout += sec / day + "天";
  455. sec %= day;
  456. }
  457. if (sec > hours)
  458. {
  459. strResout += sec / hours + "小时";
  460. sec %= hours;
  461. }
  462. if (sec > minutes)
  463. {
  464. strResout += sec / minutes + "分";
  465. sec %= minutes;
  466. }
  467. strResout += sec + "秒";
  468. return strResout;
  469. }
  470. /// <summary>
  471. /// 根据某个时间段查找在某批时间段中的最大并集
  472. /// </summary>
  473. /// <param name="destination"></param>
  474. /// <param name="sources"></param>
  475. /// <typeparam name="T"></typeparam>
  476. /// <returns></returns>
  477. public static ICollection<T> GetUnionSet<T>(this T destination, List<T> sources) where T : ITimePeriod, new()
  478. {
  479. var result = true;
  480. ICollection<T> frames = new List<T>();
  481. var timeFrames = sources.Where(frame =>
  482. !(destination.Start > frame.End || destination.End < frame.Start)).ToList();
  483. if (timeFrames.Any())
  484. foreach (var frame in timeFrames)
  485. {
  486. frames.Add(frame);
  487. sources.Remove(frame);
  488. }
  489. if (!frames.Any()) return frames;
  490. var timePeriod = new T()
  491. {
  492. End = frames.OrderBy(frame => frame.End).Max(frame => frame.End),
  493. Start = frames.OrderBy(frame => frame.Start).Min(frame => frame.Start)
  494. };
  495. while (result)
  496. {
  497. var maxTimeFrame = GetUnionSet<T>(timePeriod, sources);
  498. if (!maxTimeFrame.Any())
  499. result = false;
  500. else
  501. foreach (var frame in maxTimeFrame)
  502. frames.Add(frame);
  503. }
  504. return frames;
  505. }
  506. /// <summary>
  507. /// 获取一批时间段内存在相互重叠的最大时间段
  508. /// </summary>
  509. /// <param name="destination">基础时间段</param>
  510. /// <param name="sources">一批时间段</param>
  511. /// <typeparam name="T"></typeparam>
  512. /// <returns></returns>
  513. /// <remarks>源数据sources 会受到影响</remarks>
  514. public static T GetMaxTimePeriod<T>(this T destination, List<T> sources) where T : ITimePeriod, new()
  515. {
  516. var list = sources.Select(period => new T()
  517. {
  518. End = period.End,
  519. Start = period.Start,
  520. }).ToList();
  521. var timePeriods = GetUnionSet(destination, list);
  522. return new T()
  523. {
  524. End = timePeriods.OrderBy(period => period.End).Max(period => period.End),
  525. Start = timePeriods.OrderBy(period => period.Start).Min(period => period.Start)
  526. };
  527. }
  528. }
  529. public interface ITimePeriod
  530. {
  531. /// <summary>
  532. /// 起始时间
  533. /// </summary>
  534. public DateTime Start { get; set; }
  535. /// <summary>
  536. /// 终止时间
  537. /// </summary>
  538. public DateTime End { get; set; }
  539. }
  540. /// <summary>
  541. /// 区间模式
  542. /// </summary>
  543. public enum RangeMode
  544. {
  545. /// <summary>
  546. /// 开区间
  547. /// </summary>
  548. Open,
  549. /// <summary>
  550. /// 闭区间
  551. /// </summary>
  552. Close,
  553. /// <summary>
  554. /// 左开右闭区间
  555. /// </summary>
  556. OpenClose,
  557. /// <summary>
  558. /// 左闭右开区间
  559. /// </summary>
  560. CloseOpen
  561. }
  562. }