使用.NET Framework中新的日期時間類型

本文首發博客園,作者TerryLee,原文地址:使用.NET Framework中新的日期時間類型
 

概述

寫下本文緣於前幾天博客園一位朋友發表了一篇.NET面試題的文章,其中一個關於DateTime的問題引起了大家激烈的爭論,鑑於日期時間類型是大家開發中會頻繁使用的一箇中數據類型,這裏我們有必要來對.NET Framework中的日期時間類型做一個深入的認識。
從.NET Framework 1.0開始,就提供了DateTime類型來表示一個日期時間類型,它是一個結構類型,並且不可以爲空,這在一定程度上給我們在往數據庫中保存數據時帶來了很大的麻煩,因爲我們知道,在數據庫中datatime類型是可以爲Null的,爲了解決這個問題,不得不經常使用DateTime.MinValue來表示,但這並不是我們想要的。幸運的是到了.NET Framework 2.0中,提供了可空類型,此時我們就可以使用Nullable<DateTime>來表示一個日期時間類型,它是可以爲Null的,這給我們帶來了極大的方便。
到了.NET Framework 3.5中,又爲我們提供了一個全新的日期時間類型DateTimeOffset,它通常以相對於格林威治時間(GMT,Greenwich Mean Time)的日期和時間來表示,格林威治時間又被稱爲國際標準時間UTC(Universal Time Code)。除此之外,在.NET Framework中還爲我們提供了TimeZone類用來表示時區,到了.NET Framework 3.5中,對TimeZone類進一步增強,提供了TimeZoneInfo類來表示世界上的任何時區。
在本文中,我們將對以上日期時間類型、時區類進行詳細的介紹。

DateTime和DateTimeOffset

DateTime 值類型表示值範圍在公元0001 年1 月1 日午夜12:00:00 到公元9999 年12月31日晚上11:59:59 之間的日期和時間;DateTimeOffset包含一個DateTime 值以及一個名爲Offset屬性,該屬性用於確定當前 DateTimeOffset 實例的日期和時間與UTC之間的差值,我們先來看一下這段代碼的輸出:
static void Main(string[] args)
{
    Console.WriteLine(DateTime.Now);
    Console.WriteLine(DateTimeOffset.Now);
}
輸出結果爲:
TerryLee_0179
可以看到,DateTime輸出了日期和時間,DateTimeOffset類型不僅輸出了日期和時間,還給出當前時間與UTC之間的差值。接下來我們再看一段代碼,如何手工構造一個DateTime和DateTimeOffset實例:
static void Main(string[] args)
{
    DateTime dateA = new DateTime(2008,8,26,23,1,48);
    DateTimeOffset dateB = new DateTimeOffset(2008, 8, 26, 23, 1, 48,
        new TimeSpan(4,0,0));
    Console.WriteLine(dateA);
    Console.WriteLine(dateB);
}
輸出結果如下圖所示:
TerryLee_0180

轉換DateTime爲DateTimeOffset

通過上面的兩個例子,大家應該對DateTimeOffset有了一個基本的認識,DateTimeOffset提供了比DateTime更高程度的時區識別能力,接下來我們看如何在DateTime和DateTimeOffset之間進行轉換,開始之前我們先了解一下DateTimeKind枚舉,在DateTime中提供了一個名爲Kind的屬性,它用來指示DateTime對象是表示本地時間、國際標準時間(UTC),還是既不指定爲本地時間,也不指定爲國際標準時間(UTC),DateTimeKind的定義如下:
public enum DateTimeKind
{
    Unspecified,
    Utc,
    Local
}
對於UTC 和本地DateTime值,得到的DateTimeOffset值的Offset屬性準確反映UTC 或本地時區偏移量,如下面的代碼將 UTC 時間轉換爲與之等效的DateTimeOffset值:
static void Main(string[] args)
{
    DateTime dateA = new DateTime(2008,8,24,23,33,58);
    DateTime dateB = DateTime.SpecifyKind(dateA, DateTimeKind.Utc);
    DateTimeOffset dateC = dateB;
    Console.WriteLine(dateB);
    Console.WriteLine(dateC);
}
輸出結果如下圖所示:
TerryLee_0181
再來寫一個表示本地時間的轉換,如下代碼所示:
static void Main(string[] args)
{
    DateTime dateA = new DateTime(2008, 8, 24, 23, 33, 58);
    DateTime dateB = DateTime.SpecifyKind(dateA, DateTimeKind.Local);
    DateTimeOffset dateC = dateB;
    Console.WriteLine(dateB);
    Console.WriteLine(dateC);
}
輸出結果如下圖所示:
TerryLee_0183
如果在轉換時指定的時間是Unspecified,轉換後產生的DateTimeOffset的值的偏移量將會爲本地時區,如下代碼所示:
static void Main(string[] args)
{
    DateTime dateA = new DateTime(2008, 8, 24, 23, 33, 58);
    DateTime dateB = DateTime.SpecifyKind(dateA, DateTimeKind.Unspecified);
    DateTimeOffset dateC = dateB;
    Console.WriteLine(dateB);
    Console.WriteLine(dateC);
}
輸出結果如下圖所示,可以看到它產生的輸出是本地時區:
TerryLee_0184
這一點其實從DateTimeOffset的一個參數爲DateTime的構造函數中就能夠看出來,它只判斷DateTime是否爲UTC,否則就取當前本地時區的偏移量:
public DateTimeOffset(DateTime dateTime) { 
    TimeSpan offset;
    if (dateTime.Kind != DateTimeKind.Utc) {
        // Local 和 Unspecified 都轉換爲Local
        offset = TimeZone.CurrentTimeZone.GetUtcOffset(dateTime); 
    }
    else { 
        offset = new TimeSpan(0); 
    }
    m_offsetMinutes = ValidateOffset(offset); 
    m_dateTime = ValidateDate(dateTime, offset);
} 

