微软的.NET对时间的处理

时间DateTime是一个时常让人复杂困惑的数据类型,软件开发人员在编写“将日期从服务器发送到浏览器“类似功能代码有时,经常会出现结果与预期不符。其实,不光是普通开发人员,就连微软的程序员也为此伤痛脑筋,本文将探讨.NET里对时间的处理。

引言

鲁迅说过:地上本没有路,走的人多了,便有了路。同样,地球上本来没有时间,自从人类自己定义了时间,变有了时间,换句话说,而现在所谓的“时间”也都是人工定义的。

UTC时间

我们知道,地球是圆的,为了让时间看起来更合理,人类的对地球进行了划分:以本初子午线为起点,向东,向西,每隔15度划分一个区,称为时区。

地球为360度,所以,很容易得出:

<code>360/15=24 个时区/<code>
微软的.NET对时间的处理

如果我们把上图展开,就可以得到如下的划分图。

微软的.NET对时间的处理

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

UTC能精确代表单一时间点,UTC时刻对于住在北京和住在纽约的人来说都是一样的。世界上所有时区的本地时间 都以相对于UTC正负偏移表示而得到的。

在上图中,北京处于+8区,所以电邮中若有

<code>Sun,04,June 2019 9:45:29 +0800

说明信件发送第时间是2019年6月04日,星期日,上午9点45分29秒,
该地区领先UTC8小时 (+800,就是东八区时间)。
发这封邮件的时间,北京时间是9点45,伦敦时间凌晨1点45 ..../<code>

ISO 8601时间显示格式

Web上著名且大规模采用的是ISO-8601 标准。当没有更多信息的时候,只包含Date/Time 的写法被假定为当地时间,要指示该时间是UTC时间,可在Datetime值后面加上字母 z,

在Datetime值后面增加 +hh:mm、 +hhmm, -hh 可指示该时间值相对于UTC时间的偏移。当使用JS的一些插件时,日期通常写作:

<code>2013-11-18T19:55:00+08:00/<code>

.NET是如何计算时间?

微软开源了.NET源代码,通过的.NET源码的定义,还是可以一窥微软处理时间的方式

(1)时间的最小单位是纳秒。1000毫秒是1秒,60秒是1分,60分是1小时,24小时是1天。通过层层推进,来定义时间的格式。

<code>        //时间的最小单位是纳秒
private const long TicksPerMillisecond = 10000;
//1秒等于1000*1纳秒
private const long TicksPerSecond = TicksPerMillisecond * 1000;
//1分=60*1秒
private const long TicksPerMinute = TicksPerSecond * 60;
//1时=60*1分
private const long TicksPerHour = TicksPerMinute * 60;
//1天=24*1时
private const long TicksPerDay = TicksPerHour * 24;/<code>

(2)闰年/非闰年

普通年份,1年有365天,每4年是一个闰年,所以,4年累计共计天数是 365*4+1。每100年共DaysPer4Years * 25 - 1天

注:关于闰年算法简单的算法是:四年一闰、百年不闰、400年再闰。

<code>        //非闰年365天
private const int DaysPerYear = 365;
// Number of days in 4 years
private const int DaysPer4Years = DaysPerYear * 4 + 1; // 1461
// Number of days in 100 years
private const int DaysPer100Years = DaysPer4Years * 25 - 1; // 36524

// Number of days in 400 years
private const int DaysPer400Years = DaysPer100Years * 4 + 1; // 146097/<code>

(3)时间的累加

在.NET里,对日期的统计是从 “0001年1月1 ” 到 “9999年12月31”,在这里微软并没有采用高深的算法,相反,他采用“枚举”方式,也就是把所有可能的情况给列出来。

在0001年-9999年这个时间段里,有分为四段:

1)从 "0001.1.1-1600.12.31"系统不进行处理。系统共计584388天。

2)从0001.1.1到1900年共 DaysPer400Years * 4 + DaysPer100Years * 3 - 367; 。

3)从0001.1.1到1970年,共DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17

4)计算机主要处理1970.1.1日之后的日期

<code>     // Number of days from 1/1/0001 to 12/31/1600
private const int DaysTo1601 = DaysPer400Years * 4; //
// Number of days from 1/1/0001 to 12/30/1899
private const int DaysTo1899 = DaysPer400Years * 4 + DaysPer100Years * 3 - 367;
// Number of days from 1/1/0001 to 12/31/1969
internal const int DaysTo1970 = DaysPer400Years * 4 + DaysPer100Years * 3 + DaysPer4Years * 17 + DaysPerYear; // 719,162
// Number of days from 1/1/0001 to 12/31/9999
private const int DaysTo10000 = DaysPer400Years * 25 - 366; // 3652059

internal const long MinTicks = 0;
internal const long MaxTicks = DaysTo10000 * TicksPerDay - 1;
private const long MaxMillis = (long)DaysTo10000 * MillisPerDay;

private const String TicksField = "ticks";
private const String DateDataField = "dateData";

private UInt64 dateData;
}
/<code>

(4)月份处理

枚举所有的月份,非闰年365天,闰年366天。

<code>        private static readonly int[] DaysToMonth365 = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
private static readonly int[] DaysToMonth366 = {
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366};/<code>

.NET如何存储时间?

在上面DateTime里,微软使用64位二进制存储时间。

其中 01-62位存储的是 1/1/0001 12:00:00am 到 12/31/9999 23:59:59.9999999

63位和64位是状态为,这2位可以有四种组合(也就是00,01,10,11),这四个状态主要确定时间的格式,是UTC还是Local还是Unspecified

由此,我们就可以大致得到.NET存储的时间方式,例如如果要存储 2020.1.1 0:0:0 时,计算机是把这个日期减去 0001.1.1,然后把得到的64位无符号整数存储起来,作为日期的最终存储格式。


.NET与Javascript时间的区别

不同的语言,对时间的处理机制并不完全相同,例如JavaScript。他的UTC() 方法可根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。


微软的.NET对时间的处理

总结

总之,时间是计算机体系里极其重要的数据结构,看似简单但是繁琐,更为重要的是,时间是最基础的,一丁点的错误,都可能产生“失之毫厘谬以千里”。

上世纪70年代,当计算机刚刚诞生时,微软使用2个数字表示年份,例如“98.1.1”表示的“1998年1月1日”,系统自动把“98”识别为“1998年”,由此当人类进入到“2000”年时,计算机把“00.1.1.解释为“1900年1月1日”,而并不是人类想要的“2000年1月1日”,并由此产生当时的“千年虫”问题。

后来,微软对系统进行修复,使用四个数字表示年份。

现在,微软.NET里时间最大表示的日期是9999.12.31,如果人类可以延续到10000年,估计那时候的人民又要头疼了,因为9999年过后,系统将默认开始按照0000年计时。


分享到:


相關文章: