引言

    DateTime是一个时常让人复杂困惑的数据类型,开发人员编写【将日期从Web服务器返回到浏览器】类似代码有时结果与预期不符。

ASP.NET MVC 5和 Web API 2/ASP.NETCore 以不同方式序列化日期,这可能会给在一个Web应用程序中同时使用这两个序列化的开发人员带来更多混淆。

本文会尽量覆盖 ASP.NET / ASP.NETCore 中与 Date/Time有关的歧义、参数绑定、序列化相关的知识点和坑位。

知识点

DateTime

DateTime最大问题在于有歧义:地球上各时区形成了一个万花筒,某一时刻在各个时区的本地定义值是不同的;同理给出某时间值,万花筒上的各个时区使用这个值标识了 不同的时刻。

正因为这个现状,在Internet以及无线电通信时,需要定义统一的时间标准。

UTC时间标准

协调世界时(Coordinated Universal Time,缩写为UTC)是全世界定义时钟和时间的主要时间标准。 以原子时秒长为基础在时刻上尽量接近世界时的一种计量系统,

格林威治GMT时间 是19 世纪中叶英帝国基准时间,他以格林威治天文台的0度经线,将世界分为24个时区。 为了方便在不需要精确到秒的情况下,通常将GMT和UTC视为等同。

  UTC能精确代表单一时间点,UTC时刻对于住在加利福尼亚和中国的人来说都是一样的。世界上所有时区的本地时间 都以相对于UTC正负偏移表示(以英国格林威治时区为0时区)

  如果把以上北京时间2019/06/04 09:45:29转化为UTC时间,可以使用以下公式: UTC + 时区差 = 当地时间。

电邮中若有

Sun,04,June 2019 9:45:29 +0800

说明信件发送第时间是2019年6月04日,星期日,上午9点45分29秒,该地区领先UTC8小时 (+800,就是东八区时间)。

发这封邮件的时间,北京时间是9点45,伦敦时间凌晨1点45 ......

中国大陆采用ISO 8601-1988的《数据元和交换格式信息交换日期和时间表示法》(GB/T 7408-1994)称之为国际协调时间,代替原来的GB/T 7408-1994;中国台湾采用CNS 7648的《资料元及交换格式–资讯交换–日期及时间的表示法》,称之为世界统一时间。

ISO 8601时间显示格式

  Web上著名且大规模采用的是ISO-8601 标准。https://www.cl.cam.ac.uk/~mgk25/iso-time.html

当没有更多信息的时候,只包含Date/Time 的写法被假定为当地时间,要指示该时间是UTC时间,可在Datetime值后面加上字母 z

在Datetime值后面增加 +hh:mm、 +hhmm, -hh 可指示该时间值相对于UTC时间的偏移

// Date: 2016-10-12 10:18:42 UTC time
ISO : --11T10::42z == 2016-10-11T10:18:42+00:00 // Date: 2016-10-12 10:18:42 Pacific time 对应的UTC时间是 2016-10-11T16:18:42
 ISO : --11T10::-:00    # 本地时间上午10点左右, 英国伦敦时间下午4点左右: 该本地时间相对伦敦时间提前 6小时。

也是就说我们现在常见的时间格式   2026-10-11T10:18:42+08:00 前面是本地北京时间,后面标记了时区。

.Net中关于Date/Time的实现

  .Net 4.0+ 提供的各种结构已经全方位支持 date, timezone, timezone之间的转化,足以解决开发者遇到的Date/Time相关的问题。

DateTime

  DateTime 定义了一个特殊的date/time, 内置的Kind属性提供了受限的时区信息

  ① DateTimeKind.UTC 指定了UTC的DateTime,明确定义了单一时间点

  ② DateTimeKind.Local 指示了本地时间,这个值在具有相同时区的其他系统依然能够定义一个时间点,但是在其他时区以外,这个DateTime值会有不同解释。

  ③ DateTimeKind.Unspecified 更没有兼容性,仅表示时间值

  我们关注 DateTime.ToUniversalTime() 方法的表现,当DateTime被设定为Unspecified时候, ToUniversalTime会首先假定该值是 Local

// 以下代码的执行环境是北京时间
var dt1 = new DateTime(, , , , , , DateTimeKind.Local);
var dt1_temp = dt1.ToUniversalTime(); var dt2 = new DateTime(, , , , , , DateTimeKind.Utc);
var dt2_temp = dt2.ToUniversalTime(); var dt3 = new DateTime(, , , , , , DateTimeKind.Unspecified);
var dt3_temp = dt3.ToUniversalTime();

var dt4 = new DateTime(, 6, 4, 9, 45, 29); // Unspecified DateTime
var dt4_temp = dt4.ToUniversalTime();
Console.WriteLine(dt1_temp.ToString());
Console.WriteLine(dt2_temp.ToString());
Console.WriteLine(dt3_temp.ToString());       // Unspecified DateTime在被应用ToUniversalTime 方法时会被假定是Local
Console.WriteLine(dt4_temp.ToString());
output: 
2019/6/4 1:45:29
2019/6/4 9:45:29
2019/6/4 1:45:29
2019/6/4 1:45:29

when to use datetime?

  • 你只处理当地时间,你没有跨越时区的计算

  • 你只处理 UTC时间

  • 处理抽象日期/时间(使用 Unspecified):例如跨国公司的跨时区门店都在早上9点开业

DateTimeOffset

表示时间点,通常表示为一天中相对于UTC时间的日期/时间,该结构体自然带有相对于UTC时间的偏移信息

when to use DateTimeOffset?

  • 代码需要应对不同时区的时间值

  • 时区之间相互转化

  • 需要进行跨时区的计算

Date/Time序列化

  Web API2 和ASP.NET Core内置的JSON序列化器是Newtonsoft.Json,JSON.NET将日期时间序列化为ISO-8601格式:

Date value
serialized value
DateTime.Now (Pacific time)
2016-10-11T19:25:34.8346658-07:00
DateTime.UtcNow
2016-10-12T01:25:34.8346658Z
new DateTime(2016, 6, 6, 4, 5,5 )
2016-06-06T04:05:05
new DateTimeOffset(new DateTime(2016, 6, 6, 4, 5,5 ), new TimeSpan(-2, 0,0))
2016-06-06T04:05:05-02:00
  
  ASP.NET MVC 5内置的JSON Serializer还是System.Web.Script.Serialization.JavaScriptSerializer, 该序列化器将date序列化为时间戳:“\/Date(ticks)\/”, ticks 表示从1970-1-1 00:00:00 UTC(Unix Epoch)经历的毫秒数,

这样的格式在客户端需要使用JavaScript做一些本地转化, 转化后的值无法体现时区。

public ActionResult Contact()
{
var data = new Test
{
Name = "hj",
Time = DateTime.Now
};
return Json(data,JsonRequestBehavior.AllowGet);
} output:
{
    "Name": "hj",
    "Time": "/Date(1559634654877)/"
  }
JavaScriptSerializer还有更多缺点,现在社区鼓励使用Json.Net 序列化器。
 

Date/Time字符串转换

  在开发中,常涉及Date/Time 参数绑定和字符串转换, 当中也有一些坑位需要规避。

一个时间日期字符串, 若没有相对于UTC的偏移信息,转换后的DateTime对象的DateTimeKind是Unspecified;

若指定了offset,转换后的DateTime对象的DateTimeKind是Local, 并且时间值被调整到机器的当地时间。

最近我们生产环境WebSite再迁移到k8s集群 (UTC时间)之后,就遇到这样的问题:

订单转储的预期是:北京时间2019-05-11--->2019-05-12

实际情况是在网站端转换为Unspecified, 而进一步ToUniversalTime()过滤的时候,该时间段又被假定是机器的当地时间, 也就是说查询时间段变成了: 伦敦时间2019-05-11--->2019-05-12

这样自然与预期不符。

解决思路: 添加偏移信息,告知明确的时间段: 2019-05-11 00:00:00+08:00 ----> 2019-05-12 00:00:00+08:00

 

开发者每天都在使用的Date/Time有很多学问, 涉及Culture、Calendar、夏令时, 这里只是浅谈最常用的知识点和坑位。

 
作者:JulianHuang

码甲拙见,如有问题请下方留言大胆斧正;码字+Visio制图,均为原创,看官请不吝好评+关注,  ~。。~

本文欢迎转载,请转载页面明显位置注明原作者及原文链接

 

你需要了解的有关.NET日期时间的必要信息的更多相关文章

  1. EasyUI datagrid 日期时间格式化

    EasyUI datagrid中显示日期时间时,会显示为以下不太直观的数值: 添加以下JavaScript脚本,然后在field中添加 formatter: DateTimeFormatter 即可. ...

  2. POCO库——Foundation组件之日期时间DateTime

    日期时间DateTime:内部提供多个设计计时器.日期.时区.时间戳等: Clock.h :Clock时钟计时类,_clock:Int64类型时钟值,CLOCKVAL_MIN.CLOCKVAL_MAX ...

  3. db2 日期时间格式

    db2日期和时间常用汇总 1.db2可以通过SYSIBM.SYSDUMMY1.SYSIBM.DUAL获取寄存器中的值,也可以通过VALUES关键字获取寄存器中的值. SELECT 'HELLO DB2 ...

  4. Angularjs在控制器(controller.js)的js代码中使用过滤器($filter)格式化日期/时间实例

    Angularjs内置的过滤器(filter)为我们的数据信息格式化提供了比较强大的功能,比如:格式化时间,日期.格式化数字精度.语言本地化.格式化货币等等.但这些过滤器一般都是在VIEW中使用的,比 ...

  5. MySQL学习笔记八:日期/时间的处理

    MySQL日期时间的处理,在其官网文档上都有详细的阐述,想了解更多的同学可自行查阅. 1.查询当前日期时间:函数有now(),localtime(),current_timestamp(),sysda ...

  6. Java日期时间操作的一些方法

    1. 获得Calendar实例: Calendar c = Calendar.getInstance(); 2. 定义日期/时间的格式: SimpleDateFormat sdf =new Simpl ...

  7. mysql与oracle的日期/时间函数小结

    前言 本文的日期/时间全部格式化为”2016-01-01 01:01:01“形式: MONITOR_TIME为数据库表字段: 字符串与日期/时间相互转换函数 Oracle 日期/时间转字符串函数:to ...

  8. js 日期时间排序 数组

    不多说直接show代码 var timeArr=[ {'id':'A01','date':'2016-04-20 23:22:11'}, {'id':'A02','date':'2016-04-21 ...

  9. sql server日期时间转字符串

    一.sql server日期时间函数Sql Server中的日期与时间函数 1.  当前系统日期.时间     select getdate()  2. dateadd  在向指定日期加上一段时间的基 ...

随机推荐

  1. sap 图标查看

    showicon这个程序很不错,可以显示SAP里所有的ICON(图标). 用事务码SE38直接运行程序:showicon 即可. 显示列表之后,双击任何一个图标可以显示出每一个图标的详细信息.

  2. LeetCode:删除链表中的节点【203】

    LeetCode:删除链表中的节点[203] 题目描述 删除链表中等于给定值 val 的所有节点. 示例: 输入: 1->2->6->3->4->5->6, val ...

  3. Android Weekly Notes Issue #290

    Android Weekly Issue #290 December 31st, 2017 Android Weekly Issue #290 本期内容包括介绍Kotlin逆变协变的一篇(虽然没说清楚 ...

  4. ES6 Fetch API HTTP请求实用指南

    本次将介绍如何使用Fetch API(ES6 +)对REST API的 HTTP请求,还有一些示例提供给大家便于大家理解. 注意:所有示例均在带有箭头功能的 ES6中给出. 当前的Web /移动应用程 ...

  5. chunkhash笔记

    假设有main1.main2两个入口文件,main引入chunk1.chunk2,main2引入chunk1 改变chunk2 main1的chunkhash改变,main2不发生改变 main再引入 ...

  6. 安装wampserver 计算机丢失msvcr100.dll

    刚刚重新安装了Windows 7 64位系统,再安装Wampserver 2时却提示系统错误,如下图所示: 在网上下载了MSVCR100.dll放到system32文件夹下依然没有用. 百度搜索了一下 ...

  7. C++ 结构体多元素sort排序调用时的写法

    //总结一下,结构体数据排序的快速写法 //以后在遇到需要写的时候,不要迟疑快速写完 struct node { int u, v, w; }a[10000]; //假设该结构体有3个元素 //现在仅 ...

  8. matlab的数组

    1.定义:同一类型的元素的集合. 2.生成:用[]创建,元素之间用逗号或者空格隔开. 第一例: >>a=[1,2,3,4] a = 1 2 3 4 注意,取矩阵的某几列,是这样a(:,2: ...

  9. HihoCoder1641 : 热门号码([Offer收割]编程练习赛37)(模拟)

    描述 1 2 3 ABC DEF 4 5 6 GHI JKL MNO 7 8 9 PQRS TUV WXYZ * 0 # 我们知道电话拨号盘上数字会有若干字母对应,例如2对应ABC,7对应PQRS. ...

  10. 「NOIP2017」「LuoguP3952」 时间复杂度(模拟,栈

    题目描述 小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写程序 ...