C#中DateTime的缺陷与代替品DateTimeOffset
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时间戳,都能胜任。空间代价也非常小,除非你的存储空间真的是硬伤。。
C#中DateTime的缺陷与代替品DateTimeOffset的更多相关文章
- C#中DateTime的缺陷 ---- 代替品DateTimeOffset
C#中的DateTime在逻辑上有个非常严重的缺陷: > var d = DateTime.Now; > var d2 = d.ToUniversalTime(); > d == d ...
- .NET中DateTime.Now.ToString的格式化字符串
.NET中DateTime.Now.ToString显示毫秒:DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") DateTime.N ...
- easyui datagrid中datetime字段的显示和增删改查问题
datagrid中datetime字段的异常显示: 使用过easyui datagrid的应该都知道,如果数据库中的字段是datetime类型,绑定在datagrid显式的时候会不正常显示,一般需要借 ...
- 解决Asp.net Mvc返回JsonResult中DateTime类型数据格式的问题
问题背景: 在使用asp.net mvc 结合jquery esayui做一个系统,但是在使用使用this.json方法直接返回一个json对象,在列表中显示时发现datetime类型的数据在转为字符 ...
- C#中 DateTime , DateTime2 ,DateTimeOffset 之间的小区别 (转载)
闲来无事列了个表比对一下这3兄弟之间还是有一点差距的╮(╯_╰)╭ DateTime DateTime2 DateTimeOffset 日期范围 1753-01-01到 9999-12-31 00 ...
- SQL中DateTime转换成Varchar样式
SQL中DateTime转换成Varchar样式语句及查询结果:Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2006 10:57AMSelect ...
- python中datetime模块中datetime对象的使用方法
本文只讲述datetime模块中datetime对象的一些常用的方法,如果读者需要更多datetime模块的信息,请查阅此文档. datetime模块的对象有如下: timedelta date da ...
- C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决办法
原文:C# WebAPI中DateTime类型字段在使用微软自带的方法转json格式后默认含T的解决办法 本人新手,在.Net中写WebAPI的时候,当接口返回的json数据含有日期时间类型的字段时, ...
- 解决python写入mysql中datetime类型遇到的问题
解决python写入mysql中datetime类型遇到的问题 刚开始使用python,还不太熟练,遇到一个datetime数据类型的问题: 在mysql数据库中,有一个datetime类型的字段用于 ...
随机推荐
- Redis进阶实践之十二 Redis的Cluster集群动态扩容
一.引言 上一篇文章我们一步一步的教大家搭建了Redis的Cluster集群环境,形成了3个主节点和3个从节点的Cluster的环境.当然,大家可以使用 Cluster info 命令查看Cl ...
- 隐藏文件的查看(Win/Linux/macOS)
Windows(10): 点查看->点选项,弹出文件夹选项,点查看,高级设置里找到隐藏文件和文件夹这个选项,按需求选显示或者隐藏即可. Linux: Linux下,类似于.ssh开头的文件或者文 ...
- network programming-简单的TCP客户服务器编程
简单的TCP程序客户端流程:创建套接字(套接字用IP地址:端口号)表示)socket()->请求连接connect()->交换数据 send()/recv()->关闭连接 close ...
- hihoCoder 1513 : 小Hi的烦恼 位运算好题
思路:考虑第i个同学,第一门课排名比他靠前的同学的集合是S1,第二门课是S2...第五门课是S5,很明显比这个同学每门课程都优秀的同学就是S1&S2&S3&S4&S5, ...
- string (KMP+期望DP)
Time Limit: 1000 ms Memory Limit: 256 MB Description 给定一个由且仅由字符 'H' , 'T' 构成的字符串$S$. 给定一个最初为空的字符串 ...
- .net Winfrom 僵尸窗口 的问题
执行顺序如下: Form1 form1 =new Form1(); form1.ShowDialog(); Form2 form2= Application.OpenForms["Form2 ...
- 安装linux环境及相关包方法
安装linux系统: 先下载 iso镜像文件,在windows系统下,插入u盘,下载 Ultraiso软件,安装好后,根据 http://jingyan.baidu.com/article/a378c ...
- 总结MySQL大数据量下如何进行优化
写在建库前: 在确定数据库业务后.建立数据库表格时,就应对一些常见问题有所考虑,以避免在数据增长一段时间后再做应对,可能造成时间及维护成本增加: 数据的月增量,年增量 数据的快速增长点 是否需要触发器 ...
- Centos定时启动和清除任务
因为需要定时并发执行任务,所以查到了crontab这个工具,介绍一下其用法: SHELL=/bin/bash PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=roo ...
- 妙用ES6解构和扩展运算符让你的代码更优雅
http://www.cnblogs.com/chrischjh/p/4848934.html