博客 / 詳情

返回

一文搞懂 C#.NET DateTimeOffset:時間戳、時區與偏移量全掌握

簡介

DateTimeOffsetSystem 命名空間中的結構體,用於表示特定時間點及其相對於協調世界時(UTC)的偏移量。相比 DateTime,它更適合處理跨時區場景,確保時間數據在全球化應用中的一致性和精確性。

  • 定義:System.DateTimeOffset 表示帶有固定時區偏移量的點時間。
  • 用途:既保留了 UTC 時間戳,也攜帶了相對於 UTC 的偏移量(Offset),在分佈式、多時區場景下更可靠。
  • 內部存儲:

    • DateTime _dateTimeKind 固定為 Unspecified
    • TimeSpan _offset(-14h 到 +14h 範圍)
var dto = new DateTimeOffset(2025, 8, 8, 14, 30, 0, TimeSpan.FromHours(+8));
graph TD
    A[DateTimeOffset] --> B[DateTime]
    A --> C[Offset]
    B --> D[日期部分]
    B --> E[時間部分]
    C --> F[UTC偏移量]

DateTimeOffset 結構詳解

內部表示

public struct DateTimeOffset : IComparable, IFormattable, ... 
{
    private readonly DateTime _dateTime;
    private readonly short _offsetMinutes;
    
    // 屬性
    public DateTime DateTime { get; }       // 日期時間部分
    public DateTime UtcDateTime { get; }    // UTC 等效時間
    public TimeSpan Offset { get; }         // 偏移量
    public long Ticks { get; }              // 自 0001-01-01 的滴答數
    public DateTime Date { get; }           // 日期部分
    public int Year { get; }                // 年
    // ... 其他屬性類似 DateTime
}

核心API

屬性

屬性 説明
DateTime 去除偏移量的本地時間(DateTimeKind.Unspecified
UtcDateTime Offset 轉換到 UTC 的 DateTimeKind.Utc
Offset 相對於 UTC 的偏移量(TimeSpan
LocalDateTime 按當前系統時區再次轉換的本地時間(DateTimeKind.Local
Date DateTime.Date 部分,保留偏移量
Year/Month/Day 年、月、日
Hour/Minute/Second 小時、分鐘、秒
Ticks 0001-01-01T00:00:00 起的刻度數(100 ns 為單位,不含偏移)
Now 當前本地時間帶本地偏移(等同 new DateTimeOffset(DateTime.Now)
UtcNow 當前 UTC 時間,偏移量為 TimeSpan.Zero

方法

  • Add(TimeSpan):添加時間跨度。
  • AddDays(double)、AddHours(double) 等:添加特定單位。
  • Subtract(DateTimeOffset):計算時間差(返回 TimeSpan)。
  • ToString(string):格式化輸出。
  • ToUniversalTime():轉換為 UTC
  • ToOffset(TimeSpan):轉換為指定偏移。
  • Parse(string)、TryParse(string, out DateTimeOffset):解析字符串。

靜態方法

  • DateTimeOffset.Compare(DateTimeOffset, DateTimeOffset):比較兩個時間。
  • DateTimeOffset.ParseExact(string, string, IFormatProvider):精確解析。

構造與轉換

常用構造器

// 指定本地時間和偏移
var dto1 = new DateTimeOffset(2025,8,8,14,30,0, TimeSpan.FromHours(+8));

// 從 DateTime 創建(注意 Kind)
var dtLocal = DateTime.Now;  // Kind.Local
var dto2 = new DateTimeOffset(dtLocal);

// 從 UTC DateTime 創建
var dtUtc = DateTime.UtcNow;
var dto3 = new DateTimeOffset(dtUtc).ToOffset(TimeSpan.Zero);

偏移轉換

// 將 dto 轉到另一個時區偏移(僅改變 Offset,不更改瞬時時間)
var dtoTokyo = dto.ToOffset(TimeSpan.FromHours(+9));

// 獲取共同的絕對時刻
DateTimeOffset a = dto1, b = dtoTokyo;
bool sameInstant = a.ToUniversalTime() == b.ToUniversalTime();  // true

與 DateTime 相互轉換

// DateTimeOffset -> DateTime (Unspecified)
DateTime dtUnspecified = dto.DateTime;

// DateTimeOffset -> UTC DateTime
DateTime dtUtc = dto.UtcDateTime;

// DateTimeOffset -> Local DateTime
DateTime dtLocal2 = dto.LocalDateTime;

// DateTime -> DateTimeOffset
var dtoFromDt = new DateTimeOffset(dtUnspecified, TimeZoneInfo.Local.GetUtcOffset(dtUnspecified));

格式化與解析

標準格式字符串

格式符 説明 示例輸出
"o" 往返格式 2023-10-05T14:30:00.0000000+08:00
"r" RFC1123 Thu, 05 Oct 2023 14:30:00 +0800
"u" 通用可排序 2023-10-05 06:30:00Z
"s" 可排序 2023-10-05T14:30:00
"g" 常規短格式 10/5/2023 2:30 PM
"G" 常規長格式 10/5/2023 2:30:00 PM

格式化

  • 標準格式
dto.ToString("o");  // 2025-08-08T14:30:00.0000000+08:00
dto.ToString("u");  // 2025-08-08 14:30:00Z
  • 自定義格式
dto.ToString("yyyy-MM-dd HH:mm zzz");   // 2025-08-08 14:30 +08:00
dto.ToString("yyyy-MM-dd HH:mm K");     // K 同 zzz

解析

  • Parse / TryParse
DateTimeOffset.Parse("2025-08-08T14:30:00+08:00");
DateTimeOffset.TryParse("2025-08-08 06:30:00Z", out var dto);
  • ParseExact / TryParseExact
DateTimeOffset.ParseExact("2025/08/08 14:30 +0800",
    "yyyy/MM/dd HH:mm zzz", CultureInfo.InvariantCulture);
DateTimeOffset.TryParseExact(input, 
    new[]{ "o","yyyy-MM-dd HH:mm zzz"}, 
    CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out dto);

比較與運算

var a = new DateTimeOffset(2025,8,8,10,0,0,TimeSpan.FromHours(+1));
var b = new DateTimeOffset(2025,8,8,18,0,0,TimeSpan.FromHours(+9));
// 絕對瞬時時間相同
bool eq = a.ToUniversalTime() == b.ToUniversalTime();  // true

// 差值
TimeSpan diff = b - a;  // Zero
  • 支持 +/- TimeSpan、- DateTimeOffset(結果 TimeSpan
  • 支持 Compare(), Equals(), 運算符 <, > 等基於 UTC 比較

與 DateTime 的比較

特性 DateTime DateTimeOffset
時區信息 僅有 Kind,不含偏移量 明確定義了相對於 UTC 的偏移量
存儲一致性 若用本地 DateTime.Now 存庫,會丟失上下文 可存儲為 ISO 8601 帶偏移字符串,恢復無歧義
夏令時問題 依賴系統時區轉換,歧義轉化 偏移固定,不受夏令時影響
跨系統/跨語言互操作性 存 UTC 時需額外約定 標準化輸出帶偏移,易互操作
推薦用於多時區業務場景 較麻煩 首選

資源和文檔

  • DateTimeOffset:https://learn.microsoft.com/en-us/dotnet/api/system.datetimeo...
  • TimeZoneInfo:https://learn.microsoft.com/en-us/dotnet/api/system.timezoneinfo
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.