首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> C# 入門

C# 與 SQL Server DateTime 類型交互完整指南:格式化、參數化、陷阱避坑

     編輯:C# 入門

DateTime 是 C# 與 SQL Server 交互中最常見也最容易出錯的類型。本文系統梳理 SQL 寫法、C# 格式化、參數化查詢最佳實踐,並揭示字符串拼接 SQL 的致命陷阱。

一、SQL Server 端日期寫法

1. 硬編碼字面量 (僅限測試/靜態數據)


-- 僅日期 (隱含 00:00:00)
INSERT INTO Customer (Birthday) VALUES ('1985-09-16'); -- 推薦 ISO 8601 格式 yyyy-MM-dd

-- 日期時間 (精確到秒)
INSERT INTO Customer (CreateTime) VALUES ('1985-09-16 12:16:31');

-- 日期時間 (精確到毫秒)
INSERT INTO Customer (LogTime) VALUES ('2023-10-01 14:30:45.123');

⚠️ 必須加單引號。無引號 1985-09-16 會被解析為數學運算 1985 - 9 - 16 = 1960 (整數)。帶時間部分時無引號直接語法錯誤。

2. 系統函數 (動態數據)


-- 當前系統時間 (含毫秒,精度 ~3.33ms)
GETDATE()          -- 返回 datetime, 如 2023-10-01 14:30:45.123

-- 當前 UTC 時間
GETUTCDATE()       -- 返回 datetime

-- 高精度 (100ns),含時區偏移 (SQL 2008+)
SYSDATETIME()      -- 返回 datetime2(7)
SYSUTCDATETIME()   -- 返回 datetime2(7)
SYSDATETIMEOFFSET()-- 返回 datetimeoffset(7)

-- 更新示例
UPDATE CustomerInfo 
SET LastLogin = GETDATE() 
WHERE CustomerId = 13;

二、C# 端 DateTime 核心屬性與格式化

常用屬性


DateTime now = DateTime.Now;
now.Year;        // 2023
now.Month;       // 10
now.Day;         // 1
now.Hour;        // 14
now.Minute;      // 30
now.Second;      // 45
now.Millisecond; // 123
now.DayOfWeek;   // Monday
now.Ticks;       // 638345678901234567 (100ns 刻度)
now.Kind;        // Local / Utc / Unspecified (極重要!)

常用格式化輸出

代碼輸出示例說明
now.ToString("yyyy-MM-dd")2023-10-01標準日期
now.ToString("yyyy-MM-dd HH:mm:ss")2023-10-01 14:30:45標準日期時間 (24小時制)
now.ToString("yyyy-MM-dd HH:mm:ss.fff")2023-10-01 14:30:45.123含毫秒 (SQL datetime 精度)
now.ToString("o") / now.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK")2023-10-01T14:30:45.1234567+08:00ISO 8601 標準格式 (含時區),推薦用於序列化/日志/參數傳遞
now.ToShortDateString()2023/10/01當前文化短日期
now.ToShortTimeString()14:30當前文化短時間

三、核心陷阱:字符串拼接 SQL 的單引號與格式災難

錯誤代碼 (原文截圖所示錯誤):


// ❌ 極度危險:SQL 注入 + 格式依賴當前線程文化
string sql = $"INSERT INTO Orders (OrderTime) VALUES ({DateTime.Now})"; 
// 生成 SQL 可能為: INSERT ... VALUES (2023-10-01 14:30:45) -- 缺單引號,語法錯
// 或: INSERT ... VALUES (2023/10/01 14:30:45) -- 斜杠分隔,SQL Server 可能報錯
// 或: INSERT ... VALUES (10/01/2023 2:30:45 PM) -- 美式格式,日月顛倒導致數據錯誤

四、黃金法則:參數化查詢

參數化查詢完全避開格式化、單引號、SQL 注入、文化差異所有問題。

ADO.NET 原生寫法


string sql = "INSERT INTO Orders (CustomerId, OrderTime, Amount) VALUES (@CustId, @OrderTime, @Amt)";

using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand(sql, conn))
{
    cmd.Parameters.Add("@CustId", SqlDbType.Int).Value = 1001;
    cmd.Parameters.Add("@OrderTime", SqlDbType.DateTime2).Value = DateTime.Now; // 直接傳 DateTime 對象!
    cmd.Parameters.Add("@Amt", SqlDbType.Decimal).Value = 123.45m;
    
    conn.Open();
    cmd.ExecuteNonQuery();
}

Dapper / Entity Framework Core (現代 ORM)


// Dapper
conn.Execute("INSERT INTO Orders (CustomerId, OrderTime) VALUES (@CustId, @Time)", 
    new { CustId = 1001, Time = DateTime.Now });

// EF Core
var order = new Order { CustomerId = 1001, OrderTime = DateTime.Now };
context.Orders.Add(order);
context.SaveChanges();
// 無需關心 SQL 格式,ORM 自動生成參數化 SQL

五、進階:DateTime Kind 與時區處理

SQL Server datetime/datetime2 不存儲時區信息。最佳實踐:

  1. 存儲 UTC:`DateTime.UtcNow` 或 `DateTimeOffset.UtcNow` 存入 DB。
  2. 讀取指定 Kind:讀出後手動標記 `DateTime.SpecifyKind(dt, DateTimeKind.Utc)`。
  3. 展示時轉換:前端/報表層根據用戶時區將 UTC 轉為本地時間 (`TimeZoneInfo.ConvertTime`)。

// 寫入 UTC
cmd.Parameters.Add("@CreatedUtc", SqlDbType.DateTime2).Value = DateTime.UtcNow;

// 讀出並標記
DateTime utcFromDb = reader.GetDateTime(0);
DateTime utcAware = DateTime.SpecifyKind(utcFromDb, DateTimeKind.Utc);

// 轉為用戶本地時間 (假設用戶在中國標準時間)
TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(utcAware, cst);

六、SQL Server 類型選擇建議

類型精度範圍存儲建議
datetime~3.33ms1753-99998 bytes舊系統兼容
datetime2(n)100ns (n=7)0001-99996-8 bytes新項目首選,精度高、範圍廣、符合標準
datetimeoffset(n)100ns0001-99998-10 bytes需保留時區偏移時使用
date1 天0001-99993 bytes僅需日期 (生日、證件有效期)
time(n)100ns00:00-23:593-5 bytes僅需時間 (班次、鬧鐘)

總結

  1. 永不拼接 SQL 字符串 —— 參數化查詢是鐵律。
  2. C# 直接傳 DateTime 對象 給參數,不要 .ToString()。
  3. 統一使用 UTC 存儲,展示層做時區轉換。
  4. 新表優先用 datetime2,避開 datetime 精度與範圍限制。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved