using System; using System.Data.SqlClient; using System.Globalization; using System.Text.RegularExpressions; namespace Sim.Data.Engine; /// 值在 .NET、SQLite、T-SQL 之间的转换。日期一律以 "yyyy-MM-dd HH:mm:ss" 文本保存,按文本比较即按时间先后。 internal static class Values { private static readonly string[] DateFormats = { "yyyy-M-d", "yyyy-M-d H:m", "yyyy-M-d H:m:s", "yyyy-M-d H:m:s.FFFFFFF", "yyyy-M-dTH:m:s", "yyyy-M-dTH:m:s.FFFFFFF", "yyyy/M/d", "yyyy/M/d H:m", "yyyy/M/d H:m:s", "yyyy/M/d H:m:s.FFFFFFF", "yyyyMMdd", "yyyyMMdd H:m:s", "yyyy.M.d", "yyyy.M.d H:m:s", }; /// 形如日期的字符串字面量(比较时转成标准格式,例如 BETWEEN '2024/1/16 9:05:03' AND ...) private static readonly Regex DateLike = new(@"^\d{4}[-/.]\d{1,2}[-/.]\d{1,2}([ T]\d{1,2}:\d{1,2}(:\d{1,2}(\.\d+)?)?)?$"); public static bool LooksLikeDate(string text) => DateLike.IsMatch(text.Trim()); /// 按 SQL Server 接受的写法解析日期(包括中文 Windows 上 DateTime.ToString() 的 2024/1/16 9:05:03) public static bool TryParseDateTime(string text, out DateTime value) => DateTime.TryParseExact(text.Trim(), DateFormats, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out value); public static string FormatDateTime(DateTime value) => value.Millisecond == 0 && value.Ticks % TimeSpan.TicksPerMillisecond == 0 ? value.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture) : value.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture); public static string FormatDate(DateTime value) => value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); /// 字符串里的日期改成标准格式;只有日期没有时间的保持只有日期(与 datetime 比较时相当于当天 0 点) public static string NormalizeDateLiteral(string text) { if (!TryParseDateTime(text, out var value)) return text; return text.Contains(':') ? FormatDateTime(value) : FormatDate(value); } /// C# 传进来的参数值 → SQLite 能保存的值 public static object? ToStorage(object? value) => value switch { null or DBNull => null, DateTime d => FormatDateTime(d), DateTimeOffset d => FormatDateTime(d.LocalDateTime), TimeSpan t => t.ToString(@"hh\:mm\:ss", CultureInfo.InvariantCulture), bool b => b ? 1L : 0L, Guid g => g.ToString().ToUpperInvariant(), decimal m => (double)m, Enum e => Convert.ToInt64(e, CultureInfo.InvariantCulture), _ => value, }; /// SQLite 读出的值 → SQL Server 对这个类型返回的 .NET 值 public static object FromStorage(object value, TSqlType? type) { if (value is DBNull) return value; if (type == null || type.ClrType == typeof(object)) return FromExpression(value); try { return type.ClrType switch { var t when t == typeof(int) => Convert.ToInt32(value, CultureInfo.InvariantCulture), var t when t == typeof(long) => Convert.ToInt64(value, CultureInfo.InvariantCulture), var t when t == typeof(short) => Convert.ToInt16(value, CultureInfo.InvariantCulture), var t when t == typeof(byte) => Convert.ToByte(value, CultureInfo.InvariantCulture), var t when t == typeof(bool) => Convert.ToInt64(value, CultureInfo.InvariantCulture) != 0, var t when t == typeof(double) => Convert.ToDouble(value, CultureInfo.InvariantCulture), var t when t == typeof(float) => Convert.ToSingle(value, CultureInfo.InvariantCulture), var t when t == typeof(decimal) => Convert.ToDecimal(value, CultureInfo.InvariantCulture), var t when t == typeof(DateTime) => value is string s && TryParseDateTime(s, out var d) ? d : value, var t when t == typeof(TimeSpan) => value is string s && TimeSpan.TryParse(s, CultureInfo.InvariantCulture, out var ts) ? ts : value, var t when t == typeof(Guid) => value is string s && Guid.TryParse(s, out var g) ? g : value, var t when t == typeof(string) => Convert.ToString(value, CultureInfo.InvariantCulture)!, _ => value, }; } catch (Exception ex) when (ex is FormatException or OverflowException or InvalidCastException) { return value; } } /// 表达式列(如 count(*)、getdate())没有声明类型:整数按 int 返回,日期样子的文本按 DateTime 返回 public static object FromExpression(object value) => value switch { long l when l is >= int.MinValue and <= int.MaxValue => (int)l, string s when DateLike.IsMatch(s) && s.Contains(':') && TryParseDateTime(s, out var d) => d, _ => value, }; /// T-SQL 字符串字面量 public static string Quote(string text) => "'" + text.Replace("'", "''") + "'"; /// SQLite 标识符 public static string QuoteName(string name) => "\"" + name.Replace("\"", "\"\"") + "\""; public static SqlException ConvertError(object? value, string type) => SqlException.ConvertFailed(Convert.ToString(value, CultureInfo.InvariantCulture) ?? "", type); }