C#中的DateTime在逻辑上有个非常严重的缺陷:

> var d = DateTime.Now;
> var d2 = d.ToUniversalTime();
> d == d2
false
> d.Equals(d2);
false

在C#交互模式中输入以上代码,可以发现尽管一个是本地时间(d),一个是UTC时间(d2),只是时区不一样,但在这个世界上,应该属于同一个时刻。然而两个时间却不相等。。。

原因在于DateTime不存储时区,或者说,只存储了一个模糊的关于时区的字段Kind,它是DateTimeKind枚举类型的,有三种取值:Utc/Local/Unspecified,当取值为Unspecified时,则会有歧义。

但我还是要吐槽,如果d.Kind或d2.Kind中任意一个是Unspecified,那么d != d2我可以理解。但是上面的d.Kind是Local,d2.Kind是Utc,如果按照DateTime不存储时区的逻辑,那么这两个统一转换Utc或者Local时,那么它们应该相等,事实上也是如此:

> d == d2.ToLocalTime()
true

如果把d的本地时间t1当做9,本地时间所处时区z1当做+8,相应的UTC时间t0当做1,UTC时间所处时区z0当做0,对它们做规范化处理:

那么 d = t1-z1 = 9 - 8 = 1, d2 = t0 - z0 = 1 - 0 = 1 。

然而 d != d2。这才是它怪异的地方。

以东八区为例,在C#交互模式中输入以下代码:

