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.

369 lines
14 KiB

4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
  1. using Apewer.Internals;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Globalization;
  6. using System.Text;
  7. namespace Apewer
  8. {
  9. /// <summary>时钟。</summary>
  10. public static class ClockUtility
  11. {
  12. #region Fixed
  13. private static DateTime _zero = new DateTime(0L, DateTimeKind.Unspecified);
  14. private static DateTime _origin = NewOrigin(DateTimeKind.Unspecified);
  15. private static DateTime _utc_origin = NewOrigin(DateTimeKind.Utc);
  16. /// <summary>创建新的零值 DateTime 对象。</summary>
  17. public static DateTime Zero { get => _zero; }
  18. /// <summary>获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000,表示为本地时间。</summary>
  19. public static DateTime Origin { get => _origin; }
  20. /// <summary>获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000,表示为协调通用时间 (UTC)。</summary>
  21. public static DateTime UtcOrigin { get => _utc_origin; }
  22. /// <summary>获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为本地时间。</summary>
  23. public static DateTime Now { get => DateTime.Now; }
  24. /// <summary>获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为协调通用时间 (UTC)。</summary>
  25. public static DateTime UtcNow { get => DateTime.UtcNow; }
  26. /// <summary>创建一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000。</summary>
  27. public static DateTime NewOrigin(DateTimeKind kind) => new DateTime(1970, 1, 1, 0, 0, 0, 0, kind);
  28. #endregion
  29. #region Clone
  30. /// <summary>克隆 DateTime 对象,并使用新的 Kind。</summary>
  31. /// <param name="dateTime">要克隆的 DateTime 对象。</param>
  32. /// <param name="kind">时间类型。</param>
  33. /// <returns>克隆后带有新 Kind 的 DateTime 对象。</returns>
  34. public static DateTime Clone(this DateTime dateTime, DateTimeKind kind)
  35. {
  36. return new DateTime(dateTime.Ticks, kind);
  37. }
  38. #endregion
  39. #region Common
  40. /// <summary>判断指定年份是闰年。</summary>
  41. public static bool IsLeapYear(this int year)
  42. {
  43. if (year % 400 == 0) return true;
  44. if (year % 100 == 0) return false;
  45. if (year % 4 == 0) return true;
  46. return false;
  47. }
  48. /// <summary>判断指定年份是闰年。</summary>
  49. public static bool IsLeapYear(DateTime datetime) => IsLeapYear(SafeDateTime(datetime).Year);
  50. /// <summary>获取指定年月的天数。</summary>
  51. public static int MonthDays(int year, int month)
  52. {
  53. switch (month)
  54. {
  55. case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31;
  56. case 4: case 6: case 9: case 11: return 30;
  57. case 2: return IsLeapYear(year) ? 29 : 28;
  58. default: return 0;
  59. }
  60. }
  61. /// <summary>尝试获取安全的 DateTime 对象。</summary>
  62. public static DateTime SafeDateTime(object datetime)
  63. {
  64. if (datetime is DateTime)
  65. {
  66. var span = (DateTime)datetime - Origin;
  67. return Origin.AddMilliseconds(span.TotalMilliseconds);
  68. }
  69. else
  70. {
  71. if (datetime == null) return Zero;
  72. var type = datetime.GetType();
  73. if (type.Equals(typeof(string)))
  74. {
  75. var s = datetime as string;
  76. if (string.IsNullOrEmpty(s)) return Zero;
  77. DateTime value;
  78. var success = DateTime.TryParse(s, out value);
  79. return success ? value : Zero;
  80. }
  81. else
  82. {
  83. if (datetime == null) return Zero;
  84. try
  85. {
  86. var s = datetime.ToString();
  87. if (string.IsNullOrEmpty(s)) return Zero;
  88. return SafeDateTime(s);
  89. }
  90. catch { return Zero; }
  91. }
  92. }
  93. }
  94. #endregion
  95. #region Stamp
  96. /// <summary>获取当前本地时间的毫秒时间戳。</summary>
  97. public static long NowStamp { get => Stamp(Now); }
  98. /// <summary>获取当前 UTC 的毫秒时间戳。</summary>
  99. public static long UtcStamp { get => Stamp(UtcNow); }
  100. /// <summary>获取毫秒时间戳。</summary>
  101. public static long Stamp(DateTime datetime, bool byMilliseconds = true)
  102. {
  103. var span = datetime - Origin;
  104. var value = byMilliseconds ? span.TotalMilliseconds : span.TotalSeconds;
  105. var stamp = Convert.ToInt64(value);
  106. return stamp;
  107. }
  108. /// <summary>从毫秒时间戳获取 DateTime 对象。发生异常且不允许异常时将返回 1970-01-01 00:00:00.000。</summary>
  109. /// <exception cref="ArgumentOutOfRangeException"></exception>
  110. public static DateTime FromStamp(long stamp, DateTimeKind kind = DateTimeKind.Unspecified, bool throwException = true)
  111. {
  112. try
  113. {
  114. var origin = NewOrigin(kind);
  115. var datetime = origin.AddMilliseconds(Convert.ToDouble(stamp));
  116. return datetime;
  117. }
  118. catch
  119. {
  120. if (throwException) throw new ArgumentOutOfRangeException();
  121. return Origin;
  122. }
  123. }
  124. #endregion
  125. #region Text
  126. /// <summary>解析文本,获取 DateTime 对象。</summary>
  127. public static Class<DateTime> FromText(string text)
  128. {
  129. var str = text;
  130. if (string.IsNullOrEmpty(str)) return null;
  131. var utc = false;
  132. var lower = str.ToLower();
  133. if (lower.EndsWith(" utc"))
  134. {
  135. utc = true;
  136. str = str.Substring(0, str.Length - 4);
  137. }
  138. DateTime dt;
  139. if (!DateTime.TryParse(str, out dt))
  140. {
  141. if (!str.Contains("-") && DateTime.TryParseExact(str, "yyyy-M-d", null, DateTimeStyles.None, out dt))
  142. {
  143. if (!str.Contains("/") && DateTime.TryParseExact(str, "yyyy/M/d", null, DateTimeStyles.None, out dt))
  144. {
  145. return null;
  146. }
  147. }
  148. }
  149. if (utc) dt = new DateTime(dt.Ticks, DateTimeKind.Utc);
  150. return new Class<DateTime>(dt);
  151. }
  152. #endregion
  153. #region Lucid & Compact
  154. /// <summary>表示当前本地时间的文本,显示为易于阅读的格式。</summary>
  155. public static string LucidNow { get => Lucid(Now); }
  156. /// <summary>表示当前 UTC 的文本,显示为易于阅读的格式。</summary>
  157. public static string LucidUtc { get => Lucid(UtcNow); }
  158. /// <summary>表示当前本地日期的文本,显示为易于阅读的格式。</summary>
  159. public static string LucidDate { get { return Lucid(DateTime.Now, true, false, false, false); } }
  160. /// <summary>表示当前本地时间的文本,显示为紧凑的格式。</summary>
  161. public static string CompactNow { get => Compact(Now); }
  162. /// <summary>表示当前 UTC 的文本,显示为紧凑的格式。</summary>
  163. public static string CompactUtc { get => Compact(UtcNow); }
  164. /// <summary>表示当前本地日期的文本,显示为紧凑的格式。</summary>
  165. public static string CompactDate { get { return Compact(DateTime.Now, true, false, false, false); } }
  166. /// <summary>转换 DateTime 对象到易于阅读的文本。</summary>
  167. public static string Lucid(DateTime datetime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
  168. {
  169. var safe = SafeDateTime(datetime);
  170. var sb = new StringBuilder();
  171. if (date) sb.Append(FormatDate(safe, true));
  172. if (time)
  173. {
  174. if (date) sb.Append(" ");
  175. sb.Append(FormatTime(safe, true, seconds, milliseconds));
  176. }
  177. var lucid = sb.ToString();
  178. return lucid;
  179. }
  180. /// <summary>转换 DateTime 对象到紧凑的文本。</summary>
  181. public static string Compact(DateTime datetime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
  182. {
  183. var safe = SafeDateTime(datetime);
  184. var sb = new StringBuilder();
  185. if (date) sb.Append(FormatDate(safe, false));
  186. if (time)
  187. {
  188. sb.Append(FormatTime(safe, false, seconds, milliseconds));
  189. }
  190. var lucid = sb.ToString();
  191. return lucid;
  192. }
  193. private static string FormatDate(DateTime datetime, bool lucid)
  194. {
  195. var sb = new StringBuilder();
  196. var y = NumberUtility.Restrict(datetime.Year, 0, 9999);
  197. var m = NumberUtility.Restrict(datetime.Month, 1, 12);
  198. var d = NumberUtility.Restrict(datetime.Day, 1, MonthDays(y, m));
  199. if (y < 10) sb.Append("000");
  200. else if (y < 100) sb.Append("00");
  201. else if (y < 1000) sb.Append("0");
  202. sb.Append(y.ToString());
  203. if (lucid) sb.Append("-");
  204. if (m < 10) sb.Append("0");
  205. sb.Append(m.ToString());
  206. if (lucid) sb.Append("-");
  207. if (d < 10) sb.Append("0");
  208. sb.Append(d.ToString());
  209. var date = sb.ToString();
  210. return date;
  211. }
  212. private static string FormatTime(DateTime datetime, bool lucid, bool seconds, bool milliseconds)
  213. {
  214. var sb = new StringBuilder();
  215. var h = NumberUtility.Restrict(datetime.Hour, 0, 23);
  216. var m = NumberUtility.Restrict(datetime.Minute, 0, 59);
  217. var s = NumberUtility.Restrict(datetime.Second, 0, 59);
  218. var ms = NumberUtility.Restrict(datetime.Millisecond, 0, 999);
  219. if (h < 10) sb.Append("0");
  220. sb.Append(h.ToString());
  221. if (lucid) sb.Append(":");
  222. if (m < 10) sb.Append("0");
  223. sb.Append(m.ToString());
  224. if (seconds)
  225. {
  226. if (lucid) sb.Append(":");
  227. if (s < 10) sb.Append("0");
  228. sb.Append(s.ToString());
  229. if (milliseconds)
  230. {
  231. if (lucid) sb.Append(".");
  232. if (ms < 10) sb.Append("00");
  233. else if (ms < 100) sb.Append("0");
  234. sb.Append(ms.ToString());
  235. }
  236. }
  237. var time = sb.ToString();
  238. return time;
  239. }
  240. /// <summary>解析文本。</summary>
  241. /// <param name="text">要被解析的文本。</param>
  242. /// <param name="failed">解析失败时的获取方法。</param>
  243. public static Nullable<DateTime> Parse(string text, Func<DateTime> failed = null)
  244. {
  245. if (string.IsNullOrEmpty(text)) return null;
  246. // 使用默认解析。
  247. {
  248. if (DateTime.TryParse(text, out DateTime dt)) return dt;
  249. }
  250. // 尝试解析 Lucid 格式。
  251. var byLucid = ParseLucid(text);
  252. if (byLucid != null) return byLucid;
  253. // 尝试 failed 回调。
  254. if (failed != null) return failed.Invoke();
  255. return null;
  256. }
  257. /// <summary>解析文本。</summary>
  258. public static Nullable<DateTime> ParseLucid(string lucid)
  259. {
  260. var failed = null as Nullable<DateTime>;
  261. if (lucid.IsEmpty()) return failed;
  262. int year = 0, month = 0, day = 0;
  263. if (lucid.Length < 10) return failed;
  264. if (lucid[4] != '-' || lucid[7] != '-') return failed;
  265. year = NumberUtility.Int32(lucid.Substring(0, 4));
  266. month = NumberUtility.Int32(lucid.Substring(5, 2));
  267. day = NumberUtility.Int32(lucid.Substring(8, 2));
  268. if (year < 1) return failed;
  269. if (month < 1 || month > 12) return failed;
  270. if (day < 1 || day > DateTime.DaysInMonth(year, month)) return failed;
  271. int hour = 0, minute = 0, second = 0, milli = 0;
  272. if (lucid.Length >= 16)
  273. {
  274. if (lucid[10] != ' ' || lucid[13] != ':') return failed;
  275. hour = NumberUtility.Int32(lucid.Substring(11, 2));
  276. minute = NumberUtility.Int32(lucid.Substring(14, 2));
  277. if (hour < 0 || hour > 23) return failed;
  278. if (minute < 0 || minute > 59) return failed;
  279. if (lucid.Length >= 19)
  280. {
  281. if (lucid[16] != ':') return failed;
  282. second = NumberUtility.Int32(lucid.Substring(17, 2));
  283. if (second < 0 || second > 59) return failed;
  284. if (lucid.Length >= 23)
  285. {
  286. if (lucid[19] != '.') return failed;
  287. milli = NumberUtility.Int32(lucid.Substring(20, 3));
  288. if (milli < 0 || milli > 999) return failed;
  289. }
  290. }
  291. }
  292. var entity = new DateTime(year, month, day, hour, minute, second, milli);
  293. return new Nullable<DateTime>(entity);
  294. }
  295. #endregion
  296. }
  297. }