使用.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中对于日期时间类型的支持,希望对大家有所帮助。

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
Tag标签: .NET Framework
posted @ 2008-08-29 23:13 TerryLee 阅读(6943) 评论(28)  编辑 收藏 网摘 所属分类: [01]  .NET大本营

  回复  引用  查看    
#1楼2008-08-29 23:18 | jillzhang      
做老李沙发,真爽呀
  回复  引用  查看    
#2楼[楼主]2008-08-29 23:20 | TerryLee      
@jillzhang
老张可够快的,呵呵:-)

  回复  引用  查看    
#3楼2008-08-29 23:28 | Gray Zhang      
也就是说,用DateTimeOffSet和ORM配合映射到sql server 2005的datetime类型会出问题了?
  回复  引用  查看    
#4楼[楼主]2008-08-29 23:30 | TerryLee      
@Gray Zhang
直接保存没做测试,不知道会不会有问题。简单处理一下其实也不会有什么问题,毕竟DateTimeOffset与DateTime之间的转换还是比较容易的,保存到SQL Server 2008中的DateTimeOffset类型中,是一个很不错的选择。

  回复  引用  查看    
#5楼2008-08-29 23:41 | 真见      
好像SqlServer2008中的时间类型也更新了许多
  回复  引用  查看    
#7楼2008-08-30 01:16 | Kai.Ma      
不错!
  回复  引用  查看    
#8楼2008-08-30 09:16 | 路西菲尔      
好文收藏了
  回复  引用  查看    
#9楼2008-08-30 10:42 | 侯垒      
很好,学习.
  回复  引用  查看    
#10楼[楼主]2008-08-30 11:29 | TerryLee      
@Kai.Ma
谢谢支持:)

  回复  引用  查看    
#11楼[楼主]2008-08-30 11:29 | TerryLee      
@路西菲尔
可以收藏到博客园网摘频道哦^_^

  回复  引用  查看    
#12楼[楼主]2008-08-30 11:29 | TerryLee      
@侯垒
谢谢:)

  回复  引用  查看    
#13楼2008-08-30 20:01 | louvreliu      
有个实际应用转换时区的例子吗
最近被PHP的时区搞得头疼
http://www.slive.co.uk/" target="_new">http://www.slive.co.uk/
这个php的时区是直接用算法算出来 根据页面时间做相应的变化

  回复  引用    
#14楼2008-09-01 11:07 | Duron800[未注册用户]
UTC的英文好像是Coordinated Universal Time?
  回复  引用  查看    
#15楼[楼主]2008-09-01 21:44 | TerryLee      
@louvreliu
PHP不熟悉。。。

  回复  引用  查看    
#16楼[楼主]2008-09-01 21:45 | TerryLee      
@Duron800
如果是Coordinated Universal Time,好像应该是CUT了,呵呵,搞不懂,看到很多个版本:)

  回复  引用    
#17楼2008-09-04 21:41 | maigctang[未注册用户]
UTC:协调世界时 Universal Time Coordinated
  回复  引用  查看    
#18楼2008-09-06 20:57 | BigRain      
前几天还正为这个新类型感到好奇。楼主这么快就给出了详细的说明!
  回复  引用  查看    
#19楼[楼主]2008-09-10 23:46 | TerryLee      
@maigctang
可能是我搞错了:)

  回复  引用  查看    
#20楼[楼主]2008-09-10 23:46 | TerryLee      
@BigRain
纯属巧合,呵呵

  回复  引用  查看    
#21楼2008-09-12 13:52 | seyon      
支持
  回复  引用  查看    
#22楼[楼主]2008-09-17 01:00 | TerryLee      
@seyon
谢谢:)

  回复  引用  查看    
#23楼2008-09-29 11:23 | 九成新手      
cmd图片漂亮
  回复  引用  查看    
#24楼[楼主]2008-10-08 11:36 | TerryLee      
@九成新手
:-)

  回复  引用  查看    
#25楼2008-11-13 16:15 | airwolf2026      
呃...这几天代码有根据日期来查询的...
结果...vista语言区域设置中文中国是2008/11/3
而xp语言区域设置中文中国却是2008-11-3....真是无语了啊...

  回复  引用  查看    
#26楼[楼主]2008-11-15 00:35 | TerryLee      
@airwolf2026
时间日期格式,在不同的语言文化下差别是很大的,操作系统也会有不同的处理,呵呵

  回复  引用  查看    
#27楼2009-01-12 13:47 | ★.NET=HOT      
学习TerryLee,TerryLee好像还有个人问题没给我解决,我发到你邮箱里面了呀!
  回复  引用    
#28楼2009-07-03 14:44 | 文武闲人[未注册用户]
怎么都老啦,哈哈
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1279825




相关文章:

相关链接: