日期時間格式化是每位 C# 開發者的必修課。本文系統整理 DateTime.ToString 的標準格式字串、自訂格式模式、文化特性控制、效能技巧與常見陷阱,並提供可直接複製的速查表。
單一字元指定符,對應 DateTimeFormatInfo 屬性,受 CultureInfo 影響。
| 格式 | 說明 | 範例 (en-US) | 範例 (zh-TW) |
|---|---|---|---|
| d | 短日期 | 6/15/2024 | 2024/6/15 |
| D | 長日期 | Saturday, June 15, 2024 | 2024年6月15日 |
| f | 完整日期+短時間 | Saturday, June 15, 2024 3:30 PM | 2024年6月15日 下午 03:30 |
| F | 完整日期+長時間 | Saturday, June 15, 2024 3:30:45 PM | 2024年6月15日 下午 03:30:45 |
| g | 一般 (短日期+短時間) | 6/15/2024 3:30 PM | 2024/6/15 下午 03:30 |
| G | 一般 (短日期+長時間) | 6/15/2024 3:30:45 PM | 2024/6/15 下午 03:30:45 |
| t | 短時間 | 3:30 PM | 下午 03:30 |
| T | 長時間 | 3:30:45 PM | 下午 03:30:45 |
| u | 通用可排序 (UTC) | 2024-06-15 15:30:45Z | 同左 |
| U | 完整日期時間 (UTC) | Saturday, June 15, 2024 11:30:45 PM | 2024年6月15日 上午 11:30:45 |
| o / O | 往返格式 (ISO 8601) | 2024-06-15T15:30:45.1234567+08:00 | 同左 |
| r / R | RFC1123 | Sat, 15 Jun 2024 15:30:45 GMT | 同左 |
| s | 可排序 (ISO 8601, 本地) | 2024-06-15T15:30:45 | 同左 |
| y / Y | 年月 | June, 2024 | 2024年6月 |
組合下列模式字元構建任意格式,區分大小寫。
| 模式 | 意義 | 範例 |
|---|---|---|
| y, yy, yyy, yyyy | 年 (1-4 位) | 24, 2024 |
| M, MM, MMM, MMMM | 月 (數字/縮寫/全名) | 6, 06, 6月, 六月 |
| d, dd, ddd, dddd | 日/星期 | 5, 05, 三, 星期三 |
| h, hh | 12小時制 | 3, 03 |
| H, HH | 24小時制 | 15, 15 |
| m, mm | 分鐘 | 5, 05 |
| s, ss | 秒 | 9, 09 |
| f..fffffff | 秒小數 (1-7 位) | .1, .12, .1234567 |
| t, tt | AM/PM 首字/完整 | 上, 上午 |
| z, zz, zzz | 時區偏移 | +8, +08, +08:00 |
| :, / | 時間/日期分隔符 (依文化特性) | :, / |
| 'text' 或 \c | 常值字元 | '年'M'月' → 6月 |
var dt = new DateTime(2024, 6, 15, 15, 30, 45, 123, DateTimeKind.Local);
dt.ToString("yyyy-MM-dd") // 2024-06-15
dt.ToString("yyyy/MM/dd HH:mm:ss") // 2024/06/15 15:30:45
dt.ToString("yyyy年MM月dd日") // 2024年06月15日 (需配合 zh-TW culture)
dt.ToString("HH:mm:ss.fff") // 15:30:45.123
dt.ToString("o") // 2024-06-15T15:30:45.1230000+08:00 (往返/序列化最佳)
// 明確指定文化特性,避免伺服器預設文化導致輸出不一致
var tw = new CultureInfo("zh-TW");
var us = CultureInfo.InvariantCulture; // 固定不變文化,適合日誌、API、資料庫
dt.ToString("D", tw) // 2024年6月15日
dt.ToString("D", us) // Saturday, 15 June 2024 (Invariant 以英文為基準)
最佳實務:所有對外輸出(API JSON、資料庫儲存、日誌)一律使用 CultureInfo.InvariantCulture 或 ISO 8601 (o/O/s/u);僅 UI 顯示才套用使用者文化特性。
var now = DateTime.Now;
now.Year / now.Month / now.Day / now.Hour / now.Minute / now.Second / now.Millisecond
now.DayOfWeek / now.DayOfYear / now.Ticks / now.TimeOfDay
now.Date // 日期部分 (時間歸零)
now.ToLocalTime() / now.ToUniversalTime()
now.AddDays(7) / now.AddHours(-3) / now.AddMonths(1)
now.Subtract(other).TotalDays
DateTime.DaysInMonth(2024, 2) // 29
static readonly CultureInfo Invariant = CultureInfo.InvariantCulture;string.Format("{0:yyyy-MM-dd}", dt) 較 dt.ToString("yyyy-MM-dd") 稍慢(需裝箱)。span.Format 或使用 Utf8Formatter (System.Buffers.Text) 直接寫入 Span<byte>,避免字串配置。DateTimeFormatInfo.GetFormat 內部已優化,無需額外處理。M = 月,m = 分。寫錯會產生離譜結果。YYYY 非標準模式,會被視為常值字串 "YYYY"。"yyyy'年'MM'月'" 若漏掉尾單引號會拋 FormatException。DateTime.Parse("2024-06-15") 產生 Unspecified,後續 ToUniversalTime() 行為不可預期。建議 DateTime.SpecifyKind(dt, DateTimeKind.Local) 或改用 DateTimeOffset。DateTimeOffset:內建 Offset,解決時區歧義,序列化含時區資訊,適合分散式系統。Instant、ZonedDateTime、LocalDateTime 等豐富型別,正規處理夏令時、閏秒、歷史時區變更,強烈建議新專案採用。熟練掌握標準/自訂格式字串、善用 InvariantCulture、警惕 DateTime.Kind,即能寫出正確、高效、可國際化的日期時間輸出。若專案涉及跨時區、排程、歷史資料,請優先評估 DateTimeOffset 或 NodaTime。