轉換DateTimeOffset爲DateTime

在轉換一個DateTimeOffset類型爲DateTime類型時,可以使用如下幾個屬性:
DateTime屬性:返回一個指示爲Unspecified的DateTime值;
UtcDateTime屬性:返回一個指示爲UTC的DateTime值,如果偏移量不爲0,它會轉換爲UTC時間;
LocalDateTime屬性:返回一個指示爲Local的DateTime值。
這三個屬性的在DateTimeOffset中的定義如下代碼所示:
public DateTime DateTime {
    get { 
        return ClockDateTime;
    }
}
public DateTime UtcDateTime {
    get { 
        return DateTime.SpecifyKind(m_dateTime, DateTimeKind.Utc); 
    }
} 
public DateTime LocalDateTime {
    get {
        return UtcDateTime.ToLocalTime(); 
    }
} 
可以看到,在LocalDateTime屬性中首先會獲取UtcDateTime,然後調用ToLocalTime()將其轉換爲本地時間。我們現在來看一組測試代碼:
static void Main(string[] args)
{
    DateTimeOffset basic = new DateTimeOffset(2008, 8, 24, 23, 33, 58,
            new TimeSpan(8,0,0));
    DateTime dateA = basic.DateTime;
    DateTime dateB = basic.LocalDateTime;
    DateTime dateC = basic.UtcDateTime;
    Console.WriteLine(basic);
    Console.WriteLine("--------------------------");
    Console.WriteLine("Unspecified DateTime:" + dateA);
    Console.WriteLine("Local DateTIme:" + dateB);
    Console.WriteLine("UTC DateTime:" + dateC);
}
最後輸出的結果如下圖所示:
TerryLee_0185 

在DateTime和DateTimeOffset之間選擇

上面說了這麼多關於DateTime和DateTimeOffset類型,如何在DateTime和DateTimeOffset之間進行選擇呢?從前面的示例中大家已經看到了,DateTime只可以表示UTC或者本地時區的時間,或者不確定的時區,這給我們應用程序的移植帶來了極大的麻煩,除非你指定它表示的是UTC,否則在移植應用程序時會受到諸多的限制,例如下面這段最簡單的代碼:
static void Main(string[] args)
{
    DateTime date = DateTime.Now;
    Console.WriteLine(date);
}
如果DateTime表示本地時區,那麼應用程序在本地時區內移植是不會有問題的。但是如果你的應用程序需要對不同的時區都支持,建議在使用時儘量將DateTime的Kind屬性設置爲Utc,這一點尤其重要,否則就需要考慮使用DateTimeOffset類型。
與DateTime類型不同的是,DateTimeOffset它唯一的標識了一個明確的時間點,即時間值以及相對於UTC的偏移量,它並不依賴於某個特定的時區,在大多數情況下,應當考慮使用DateTimeOffset來代替DateTime類型。並且在SQL Server 2008中也已經提供了對於DateTimeOffset數據類型的支持,詳細信息可以參考這篇文章《SQL Server 2008中的新日期數據類型》。
但是DateTimeOffset類型並不是完全用來代替DateTime類型,在應用程序只用到日期而不涉及時間,如出生日期,用DateTime類型是沒有任何問題的。

時區支持

在.NET Framework 3.5之前,我們只能使用TimeZone來表示一個時區,但是Timezone功能很有限,它只能識別本地時區,可以在UTC和本地時間之間轉換時間;而TimeZoneInfo 對TimeZone進行了很大的增強,它可以表示世界上的任意時區 。看下面一段代碼:
static void Main(string[] args)
{
    TimeZone timeZoneA = TimeZone.CurrentTimeZone;
    Console.WriteLine(timeZoneA.StandardName);
    TimeZoneInfo timeZoneB = TimeZoneInfo.Local;
    Console.WriteLine(timeZoneB.StandardName);
    TimeZoneInfo timeZoneC = TimeZoneInfo.Utc;
    Console.WriteLine(timeZoneC.StandardName);
}
輸出結果如下圖所示:
TerryLee_0186 
TimeZone提供的屬性和方法非常有限,TimeZoneInfo在這方面就顯的非常豐富,我們可以使用TimeZoneInfo在兩個不同的時區之間轉換時間,如下面的代碼:
static void Main(string[] args)
{
    DateTimeOffset chinaDate = DateTimeOffset.Now;
    DateTimeOffset easternDate = TimeZoneInfo.ConvertTime(
        chinaDate,
        TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"));
    Console.WriteLine("Now: {0}", chinaDate);
    Console.WriteLine("Now in Eastern: {0}", easternDate);
}
輸出結果如下圖所示:
TerryLee_0187 
這裏使用FindSystemTimeZoneById方法來根據ID來獲取時區。在推出TimeZoneInfo之後,在以後的開發中完全可以放棄TimeZone類了,TimeZoneInfo已經完全包含了它。

總結

本文介紹了.NET Framework中對於日期時間類型的支持,希望對大家有所幫助。

0

收藏

lihuijun

180篇文章,72W+人氣,0粉絲

Ctrl+Enter 發佈

發佈

取消

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章