You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
11 KiB

  1. using Apewer.Internals;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Text;
  6. namespace Apewer
  7. {
  8. /// <summary>时钟。</summary>
  9. public class ClockUtility
  10. {
  11. /// <summary>创建新的零值 DateTime 对象。</summary>
  12. public static DateTime Zero { get => new DateTime(0L, DateTimeKind.Utc); }
  13. /// <summary>获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000。</summary>
  14. public static DateTime Origin { get => new DateTime(1970, 1, 1, 0, 0, 0, 0); }
  15. /// <summary>获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为本地时间。</summary>
  16. public static DateTime Now { get => DateTime.Now; }
  17. /// <summary>获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为协调通用时间 (UTC)。</summary>
  18. public static DateTime UtcNow { get => DateTime.UtcNow; }
  19. #region Common
  20. /// <summary>判断指定年份是闰年。</summary>
  21. public static bool IsLeapYear(int year)
  22. {
  23. if (year % 400 == 0) return true;
  24. if (year % 100 == 0) return false;
  25. if (year % 4 == 0) return true;
  26. return false;
  27. }
  28. /// <summary>判断指定年份是闰年。</summary>
  29. public static bool IsLeapYear(DateTime datetime) => IsLeapYear(SafeDateTime(datetime).Year);
  30. /// <summary>获取指定年月的天数。</summary>
  31. public static int MonthDays(int year, int month)
  32. {
  33. switch (month)
  34. {
  35. case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31;
  36. case 4: case 6: case 9: case 11: return 30;
  37. case 2: return IsLeapYear(year) ? 29 : 28;
  38. default: return 0;
  39. }
  40. }
  41. /// <summary>尝试获取安全的 DateTime 对象。</summary>
  42. public static DateTime SafeDateTime(object datetime)
  43. {
  44. if (datetime is DateTime)
  45. {
  46. var span = (DateTime)datetime - Origin;
  47. return Origin.AddMilliseconds(span.TotalMilliseconds);
  48. }
  49. else
  50. {
  51. if (datetime == null) return Zero;
  52. var type = datetime.GetType();
  53. if (type.Equals(typeof(string)))
  54. {
  55. var s = datetime as string;
  56. if (string.IsNullOrEmpty(s)) return Zero;
  57. DateTime value;
  58. var success = DateTime.TryParse(s, out value);
  59. return success ? value : Zero;
  60. }
  61. else
  62. {
  63. if (datetime == null) return Zero;
  64. try
  65. {
  66. var s = datetime.ToString();
  67. if (string.IsNullOrEmpty(s)) return Zero;
  68. return SafeDateTime(s);
  69. }
  70. catch { return Zero; }
  71. }
  72. }
  73. }
  74. #endregion
  75. #region Stamp
  76. /// <summary>获取当前本地时间的毫秒时间戳。</summary>
  77. public static long NowStamp { get => ToStamp(Now); }
  78. /// <summary>获取当前 UTC 的毫秒时间戳。</summary>
  79. public static long UtcStamp { get => ToStamp(UtcNow); }
  80. /// <summary>获取毫秒时间戳。</summary>
  81. public static long ToStamp(DateTime datetime, bool byMilliseconds = true)
  82. {
  83. var span = datetime - Origin;
  84. var value = byMilliseconds ? span.TotalMilliseconds : span.TotalSeconds;
  85. var stamp = Convert.ToInt64(value);
  86. return stamp;
  87. }
  88. /// <summary>从毫秒时间戳获取 DateTime 对象。发生异常且不允许异常时将返回 1970-01-01 00:00:00.000。</summary>
  89. /// <exception cref="ArgumentOutOfRangeException"></exception>
  90. public static DateTime FromStamp(long stamp, bool exceptable = true)
  91. {
  92. try
  93. {
  94. var datetime = Origin.AddMilliseconds(Convert.ToDouble(stamp));
  95. return datetime;
  96. }
  97. catch
  98. {
  99. if (exceptable) throw new ArgumentOutOfRangeException();
  100. return Origin;
  101. }
  102. }
  103. #endregion
  104. #region Lucid & Compact
  105. /// <summary>表示当前本地时间的文本,显示为易于阅读的格式。</summary>
  106. public static string LucidNow { get => ToLucid(Now); }
  107. /// <summary>表示当前 UTC 的文本,显示为易于阅读的格式。</summary>
  108. public static string LucidUtc { get => ToLucid(UtcNow); }
  109. /// <summary>表示当前本地日期的文本,显示为易于阅读的格式。</summary>
  110. public static string LucidDate { get { return ToLucid(DateTime.Now, true, false, false, false); } }
  111. /// <summary>表示当前本地时间的文本,显示为紧凑的格式。</summary>
  112. public static string CompactNow { get => ToCompact(Now); }
  113. /// <summary>表示当前 UTC 的文本,显示为紧凑的格式。</summary>
  114. public static string CompactUtc { get => ToCompact(UtcNow); }
  115. /// <summary>表示当前本地日期的文本,显示为紧凑的格式。</summary>
  116. public static string CompactDate { get { return ToCompact(DateTime.Now, true, false, false, false); } }
  117. /// <summary>转换 DateTime 对象到易于阅读的文本。</summary>
  118. public static string ToLucid(DateTime datetime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
  119. {
  120. var safe = SafeDateTime(datetime);
  121. var sb = new StringBuilder();
  122. if (date) sb.Append(FormatDate(safe, true));
  123. if (time)
  124. {
  125. if (date) sb.Append(" ");
  126. sb.Append(FormatTime(safe, true, seconds, milliseconds));
  127. }
  128. var lucid = sb.ToString();
  129. return lucid;
  130. }
  131. /// <summary>转换 DateTime 对象到紧凑的文本。</summary>
  132. public static string ToCompact(DateTime datetime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
  133. {
  134. var safe = SafeDateTime(datetime);
  135. var sb = new StringBuilder();
  136. if (date) sb.Append(FormatDate(safe, false));
  137. if (time)
  138. {
  139. sb.Append(FormatTime(safe, false, seconds, milliseconds));
  140. }
  141. var lucid = sb.ToString();
  142. return lucid;
  143. }
  144. private static string FormatDate(DateTime datetime, bool lucid)
  145. {
  146. var sb = new StringBuilder();
  147. var y = NumberUtility.RestrictValue(datetime.Year, 0, 9999);
  148. var m = NumberUtility.RestrictValue(datetime.Month, 1, 12);
  149. var d = NumberUtility.RestrictValue(datetime.Day, 1, MonthDays(y, m));
  150. if (y < 10) sb.Append("000");
  151. else if (y < 100) sb.Append("00");
  152. else if (y < 1000) sb.Append("0");
  153. sb.Append(y.ToString());
  154. if (lucid) sb.Append("-");
  155. if (m < 10) sb.Append("0");
  156. sb.Append(m.ToString());
  157. if (lucid) sb.Append("-");
  158. if (d < 10) sb.Append("0");
  159. sb.Append(d.ToString());
  160. var date = sb.ToString();
  161. return date;
  162. }
  163. private static string FormatTime(DateTime datetime, bool lucid, bool seconds, bool milliseconds)
  164. {
  165. var sb = new StringBuilder();
  166. var h = NumberUtility.RestrictValue(datetime.Hour, 0, 23);
  167. var m = NumberUtility.RestrictValue(datetime.Minute, 0, 59);
  168. var s = NumberUtility.RestrictValue(datetime.Second, 0, 59);
  169. var ms = NumberUtility.RestrictValue(datetime.Millisecond, 0, 999);
  170. if (h < 10) sb.Append("0");
  171. sb.Append(h.ToString());
  172. if (lucid) sb.Append(":");
  173. if (m < 10) sb.Append("0");
  174. sb.Append(m.ToString());
  175. if (seconds)
  176. {
  177. if (lucid) sb.Append(":");
  178. if (s < 10) sb.Append("0");
  179. sb.Append(s.ToString());
  180. if (milliseconds)
  181. {
  182. if (lucid) sb.Append(".");
  183. if (ms < 10) sb.Append("00");
  184. else if (ms < 100) sb.Append("0");
  185. sb.Append(ms.ToString());
  186. }
  187. }
  188. var time = sb.ToString();
  189. return time;
  190. }
  191. /// <summary>解析 Lucid 文本。</summary>
  192. public static Nullable<DateTime> ParseLucid(string lucid)
  193. {
  194. var failed = new Nullable<DateTime>();
  195. if (lucid.IsEmpty()) return failed;
  196. int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, milli = 0;
  197. if (lucid.Length < 10) return failed;
  198. if (lucid[4] != '-' || lucid[7] != '-') return failed;
  199. year = TextUtility.GetInt32(lucid.Substring(0, 4));
  200. month = TextUtility.GetInt32(lucid.Substring(5, 2));
  201. day = TextUtility.GetInt32(lucid.Substring(8, 2));
  202. if (year < 1) return failed;
  203. if (month < 1 || month > 12) return failed;
  204. if (day < 1 || day > DateTime.DaysInMonth(year, month)) return failed;
  205. if (lucid.Length >= 16)
  206. {
  207. if (lucid[10] != ' ' || lucid[13] != ':') return failed;
  208. hour = TextUtility.GetInt32(lucid.Substring(11, 2));
  209. minute = TextUtility.GetInt32(lucid.Substring(14, 2));
  210. if (hour < 0 || hour > 23) return failed;
  211. if (minute < 0 || minute > 59) return failed;
  212. if (lucid.Length >= 19)
  213. {
  214. if (lucid[16] != ':') return failed;
  215. second = TextUtility.GetInt32(lucid.Substring(17, 2));
  216. if (second < 0 || second > 59) return failed;
  217. if (lucid.Length >= 23)
  218. {
  219. if (lucid[19] != '.') return failed;
  220. milli = TextUtility.GetInt32(lucid.Substring(20, 3));
  221. if (milli < 0 || milli > 999) return failed;
  222. }
  223. }
  224. }
  225. var entity = new DateTime(year, month, day, hour, minute, second, milli);
  226. return new Nullable<DateTime>(entity);
  227. }
  228. #endregion
  229. /// <summary>评估 Action 的执行时间,单位为秒。</summary>
  230. public static double Elapse(Action action)
  231. {
  232. if (action == null) return 0D;
  233. var sw = new Stopwatch();
  234. sw.Start();
  235. action.Invoke();
  236. sw.Stop();
  237. var milliseconds = sw.ElapsedMilliseconds;
  238. var seconds = Convert.ToDouble(milliseconds) / 1000D;
  239. return seconds;
  240. }
  241. }
  242. }