概述
寫下本文緣於前幾天博客園一位朋友發表了一篇NET面試題的文章其中一個關於DateTime的問題引起了大家激烈的爭論鑒於日期時間類型是大家開發中會頻繁使用的一個中數據類型這裡我們有必要來對NET Framework中的日期時間類型做一個深入的認識
從NET Framework 開始就提供了DateTime類型來表示一個日期時間類型它是一個結構類型並且不可以為空這在一定程度上給我們在往數據庫中保存數據時帶來了很大的麻煩因為我們知道在數據庫中datatime類型是可以為Null的為了解決這個問題不得不經常使用DateTimeMinValue來表示但這並不是我們想要的幸運的是到了NET Framework 中提供了可空類型此時我們就可以使用Nullable<DateTime>來表示一個日期時間類型它是可以為Null的這給我們帶來了極大的方便
到了NET Framework 中又為我們提供了一個全新的日期時間類型DateTimeOffset它通常以相對於格林威治時間(GMTGreenwich Mean Time)的日期和時間來表示格林威治時間又被稱為國際標准時間UTC(Universal Time Code)除此之外在NET Framework中還為我們提供了TimeZone類用來表示時區到了NET Framework 中對TimeZone類進一步增強提供了TimeZoneInfo類來表示世界上的任何時區
在本文中我們將對以上日期時間類型時區類進行詳細的介紹
DateTime和DateTimeOffset
DateTime 值類型表示值范圍在公元 年 月 日午夜:: 到公元 年月日晚上:: 之間的日期和時間DateTimeOffset包含一個DateTime 值以及一個名為Offset屬性該屬性用於確定當前 DateTimeOffset 實例的日期和時間與UTC之間的差值我們先來看一下這段代碼的輸出
static void Main(string[] args)
{
ConsoleWriteLine(DateTimeNow);
ConsoleWriteLine(DateTimeOffsetNow);
}
輸出結果為
可以看到DateTime輸出了日期和時間DateTimeOffset類型不僅輸出了日期和時間還給出當前時間與UTC之間的差值接下來我們再看一段代碼如何手工構造一個DateTime和DateTimeOffset實例
static void Main(string[] args)
{
DateTime dateA = new DateTime();
DateTimeOffset dateB = new DateTimeOffset(
new TimeSpan());
ConsoleWriteLine(dateA);
ConsoleWriteLine(dateB);
}
輸出結果如下圖所示
轉換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();
DateTime dateB = DateTimeSpecifyKind(dateA DateTimeKindUtc);
DateTimeOffset dateC = dateB;
ConsoleWriteLine(dateB);
ConsoleWriteLine(dateC);
}
輸出結果如下圖所示
再來寫一個表示本地時間的轉換如下代碼所示
static void Main(string[] args)
{
DateTime dateA = new DateTime( );
DateTime dateB = DateTimeSpecifyKind(dateA DateTimeKindLocal);
DateTimeOffset dateC = dateB;
ConsoleWriteLine(dateB);
ConsoleWriteLine(dateC);
}
輸出結果如下圖所示
如果在轉換時指定的時間是Unspecified轉換後產生的DateTimeOffset的值的偏移量將會為本地時區如下代碼所示
static void Main(string[] args)
{
DateTime dateA = new DateTime( );
DateTime dateB = DateTimeSpecifyKind(dateA DateTimeKindUnspecified);
DateTimeOffset dateC = dateB;
ConsoleWriteLine(dateB);
ConsoleWriteLine(dateC);
}
輸出結果如下圖所示可以看到它產生的輸出是本地時區
這一點其實從DateTimeOffset的一個參數為DateTime的構造函數中就能夠看出來它只判斷DateTime是否為UTC否則就取當前本地時區的偏移量
public DateTimeOffset(DateTime dateTime) {
TimeSpan offset;
if (dateTimeKind != DateTimeKindUtc) {
// Local 和 Unspecified 都轉換為Local
offset = TimeZoneCurrentTimeZoneGetUtcOffset(dateTime);
}
else {
offset = new TimeSpan();
}
m_offsetMinutes = ValidateOffset(offset);
m_dateTime = ValidateDate(dateTime offset);
}
轉換DateTimeOffset為DateTime
在轉換一個DateTimeOffset類型為DateTime類型時可以使用如下幾個屬性
DateTime屬性返回一個指示為Unspecified的DateTime值
UtcDateTime屬性返回一個指示為UTC的DateTime值如果偏移量不為它會轉換為UTC時間
LocalDateTime屬性返回一個指示為Local的DateTime值
這三個屬性的在DateTimeOffset中的定義如下代碼所示
public DateTime DateTime {
get {
return ClockDateTime;
}
}
public DateTime UtcDateTime {
get {
return DateTimeSpecifyKind(m_dateTime DateTimeKindUtc);
}
}
public DateTime LocalDateTime {
get {
return UtcDateTimeToLocalTime();
}
} 可以看到在LocalDateTime屬性中首先會獲取UtcDateTime然後調用ToLocalTime()將其轉換為本地時間我們現在來看一組測試代碼
static void Main(string[] args)
{
DateTimeOffset basic = new DateTimeOffset(
new TimeSpan());
DateTime dateA = basicDateTime;
DateTime dateB = basicLocalDateTime;
DateTime dateC = basicUtcDateTime;
ConsoleWriteLine(basic);
ConsoleWriteLine();
ConsoleWriteLine(Unspecified DateTime: + dateA);
ConsoleWriteLine(Local DateTIme: + dateB);
ConsoleWriteLine(UTC DateTime: + dateC);
}最後輸出的結果如下圖所示
在DateTime和DateTimeOffset之間選擇
上面說了這麼多關於DateTime和DateTimeOffset類型如何在DateTime和DateTimeOffset之間進行選擇呢?從前面的示例中大家已經看到了DateTime只可以表示UTC或者本地時區的時間或者不確定的時區這給我們應用程序的移植帶來了極大的麻煩除非你指定它表示的是UTC否則在移植應用程序時會受到諸多的限制例如下面這段最簡單的代碼
static void Main(string[] args)
{
DateTime date = DateTimeNow;
ConsoleWriteLine(date);
}如果DateTime表示本地時區那麼應用程序在本地時區內移植是不會有問題的但是如果你的應用程序需要對不同的時區都支持
建議在使用時盡量將DateTime的Kind屬性設置為Utc這一點尤其重要否則就需要考慮使用DateTimeOffset類型
與DateTime類型不同的是DateTimeOffset它唯一的標識了一個明確的時間點即時間值以及相對於UTC的偏移量它並不依賴於某個特定的時區在大多數情況下應當考慮使用DateTimeOffset來代替DateTime類型並且在SQL Server 中也已經提供了對於DateTimeOffset數據類型的支持詳細信息可以參考這篇文章《SQL Server 中的新日期數據類型》
但是DateTimeOffset類型並不是完全用來代替DateTime類型在應用程序只用到日期而不涉及時間如出生日期用DateTime類型是沒有任何問題的
時區支持
在NET Framework 之前我們只能使用TimeZone來表示一個時區但是Timezone功能很有限它只能識別本地時區可以在UTC和本地時間之間轉換時間而TimeZoneInfo 對TimeZone進行了很大的增強它可以表示世界上的任意時區 看下面一段代碼
static void Main(string[] args)
{
TimeZone timeZoneA = TimeZoneCurrentTimeZone;
ConsoleWriteLine(timeZoneAStandardName);
TimeZoneInfo timeZoneB = TimeZoneInfoLocal;
ConsoleWriteLine(timeZoneBStandardName);
TimeZoneInfo timeZoneC = TimeZoneInfoUtc;
ConsoleWriteLine(timeZoneCStandardName);
}輸出結果如下圖所示
TimeZone提供的屬性和方法非常有限TimeZoneInfo在這方面就顯的非常豐富我們可以使用TimeZoneInfo在兩個不同的時區之間轉換時間如下面的代碼
static void Main(string[] args)
{
DateTimeOffset chinaDate = DateTimeOffsetNow;
DateTimeOffset easternDate = TimeZoneInfoConvertTime(
chinaDate
TimeZoneInfoFindSystemTimeZoneById(Eastern Standard Time));
ConsoleWriteLine(Now: {} chinaDate);
ConsoleWriteLine(Now in Eastern: {} easternDate);
}
輸出結果如下圖所示
這裡使用FindSystemTimeZoneById方法來根據ID來獲取時區在推出TimeZoneInfo之後在以後的開發中完全可以放棄TimeZone類了TimeZoneInfo已經完全包含了它
總結
本文介紹了NET Framework中對於日期時間類型的支持希望對大家有所幫助
From:http://tw.wingwit.com/Article/program/net/201311/13894.html