一起谈.NET技术,System.DateTime 详解(续)

简介:   在《System.DateTime 详解》一文中,我们从跨时区的角度剖析了我们熟悉的System.DateTime类型。如果你还是采用传统的ADO.NET编程方式,并使用DataSet作为数据实体,可能你会熟悉System.Data.DataSetDateTime这么一个类型。

  在《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>
目录
相关文章
|
8天前
|
人工智能 开发框架 量子技术
【专栏】.NET 技术:驱动创新的力量
【4月更文挑战第29天】.NET技术,作为微软的开发框架,以其跨平台、开源和语言多样性驱动软件创新。它在云计算、AI/ML、混合现实等领域发挥关键作用,通过Azure、ML.NET等工具促进新兴技术发展。未来,.NET将涉足量子计算、微服务和无服务器计算,持续拓宽软件开发边界,成为创新的重要推动力。掌握.NET技术,对于开发者而言,意味着握有开启创新的钥匙。
|
8天前
|
开发框架 .NET C#
【专栏】理解.NET 技术,提升开发水平
【4月更文挑战第29天】本文介绍了.NET技术的核心概念和应用,包括其跨平台能力、性能优化、现代编程语言支持及Web开发等特性。文章强调了深入学习.NET技术、关注社区动态、实践经验及学习现代编程理念对提升开发水平的重要性。通过这些,开发者能更好地利用.NET构建高效、可维护的多平台应用。
|
8天前
|
机器学习/深度学习 vr&ar 开发者
【专栏】.NET 技术:引领开发新方向
【4月更文挑战第29天】本文探讨了.NET技术如何引领软件开发新方向,主要体现在三方面:1) 作为跨平台开发的先锋,.NET Core支持多操作系统和移动设备,借助.NET MAUI创建统一UI,适应物联网需求;2) 提升性能和开发者生产力,采用先进技术和优化策略,同时更新C#语言特性,提高代码效率和可维护性;3) 支持现代化应用架构,包括微服务、容器化,集成Kubernetes和ASP.NET Core,保障安全性。此外,.NET还不断探索AI、ML和AR/VR技术,为软件开发带来更多创新可能。
|
8天前
|
开发框架 Cloud Native 开发者
【专栏】剖析.NET 技术的核心竞争力
【4月更文挑战第29天】本文探讨了.NET框架在软件开发中的核心竞争力:1) .NET Core实现跨平台与云原生技术的融合,支持多操作系统和容器化;2) 提升性能和开发者生产力,采用JIT、AOT优化,提供C#新特性和Roslyn编译器平台;3) 支持现代化应用架构,包括微服务和容器化,内置安全机制;4) 丰富的生态系统和社区支持,拥有庞大的开发者社区和微软的持续投入。这些优势使.NET在竞争激烈的市场中保持领先地位。
|
8天前
|
开发框架 .NET 开发者
【专栏】领略.NET 技术的创新力量
【4月更文挑战第29天】.NET技术自ASP.NET起历经创新,现以.NET Core为核心,展现跨平台能力,提升性能与生产力,支持现代化应用架构。.NET Core使开发者能用同一代码库在不同操作系统上构建应用,扩展至移动和物联网领域。性能提升,C#新特性简化编程,Roslyn编译器优化代码。拥抱微服务、容器化,内置安全机制,支持OAuth等标准。未来.NET 6将引入更快性能、Hot Reload等功能,预示着.NET将持续引领软件开发潮流,为开发者创造更多机会。
|
8天前
|
物联网 vr&ar 开发者
【专栏】.NET 技术:为开发注入活力
【4月更文挑战第29天】本文探讨了.NET技术的创新,主要体现在三个方面:1) .NET Core实现跨平台开发革命,支持多种操作系统和硬件,如.NET MAUI用于多平台UI;2) 性能提升与生产力飞跃,C#新特性简化编程,JIT和AOT优化提升性能,Roslyn提供代码分析工具;3) 引领现代化应用架构,支持微服务、容器化,内置安全机制。未来,.NET 7将带来更多新特性和前沿技术整合,如量子计算、AI,持续推动软件开发创新。开发者掌握.NET技术将赢得竞争优势。
|
8天前
|
人工智能 前端开发 Cloud Native
【专栏】洞察.NET 技术的开发趋势
【4月更文挑战第29天】本文探讨了.NET技术的三大发展趋势:1) 跨平台与云原生技术融合,通过.NET Core支持轻量级、高性能应用,适应云计算和微服务;2) 人工智能与机器学习的集成,如ML.NET框架,使开发者能用C#构建AI模型;3) 引入现代化前端开发技术,如Blazor,实现前后端一致性。随着.NET 8等新版本的发布,期待更多创新技术如量子计算、AR/VR的融合,.NET将持续推动软件开发的创新与进步。
|
8天前
|
人工智能 前端开发 Devops
【专栏】洞察.NET 技术在现代开发中的作用
【4月更文挑战第29天】本文探讨了.NET技术在现代软件开发中的核心价值、应用及挑战。.NET提供语言统一性与多样性,强大的Visual Studio工具,丰富的类库,跨平台能力及活跃的开发者社区。实际应用包括企业级应用、Web、移动、云服务和游戏开发。未来面临性能优化、容器化、AI集成等挑战,需持续创新。开发者应深入理解.NET,把握技术趋势,参与社区,共创美好未来。
|
8天前
|
开发工具 C# 开发者
【专栏】理解.NET 技术,开创美好未来
【4月更文挑战第29天】本文探讨了.NET技术在软件开发中的关键作用,强调其核心优势,如语言多样性、丰富类库、强大的开发工具和跨平台能力。.NET在现代应用开发中涉及企业级应用、云服务集成、微服务、移动应用和游戏开发。未来,.NET将持续创新,提升性能,拓展应用场景,并促进更紧密的社区合作,通过跨平台框架扩大应用范围。开发者应深入学习.NET,抓住技术趋势,共创美好未来。
|
8天前
|
机器学习/深度学习 人工智能 开发者
【专栏】.NET 技术:为开发带来新机遇
【4月更文挑战第29天】本文探讨了.NET技术如何为软件开发带来新机遇,分为三个部分:首先,.NET的跨平台革命,包括.NET Core的兴起、Xamarin与.NET MAUI的移动应用开发、开源社区的推动及性能优化;其次,介绍了云服务与微服务架构的集成,如Azure云服务、微服务支持、DevOps与CI/CD,以及Docker容器化;最后,讨论了AI与机器学习集成,如ML.NET、认知服务、TensorFlow和ONNX,使开发者能构建智能应用。面对这些机遇,开发者应不断学习和适应新技术,以创造更多价值。