System.DateTime 詳解(續)

作者: Artech  來源: 博客園  發布時間: 2010-09-05 22:04  閱讀: 2915 次  推薦: 0   原文鏈接   [收藏]  

  在《System.DateTime 詳解》一文中,我們從跨時區的角度剖析了我們熟悉的System.DateTime類型。如果你還是采用傳統的ADO.NET編程方式,并使用DataSet作為數據實體,可能你會熟悉System.Data.DataSetDateTime這么一個類型。這個類型也是為實現跨時區場景下對時間處理而設計的,為了對前文的補充,這篇文章就來談談基于DataSet的時間處理問題。

  一、你是否關注過DataColumn的DateTimeMode屬性

  在ADO.NET編程模型中,DataColumn代表DataTable的一個數據列,大家在熟悉不過了。不過,是否有人關注過一個名稱為DateTimeMode屬性,該屬性在DataColumn中的定義如下:

   1: public class DataColumn : MarshalByValueComponent
   2: {
   3:     //Others...
   4:     public DataSetDateTime DateTimeMode { get; set; }
   5: }

  從上面的代碼我們可以看出,DateTimeMode屬性的類型為DataSetDateTime,這實際上是一個枚舉類型。下面給出了DataSetDateTime的定義,該枚舉一共包含4個枚舉值:Local、Utc、Unspecified和UnspecifiedLocal。

   1: public enum DataSetDateTime
   2: {
   3:     Local = 1,
   4:     Unspecified = 2,
   5:     UnspecifiedLocal = 3,
   6:     Utc = 4
   7: }

  如果你讀過前文,了解了DateTimeKind,技術你之前不曾關注過這個類型,也會猜出DataColumn用類型為DataSetDateTime的DateTimeMode屬性指定時間的Kind。那么對于不同的DateTimeMode設置之間有何差異?為什么DataSetDateTime提供了另一個額外的成員UnspecifiedLocal呢?

  二、不同的DateTimeMode設置對DateTimeKind的影響

  這個問題很簡單,對于數據類型為DateTime的DataColumn,如果你對DateTimeMode屬性設置了不同的DataSetDateTime枚舉值,當你對其賦值的時候,系統會自動將設置的時間轉換成相應DateTimeKind的時間。下面的列表提供了基于不同DataSetDateTime枚舉值采用的的轉換規則:

  • DataSetDateTime.Local: 對于DateTimeKind.Local時間,不做任何轉換;對于DateTimeKind.Utc時間,基于時區偏移量進行轉換,并將Kind屬性轉換成DateTimeKind.Local;對于DateTimeKind.Unspecified,直接將Kind屬性轉換成DateTimeKind.Local,時間值(年、月、日、時、分、秒、毫秒等)保持不變;
  • DataSetDateTime.Utc: 對于DateTimeKind.Utc時間,不做任何轉換;對于DateTimeKind.Local時間,基于時區偏移量進行轉換,并將Kind屬性轉換成DateTimeKind.Utc;對于DateTimeKind.Unspecified,直接將Kind屬性轉換成DateTimeKind.Utc,時間值(年、月、日、時、分、秒、毫秒等)保持不變;
  • DataSetDateTime.Unspecified|UnspecifiedLocal:對于任何DateTimeKind類型的時間,直接將Kind屬性轉換成DateTimeKind.Unspecified,時間值(年、月、日、時、分、秒、毫秒等)保持不變。

  在這里我需要強調一下,在前文中我們提到:不同是調用DateTime的ToLocalTime/ToUtcTime方法,還是調用TimeZoneInfo的ConvertToUtcTime/ConvertFromUtcTime,如果將DateTimeKind.Unspecified轉換成DateTimeKind.Local時間,實際上是將其當成DateTimeKind.Utc時間;反之,如果轉換成DateTimeKind.Utc時間,則當成是DateTimeKind.Local時間。但是,在這里DateTimeKind.Unspecified的時間值會保留,改變的僅僅是Kind屬性。

  三、一個簡單的例子