> var d3 = new DateTime(, , );
> d3
[// ::]
> d3.ToLocalTime()
[// ::]
> d3.ToUniversalTime()
[// ::]

可以发现,一个简单的构造函数,开发者心中默认一般都是本地时间,然而它却允许直接创建出一个既非本地时间、也非UTC时间的怪物。

当d3转成本地时间时,会把d3作为UTC时间来加8小时。

当d3转成UTC时间时,却会把d3作为本地时间来减8小时。

那么d3到底是本地时间还是UTC时间呢?没人清楚,除非它存在于一个非常小的局部作用域中,并且生命周期极短,这时候我们也许可以假设它为本地时间。

然而这个本地时间也依赖于它的运行环境,如果是有几台时区不一致的计算机,阉割了时区信息的DateTime转成字符串在网络中传输到另一个时区(比如隔壁的十一区)的另一台服务器中,解析出来后,所谓的东八区本地时间8点,到了日本,变成了既非本地时间、也非UTC时间的怪物。

DateTime在官方文档中已经不推荐使用,而是推荐使用它的代替品DateTimeOffset,后者保存时区信息。

在交互模式中验证一下:

> var dto = DateTimeOffset.Now;
> var dto2 = dto.ToUniversalTime();
> dto == dto2
true

可以发现,DateTimeoffset判断两个时间是否等价的标准,是以世界时间轴的时刻来判断的,与时区无关,甚至可以与UTC时间无关。只要它们都在同一个时间体系里、能互相变换即可。

在实际项目中,建议大家:

  • 如果有使用DateTime的,统一换成DateTimeOffset。
  • 如果有用到32比特的UNIX时间戳的,统一换成64比特的long来存储UtcTicks。

即使项目本身不跨时区,仍然有可能遇到时区问题,比如如果使用了mongodb的,mongodb存储的时候都是统一存成UTC时间的(好像是,忘了),而且一般来说会带有时区信息。但是有一种情况比较糟糕,如果你的DateTime的Kind是Unspecified的,隐含的时区的信息就会丢失。再取出来之后,就会有8小时的时差。有一些第三方的或者自己公司的类库之类的,如果没有处理好这个问题,也有潜在的时区丢失问题。UNIX时间戳存成Utc Ticks也有好处,无论是精度还是时间跨度,都远超UNIX时间戳。只需要64比特,即可获得下至100纳秒的精度,上超万年的时间跨度,一劳永逸,无论是转回UNIX时间戳还是JS时间戳,都能胜任。空间代价也非常小,除非你的存储空间真的是硬伤。。

出处:http://www.cnblogs.com/zlmdy/p/8560396.html

C#中DateTime的缺陷 ---- 代替品DateTimeOffset的更多相关文章

  1. C#中DateTime的缺陷与代替品DateTimeOffset

    C#中的DateTime在逻辑上有个非常严重的缺陷: > var d = DateTime.Now; > var d2 = d.ToUniversalTime(); > d == d ...

  2. C#中 DateTime , DateTime2 ,DateTimeOffset 之间的小区别 (转载)

    闲来无事列了个表比对一下这3兄弟之间还是有一点差距的╮(╯_╰)╭   DateTime DateTime2 DateTimeOffset 日期范围 1753-01-01到 9999-12-31 00 ...

  3. .NET中DateTime.Now.ToString的格式化字符串

    .NET中DateTime.Now.ToString显示毫秒:DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") DateTime.N ...

  4. easyui datagrid中datetime字段的显示和增删改查问题

    datagrid中datetime字段的异常显示: 使用过easyui datagrid的应该都知道,如果数据库中的字段是datetime类型,绑定在datagrid显式的时候会不正常显示,一般需要借 ...

  5. 解决Asp.net Mvc返回JsonResult中DateTime类型数据格式的问题

    问题背景: 在使用asp.net mvc 结合jquery esayui做一个系统,但是在使用使用this.json方法直接返回一个json对象,在列表中显示时发现datetime类型的数据在转为字符 ...

  6. SQL中DateTime转换成Varchar样式

    SQL中DateTime转换成Varchar样式语句及查询结果:Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AMSelect ...

  7. python中datetime模块中datetime对象的使用方法

    本文只讲述datetime模块中datetime对象的一些常用的方法,如果读者需要更多datetime模块的信息,请查阅此文档. datetime模块的对象有如下: timedelta date da ...

  8. C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决办法

    原文:C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决办法 本人新手,在.Net中写WebAPI的时候,当接口返回的json数据含有日期时间类型的字段时, ...

  9. 解决python写入mysql中datetime类型遇到的问题

    解决python写入mysql中datetime类型遇到的问题 刚开始使用python,还不太熟练,遇到一个datetime数据类型的问题: 在mysql数据库中,有一个datetime类型的字段用于 ...

随机推荐

  1. XML文档的读、写

    代码: XmlDocument doc = new XmlDocument(); doc.Load("Books.xml"); //1.加载要读取的XML文件 //要想看到数据得先 ...

  2. eclipse properties 文件查看和编辑插件 Properties Editor

    Properties Edito官网地址:http://propedit.sourceforge.jp/index_en.html Properties Edito安装地址:http://proped ...

  3. 56. Merge Intervals 57. Insert Interval *HARD*

    1. Merge Given a collection of intervals, merge all overlapping intervals. For example,Given [1,3],[ ...

  4. Mac安装fish shell

    1.brew update 2.brew install fish 3.sudo vi /etc/shells 增加内容:/usr/local/bin/fish   ##增加fish到shell环境变 ...

  5. CentOS下tar解压 gz解压 bz2等各种解压文件使用方法

    .tar  解包:tar xvf FileName.tar  打包:tar cvf FileName.tar DirName  (注:tar是打包,不是压缩!)  ———————————————  . ...

  6. PHP:第一章——PHP中常量和预定义常量

    <?php /*****************************************************/ //1.常量的定义 //常量:声明一次后长期不变的量,以字母或下划线开 ...

  7. spoj375

    题解: 树链剖分的模板题 具体代码详见网上的其他代码 代码: #include<cstdio> #include<cmath> #include<cstring> ...

  8. 关于Predynastic Egypt

    游戏界面(资源增加工人,工人产生资源) 解锁建筑(花费资源增加某一地区产出) 神灵祭祀(献祭资源获得临时buff,如减少建筑时间,一定回合内减少入侵几率,增加粮食产出,减少消耗资源等) 解锁科技(增加 ...

  9. 详细介绍C++STL:unordered_map

    不得不提一下,hash_map未加入在C++11标准中. 在VC中编译: #include <hash_map> using namespace stdext; hash_map<i ...

  10. centos7下Redis-Sentinel安装和配置

    一.Redis的安装 1.从官网https://redis.io/download下载最新的stable版本(也可以下载unstable版本)redis-4.0.9.tar.gz. 2.上传到Cent ...