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.

394 lines
14 KiB

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