image   為了加深對上述轉換規則的理解,我寫了一個簡單的例子。首先我創建了一個ContractDataSet的強類型的DataSet,里面具有一個Contact數據表表示一個聯系人。Contact數據的結構如右圖所示:處理表示Id和名稱的兩個字段之外,我添加了四個DateTime類型的字段表示生日。它們分別是:LocalBirthday、UtcBirthday、UnspecifiedBirthday和UnspecifiedLocalBirthday,前綴表示該數據列采用的DateTimeMode。

  然后,我寫了下面三個輔助的方法:CreateContact通過傳入的表示生日的DateTime創建一個ContractDataSet,DisplayBirthday分別將上訴四個字段的時間和Kind打印出來。

   1: static ContactDataSet CreateContact(DateTime birthday)
   2: {
   3:     var ds = new ContactDataSet();
   4:     var row = ds.Contact.NewContactRow();
   5:     row.Id = Guid.NewGuid().ToString();
   6:     row.Name = "Foo";
   7:     SetBirthDay(row,birthday);
   8:     ds.Contact.AddContactRow(row);
   9:     return ds;
  10: }
  11: static void SetBirthDay(ContactDataSet.ContactRow row,DateTime birthDay)
  12: {
  13:     row.LocalBirthDay               = birthDay;
  14:     row.UtcBirthDay                 = birthDay;
  15:     row.UnspecifiedBirthDay         = birthDay;
  16:     row.UnspecifiedLocalBirthDay    = birthDay;
  17: }
  18: static void DispalyBirthday(ContactDataSet ds)
  19: {
  20:     var row = ds.Contact[0];
  21:     Console.WriteLine("\tLocal: \t\t\t{0}",             row.LocalBirthDay);
  22:     Console.WriteLine("\tUtc: \t\t\t{0}",               row.UtcBirthDay);
  23:     Console.WriteLine("\tUnspecified: \t\t{0}",         row.UnspecifiedBirthDay);
  24:     Console.WriteLine("\tUnspecifiedLocal: \t{0}\n",    row.UnspecifiedLocalBirthDay);
  25:  
  26:     Console.WriteLine("\tLocal: \t\t\t{0}",             row.LocalBirthDay.Kind);
  27:     Console.WriteLine("\tUtc: \t\t\t{0}",               row.UtcBirthDay.Kind);
  28:     Console.WriteLine("\tUnspecified: \t\t{0}",         row.UnspecifiedBirthDay.Kind);
  29:     Console.WriteLine("\tUnspecifiedLocal: \t{0}\n",    row.UnspecifiedLocalBirthDay.Kind);
  30: }

  我們的實例程序是這樣的:分別創建基于三種不同的DateTimeKind的DateTime對象,并據此創建三個ContractDataSet對象。最后調用DisplayBirthday方法將4個基于不同DateTimeMode的字段的時間和DateTimeKind打印出來。

   1: var ds1 = CreateContact(new DateTime(1981, 8, 24, 0, 0, 0, DateTimeKind.Local));
   2: var ds2 = CreateContact(new DateTime(1981, 8, 24, 0, 0, 0, DateTimeKind.Unspecified));
   3: var ds3 = CreateContact(new DateTime(1981, 8, 24, 0, 0, 0, DateTimeKind.Utc));
   4:  
   5: Console.WriteLine("DateTimeKind.Local");
   6: DispalyBirthday(ds1);
   7: Console.WriteLine("DateTimeKind.Unspecified");
   8: DispalyBirthday(ds2);
   9: Console.WriteLine("DateTimeKind.Utc");
  10: DispalyBirthday(ds3);

  最終的輸出結果證實了我們上述的關于時間轉換規則的結論:

   1: DateTimeKind.Local
   2:         Local:                  8/24/1981 12:00:00 AM
   3:         Utc:                    8/23/1981 4:00:00 PM
   4:         Unspecified:            8/24/1981 12:00:00 AM
   5:         UnspecifiedLocal:       8/24/1981 12:00:00 AM
   6:  
   7:         Local:                  Local
   8:         Utc:                    Utc
   9:         Unspecified:            Unspecified
  10:         UnspecifiedLocal:       Unspecified
  11:  
  12: DateTimeKind.Unspecified
  13:         Local:                  8/24/1981 12:00:00 AM
  14:         Utc:                    8/24/1981 12:00:00 AM
  15:         Unspecified:            8/24/1981 12:00:00 AM
  16:         UnspecifiedLocal:       8/24/1981 12:00:00 AM
  17:  
  18:         Local:                  Local
  19:         Utc:                    Utc
  20:         Unspecified:            Unspecified
  21:         UnspecifiedLocal:       Unspecified
  22:  
  23: DateTimeKind.Utc
  24:         Local:                  8/24/1981 8:00:00 AM
  25:         Utc:                    8/24/1981 12:00:00 AM
  26:         Unspecified:            8/24/1981 12:00:00 AM
  27:         UnspecifiedLocal:       8/24/1981 12:00:00 AM
  28:  
  29:         Local:                  Local
  30:         Utc:                    Utc
  31:         Unspecified:            Unspecified
  32:         UnspecifiedLocal:       Unspecified

  四、DataSetDateTime.Unspecified V.S. DataSetDateTime.UnspecifiedLocal

  到不前為止,我們貌似還看不到DataSetDateTime.Unspecified和DataSetDateTime.UnspecifiedLoca的差別。實際上,它們的差別體現在序列化上面:DataSetDateTime.UnspecifiedLoca在序列化的時候會保留基于當前時區的偏移量,而DataSetDateTime.Unspecified則不會。這個結論我也可以實例來證實,為此我寫了如下一段代碼對ContactDataSet進行序列化,并將序列化后的XML打印出來。

   1: var ds = CreateContact(new DateTime(1981, 8, 24, 0, 0, 0));
   2: using (MemoryStream stream = new MemoryStream(1000))
   3: {
   4:     ds.WriteXml(stream);
   5:     Console.WriteLine(ASCIIEncoding.ASCII.GetString(stream.GetBuffer()).Trim());
   6: }   

  從輸出的結果我們可以看出UnspecifiedBirthday和UnspecifiedLocalBirday之間的差別,后者有+8的偏移量,前者沒有。

   1: <ContactDataSet xmlns="http://tempuri.org/ContactDataSet.xsd">
   2:   <Contact>
   3:     <Id>a1548a6b-9b8b-4799-bc10-31337e70c831</Id>
   4:     <Name>Foo</Name>
   5:     <UnspecifiedLocalBirthDay>1981-08-24T00:00:00+08:00</UnspecifiedLocalBirthDay>
   6:     <UnspecifiedBirthDay>1981-08-24T00:00:00</UnspecifiedBirthDay>
   7:     <UtcBirthDay>1981-08-24T00:00:00Z</UtcBirthDay>
   8:     <LocalBirthDay>1981-08-24T00:00:00+08:00</LocalBirthDay>
   9:   </Contact>
  10: </ContactDataSet>
0
0
 
標簽:.NET
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()