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.
319 lines
12 KiB
319 lines
12 KiB
using Apewer.Internals;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
|
|
namespace Apewer
|
|
{
|
|
|
|
/// <summary>时钟。</summary>
|
|
public class ClockUtility
|
|
{
|
|
|
|
/// <summary>创建新的零值 DateTime 对象。</summary>
|
|
public static DateTime Zero { get => new DateTime(0L, DateTimeKind.Utc); }
|
|
|
|
/// <summary>获取一个 DateTime 对象,该对象设置为 1970-01-01 00:00:00.000。</summary>
|
|
public static DateTime Origin { get => new DateTime(1970, 1, 1, 0, 0, 0, 0); }
|
|
|
|
/// <summary>获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为本地时间。</summary>
|
|
public static DateTime Now { get => DateTime.Now; }
|
|
|
|
/// <summary>获取一个 DateTime 对象,该对象设置为此计算机上的当前日期和时间,表示为协调通用时间 (UTC)。</summary>
|
|
public static DateTime UtcNow { get => DateTime.UtcNow; }
|
|
|
|
#region Common
|
|
|
|
/// <summary>判断指定年份是闰年。</summary>
|
|
public static bool IsLeapYear(int year)
|
|
{
|
|
if (year % 400 == 0) return true;
|
|
if (year % 100 == 0) return false;
|
|
if (year % 4 == 0) return true;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>判断指定年份是闰年。</summary>
|
|
public static bool IsLeapYear(DateTime datetime) => IsLeapYear(SafeDateTime(datetime).Year);
|
|
|
|
/// <summary>获取指定年月的天数。</summary>
|
|
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: return 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>尝试获取安全的 DateTime 对象。</summary>
|
|
public static DateTime SafeDateTime(object datetime)
|
|
{
|
|
if (datetime is DateTime)
|
|
{
|
|
var span = (DateTime)datetime - Origin;
|
|
return Origin.AddMilliseconds(span.TotalMilliseconds);
|
|
}
|
|
else
|
|
{
|
|
if (datetime == null) return Zero;
|
|
var type = datetime.GetType();
|
|
if (type.Equals(typeof(string)))
|
|
{
|
|
var s = datetime as string;
|
|
if (string.IsNullOrEmpty(s)) return Zero;
|
|
DateTime value;
|
|
var success = DateTime.TryParse(s, out value);
|
|
return success ? value : Zero;
|
|
}
|
|
else
|
|
{
|
|
if (datetime == null) return Zero;
|
|
try
|
|
{
|
|
var s = datetime.ToString();
|
|
if (string.IsNullOrEmpty(s)) return Zero;
|
|
return SafeDateTime(s);
|
|
}
|
|
catch { return Zero; }
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Stamp
|
|
|
|
/// <summary>获取当前本地时间的毫秒时间戳。</summary>
|
|
public static long NowStamp { get => Stamp(Now); }
|
|
|
|
/// <summary>获取当前 UTC 的毫秒时间戳。</summary>
|
|
public static long UtcStamp { get => Stamp(UtcNow); }
|
|
|
|
/// <summary>获取毫秒时间戳。</summary>
|
|
public static long Stamp(DateTime datetime, bool byMilliseconds = true)
|
|
{
|
|
var span = datetime - Origin;
|
|
var value = byMilliseconds ? span.TotalMilliseconds : span.TotalSeconds;
|
|
var stamp = Convert.ToInt64(value);
|
|
return stamp;
|
|
}
|
|
|
|
/// <summary>从毫秒时间戳获取 DateTime 对象。发生异常且不允许异常时将返回 1970-01-01 00:00:00.000。</summary>
|
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|
public static DateTime FromStamp(long stamp, bool throwException = true)
|
|
{
|
|
try
|
|
{
|
|
var datetime = Origin.AddMilliseconds(Convert.ToDouble(stamp));
|
|
return datetime;
|
|
}
|
|
catch
|
|
{
|
|
if (throwException) throw new ArgumentOutOfRangeException();
|
|
return Origin;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Text
|
|
|
|
/// <summary>解析文本,获取 DateTime 对象。</summary>
|
|
public static Class<DateTime> 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 (!DateTime.TryParse(str, out dt))
|
|
{
|
|
if (!str.Contains("-") && DateTime.TryParseExact(str, "yyyy-M-d", null, DateTimeStyles.None, out dt))
|
|
{
|
|
if (!str.Contains("/") && DateTime.TryParseExact(str, "yyyy/M/d", null, DateTimeStyles.None, out dt))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (utc) dt = new DateTime(dt.Ticks, DateTimeKind.Utc);
|
|
return new Class<DateTime>(dt);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Lucid & Compact
|
|
|
|
/// <summary>表示当前本地时间的文本,显示为易于阅读的格式。</summary>
|
|
public static string LucidNow { get => Lucid(Now); }
|
|
|
|
/// <summary>表示当前 UTC 的文本,显示为易于阅读的格式。</summary>
|
|
public static string LucidUtc { get => Lucid(UtcNow); }
|
|
|
|
/// <summary>表示当前本地日期的文本,显示为易于阅读的格式。</summary>
|
|
public static string LucidDate { get { return Lucid(DateTime.Now, true, false, false, false); } }
|
|
|
|
/// <summary>表示当前本地时间的文本,显示为紧凑的格式。</summary>
|
|
public static string CompactNow { get => Compact(Now); }
|
|
|
|
/// <summary>表示当前 UTC 的文本,显示为紧凑的格式。</summary>
|
|
public static string CompactUtc { get => Compact(UtcNow); }
|
|
|
|
/// <summary>表示当前本地日期的文本,显示为紧凑的格式。</summary>
|
|
public static string CompactDate { get { return Compact(DateTime.Now, true, false, false, false); } }
|
|
|
|
/// <summary>转换 DateTime 对象到易于阅读的文本。</summary>
|
|
public static string Lucid(DateTime datetime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
|
|
{
|
|
var safe = SafeDateTime(datetime);
|
|
var sb = new StringBuilder();
|
|
if (date) sb.Append(FormatDate(safe, true));
|
|
if (time)
|
|
{
|
|
if (date) sb.Append(" ");
|
|
sb.Append(FormatTime(safe, true, seconds, milliseconds));
|
|
}
|
|
var lucid = sb.ToString();
|
|
return lucid;
|
|
}
|
|
|
|
/// <summary>转换 DateTime 对象到紧凑的文本。</summary>
|
|
public static string Compact(DateTime datetime, bool date = true, bool time = true, bool seconds = true, bool milliseconds = true)
|
|
{
|
|
var safe = SafeDateTime(datetime);
|
|
var sb = new StringBuilder();
|
|
if (date) sb.Append(FormatDate(safe, false));
|
|
if (time)
|
|
{
|
|
sb.Append(FormatTime(safe, 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;
|
|
}
|
|
|
|
/// <summary>解析 Lucid 文本。</summary>
|
|
public static Nullable<DateTime> ParseLucid(string lucid)
|
|
{
|
|
var failed = new Nullable<DateTime>();
|
|
if (lucid.IsEmpty()) return failed;
|
|
|
|
int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, milli = 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 > DateTime.DaysInMonth(year, month)) return failed;
|
|
|
|
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<DateTime>(entity);
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|
|
|