using System; using System.Globalization; using System.Text; using static System.DateTime; namespace Apewer { /// 时钟。 public static class ClockUtility { #region DateTime /// 尝试转换内容为 DateTime 实例。 public static Class DateTime(object value) { if (value is DateTime dt) return new Class(dt); if (value.IsNull()) return null; try { var text = value.ToString(); var parsed = System.DateTime.TryParse(text, out var result); return parsed ? new Class(result) : null; } catch { return null; } } #endregion #region Fixed private static DateTime _zero = new DateTime(0L, DateTimeKind.Unspecified); private static DateTime _origin = NewOrigin(DateTimeKind.Unspecified); private static DateTime _utc_origin = NewOrigin(DateTimeKind.Utc); /// 创建新的零值 DateTime 对象。 public static DateTime Zero { get => _zero; } /// 获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000,表示为协调通用时间 (UTC)。 public static DateTime UtcOrigin { get => _utc_origin; } /// 获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为本地时间。 public static DateTime Now { get => System.DateTime.Now; } /// 获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为协调通用时间 (UTC)。 public static DateTime UtcNow { get => System.DateTime.UtcNow; } /// 创建一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000。 public static DateTime NewOrigin(DateTimeKind kind) => new DateTime(1970, 1, 1, 0, 0, 0, 0, kind); #endregion #region Clone /// 克隆 DateTime 对象,并使用新的 Kind。 /// 要克隆的 DateTime 对象。 /// 时间类型。 /// 克隆后带有新 Kind 的 DateTime 对象。 public static DateTime Clone(this DateTime dateTime, DateTimeKind kind) { return new DateTime(dateTime.Ticks, kind); } #endregion #region Round /// 对齐时间,小于 的部分将被舍弃。 public static DateTime Round(this DateTime dateTime, DateTimePart datePart) { switch (datePart) { case DateTimePart.Year: return new DateTime(dateTime.Year, 1, 1, 0, 0, 0, 0, dateTime.Kind); case DateTimePart.Month: return new DateTime(dateTime.Year, dateTime.Month, 1, 0, 0, 0, 0, dateTime.Kind); case DateTimePart.Day: return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0, dateTime.Kind); case DateTimePart.Hour: return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, 0, 0, dateTime.Kind); case DateTimePart.Minute: return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0, 0, dateTime.Kind); case DateTimePart.Second: return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, dateTime.Second, 0, dateTime.Kind); case DateTimePart.Millisecond: return dateTime; default: throw new ArgumentException($"指定的 {nameof(DateTimePart)} 不受支持。"); } } #endregion #region Common /// 判断指定年份是闰年。 public static bool IsLeapYear(this int year) { if (year % 400 == 0) return true; if (year % 100 == 0) return false; if (year % 4 == 0) return true; return false; } /// 判断指定年份是闰年。 public static bool IsLeapYear(DateTime dateTime) => IsLeapYear(dateTime.Year); /// 获取指定年月的天数。 /// public static int MonthDays(int year, int month) { switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31; case 4: case 6: case 9: case 11: return 30; case 2: return IsLeapYear(year) ? 29 : 28; default: throw new ArgumentOutOfRangeException(nameof(month)); } } #endregion #region Stamp /// 自定义从 DateTime 转为时间戳的方法。 public static Func CustomToStamp { get; set; } /// 自定义从时间戳转为 DateTime 的方法。 public static Func CustomFromStamp { get; set; } /// 获取当前本地时间的毫秒时间戳。 public static long NowStamp { get => ToStamp(Now); } /// 获取当前 UTC 的毫秒时间戳。 public static long UtcStamp { get => ToStamp(UtcNow); } /// 获取毫秒时间戳。当指定了 时将优先使用自定义的方法。 /// 默认不判断参数的时区,与 相同。 public static long ToStamp(DateTime dateTime) { var converter = CustomToStamp; if (converter != null) return converter.Invoke(dateTime); var span = dateTime - _origin; var value = span.TotalMilliseconds; var stamp = Convert.ToInt64(Math.Floor(value)); return stamp; } /// 获取 UTC 毫秒时间戳 public static long ToUtcStamp(DateTime dateTime) { if (dateTime.Kind == DateTimeKind.Local) dateTime = dateTime.ToUniversalTime(); var span = dateTime - _utc_origin; var value = span.TotalMilliseconds; var stamp = Convert.ToInt64(Math.Floor(value)); return stamp; } /// 解析毫秒时间戳,获取 DateTime 对象。当指定了 时将优先使用自定义的方法。 /// 默认不判断系统时区,返回的结果是 /// public static DateTime FromStamp(long stamp) { var converter = CustomFromStamp; if (converter != null) return converter.Invoke(stamp); return FromStamp(stamp, DateTimeKind.Unspecified, DateTimeKind.Unspecified); } /// 从毫秒时间戳获取 DateTime 对象。 /// public static DateTime FromStamp(long stamp, DateTimeKind stampKind, DateTimeKind dateTimeKind) { switch (dateTimeKind) { case DateTimeKind.Unspecified: switch (stampKind) { case DateTimeKind.Unspecified: case DateTimeKind.Utc: case DateTimeKind.Local: return _origin.AddMilliseconds(stamp); default: throw new ArgumentOutOfRangeException(nameof(stampKind)); } case DateTimeKind.Utc: switch (stampKind) { case DateTimeKind.Unspecified: case DateTimeKind.Utc: return _utc_origin.AddMilliseconds(stamp); case DateTimeKind.Local: return NewOrigin(DateTimeKind.Local).AddMilliseconds(stamp).ToUniversalTime(); default: throw new ArgumentOutOfRangeException(nameof(stampKind)); } case DateTimeKind.Local: switch (stampKind) { case DateTimeKind.Unspecified: case DateTimeKind.Utc: return _utc_origin.AddMilliseconds(stamp).ToLocalTime(); case DateTimeKind.Local: return NewOrigin(DateTimeKind.Local).AddMilliseconds(stamp); default: throw new ArgumentOutOfRangeException(nameof(stampKind)); } default: throw new ArgumentOutOfRangeException(nameof(dateTimeKind)); } } #endregion #region Text /// 解析文本,获取 DateTime 对象。 public static Class FromText(string text) { var str = text; if (string.IsNullOrEmpty(str)) return null; var utc = false; var lower = str.ToLower(); if (lower.EndsWith(" utc")) { utc = true; str = str.Substring(0, str.Length - 4); } DateTime dt; if (!TryParse(str, out dt)) { if (!str.Contains("-") && TryParseExact(str, "yyyy-M-d", null, DateTimeStyles.None, out dt)) { if (!str.Contains("/") && TryParseExact(str, "yyyy/M/d", null, DateTimeStyles.None, out dt)) { return null; } } } if (utc) dt = new DateTime(dt.Ticks, DateTimeKind.Utc); return new Class(dt); } #endregion #region Lucid & Compact /// 表示当前本地时间的文本,显示为易于阅读的格式。 public static string LucidNow { get => Lucid(Now); } /// 表示当前 UTC 的文本,显示为易于阅读的格式。 public static string LucidUtc { get => Lucid(UtcNow); } /// 表示当前本地日期的文本,显示为易于阅读的格式。 public static string LucidDate { get { return Lucid(Now, true, false, false, false); } } /// 表示当前本地时间的文本,显示为紧凑的格式。 public static string CompactNow { get => Compact(Now); } /// 表示当前 UTC 的文本,显示为紧凑的格式。 public static string CompactUtc { get => Compact(UtcNow); } /// 表示当前本地日期的文本,显示为紧凑的格式。 public static string CompactDate { get { return Compact(Now, true, false, false, false); } } /// 转换 DateTime 对象到易于阅读的文本。 public static string Lucid(DateTime dateTime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true) { var sb = new StringBuilder(); if (date) sb.Append(FormatDate(dateTime, true)); if (time) { if (date) sb.Append(" "); sb.Append(FormatTime(dateTime, true, seconds, milliseconds)); } var lucid = sb.ToString(); return lucid; } /// 转换 DateTime 对象到紧凑的文本。 public static string Compact(DateTime dateTime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true) { var sb = new StringBuilder(); if (date) sb.Append(FormatDate(dateTime, false)); if (time) { sb.Append(FormatTime(dateTime, false, seconds, milliseconds)); } var lucid = sb.ToString(); return lucid; } private static string FormatDate(DateTime datetime, bool lucid) { var sb = new StringBuilder(); var y = NumberUtility.Restrict(datetime.Year, 0, 9999); var m = NumberUtility.Restrict(datetime.Month, 1, 12); var d = NumberUtility.Restrict(datetime.Day, 1, MonthDays(y, m)); if (y < 10) sb.Append("000"); else if (y < 100) sb.Append("00"); else if (y < 1000) sb.Append("0"); sb.Append(y.ToString()); if (lucid) sb.Append("-"); if (m < 10) sb.Append("0"); sb.Append(m.ToString()); if (lucid) sb.Append("-"); if (d < 10) sb.Append("0"); sb.Append(d.ToString()); var date = sb.ToString(); return date; } private static string FormatTime(DateTime datetime, bool lucid, bool seconds, bool milliseconds) { var sb = new StringBuilder(); var h = NumberUtility.Restrict(datetime.Hour, 0, 23); var m = NumberUtility.Restrict(datetime.Minute, 0, 59); var s = NumberUtility.Restrict(datetime.Second, 0, 59); var ms = NumberUtility.Restrict(datetime.Millisecond, 0, 999); if (h < 10) sb.Append("0"); sb.Append(h.ToString()); if (lucid) sb.Append(":"); if (m < 10) sb.Append("0"); sb.Append(m.ToString()); if (seconds) { if (lucid) sb.Append(":"); if (s < 10) sb.Append("0"); sb.Append(s.ToString()); if (milliseconds) { if (lucid) sb.Append("."); if (ms < 10) sb.Append("00"); else if (ms < 100) sb.Append("0"); sb.Append(ms.ToString()); } } var time = sb.ToString(); return time; } /// 解析文本。 /// 要被解析的文本。 /// 解析失败时的获取方法。 public static Nullable Parse(string text, Func failed = null) { if (string.IsNullOrEmpty(text)) return null; // 使用默认解析。 { if (TryParse(text, out DateTime dt)) return dt; } // 尝试解析 Lucid 格式。 var byLucid = ParseLucid(text); if (byLucid != null) return byLucid; // 尝试 failed 回调。 if (failed != null) return failed.Invoke(); return null; } /// 解析文本。 public static Nullable ParseLucid(string lucid) { var failed = null as Nullable; if (lucid.IsEmpty()) return failed; int year = 0, month = 0, day = 0; if (lucid.Length < 10) return failed; if (lucid[4] != '-' || lucid[7] != '-') return failed; year = NumberUtility.Int32(lucid.Substring(0, 4)); month = NumberUtility.Int32(lucid.Substring(5, 2)); day = NumberUtility.Int32(lucid.Substring(8, 2)); if (year < 1) return failed; if (month < 1 || month > 12) return failed; if (day < 1 || day > DaysInMonth(year, month)) return failed; int hour = 0, minute = 0, second = 0, milli = 0; if (lucid.Length >= 16) { if (lucid[10] != ' ' || lucid[13] != ':') return failed; hour = NumberUtility.Int32(lucid.Substring(11, 2)); minute = NumberUtility.Int32(lucid.Substring(14, 2)); if (hour < 0 || hour > 23) return failed; if (minute < 0 || minute > 59) return failed; if (lucid.Length >= 19) { if (lucid[16] != ':') return failed; second = NumberUtility.Int32(lucid.Substring(17, 2)); if (second < 0 || second > 59) return failed; if (lucid.Length >= 23) { if (lucid[19] != '.') return failed; milli = NumberUtility.Int32(lucid.Substring(20, 3)); if (milli < 0 || milli > 999) return failed; } } } var entity = new DateTime(year, month, day, hour, minute, second, milli); return new Nullable(entity); } #endregion } }