//C#中DateTime类型转为DateTimeOffset
DateTime thisDate = new DateTime(2020, 4, 19, 0, 0, 0);
TimeSpan offset = new TimeSpan(-6, 0, 0);
DateTimeOffset thisTime = new DateTimeOffset(thisDate, offset);
看起来很简单是不是:DateTimeOffset = Datetime + 时区
但是不对,正确的说法是:DateTimeOffset = Datetime + offset
注:offset表示的是本地时间相对于UTC时间的偏移
What? Are you kidding me? 时区和offset不是一个意思么?
一开始我也是这么理解的,直到我认真的去看微软的官方文档(见文未),我发现其文档中有一个ShowPossibleTimeZones的方法,这个方法的作用是,传入一DateTimeOffset类型的方法,然后遍历所有的时区,找到与这个datetimeoffset中的offset相匹配的时区。
我困惑了:你offset都给我了,我还找什么时区啊?Offset=-6:00,那时区肯定也是-6:00啊。在中国这么理解是没错的,我们是+8区,然后offset也是+8,不会出现问题。
好吧,运行一下下面的代码,看看运行的结果,然后我会以我探索的过程告诉你时区和offset的区别。
推荐一个在线编译运行C#代码的网站:http://csharppad.com/
using System.Collections.ObjectModel;
ReadOnlyCollection<TimeZoneInfo> timeZones;
DateTime thisDate = new DateTime(2020, 4, 19, 0, 0, 0);
DateTimeOffset thisTime = new DateTimeOffset(thisDate, new
TimeSpan(-6, 0, 0));
ShowPossibleTimeZones(thisTime);
//这个方法会打印与thisTime中的offset值一样的timezone
public static void ShowPossibleTimeZones(DateTimeOffset offsetTime) {
TimeSpan offset = offsetTime.Offset; ReadOnlyCollection
<TimeZoneInfo> timeZones;
Console.WriteLine("{0} could belong to the following time
zones:", offsetTime.ToString());
// Get all time zones defined on local system
timeZones = TimeZoneInfo.GetSystemTimeZones();
// Iterate time zones
foreach (TimeZoneInfo timeZone in timeZones) {
// Compare offset with offset for that date in that time zone
if (timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset)) {
Console.WriteLine(" {0}", timeZone.DisplayName);}
}
Console.WriteLine();
}
运行结果如下:
4/19/2020 12:00:00 AM -06:00 could belong to the following time zones:
(UTC-07:00) Chihuahua, La Paz, Mazatlan
(UTC-07:00) Mountain Time (US & Canada)
(UTC-06:00) Central America
(UTC-06:00) Saskatchewan
是的,你没看错,offset = -6,打印的结果中却有-7的时区,这是怎么回事?
就是因为夏令时,世界上有很多国家和美国类似,有冬时制和夏时制之分。跟据所处的纬度不同,世界被分成了跨度为24小时(从-12到+13)的多个时区,在不采取夏时制的国家内,其时间的offset和时区是一致的,如我国。
在采取冬夏时制的国家内,其冬令时和时区是一致的,但是到了夏令时,因为本地时间被向前调整了一个小时,其与UTC时间的偏移就减少了一个小时,但是其所处的时区却是永久不变的,所以就造成了时区和偏移的不一致。下面以美国华盛顿的夏令时时间举个例子:
- 华盛顿处于西五区(-5)
- 2020年4月16日21:36:50是夏时制时间,因为夏时制时间被向前调整了1个小时,所以其与UTC时间的偏移为-4
- 如果这时还有一个西五区的国家不采用夏时制,那么其时间应该时2020年4月16日20:36:50,比美国西五区慢一个小时,其与UTC时间的偏移为-5,与其所在的时区一致
- 如果到了冬天,采用冬时制,那么美国华盛顿的时间就又被调了回来,offset就又与时区一致了
经过上面的例子,我方才明白,时区是不变的,但由于有些时区的国家采取了不同的计时制,因此在一年中不同的时期,其当地时间与UTC的偏移会与时区所表示的不一致,所以不能简单的用当地时间+时区来精确的表示时间,而是应该用Datetime + offset,而这个offset是可以根据时区和时间来推算出来的,即:Offset = 时区 + 夏令时调整时间
所以,精确的将某个数据库存的本地时间转为datetimeoffset你得这么做:
using System.Collections.ObjectModel;
ReadOnlyCollection<TimeZoneInfo> timeZones;
timeZones = TimeZoneInfo.GetSystemTimeZones();
// 取出数据库的时间
DateTime thisDate = new DateTime(2020, 4, 19, 0, 0, 0);
// 1 确定数据库存的时间的时区名字,实例化时区对象
//const string tzName = "Central Standard Time";
const string tzName = "China Standard Time";
TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(tzName);
// 2 通过时区,找到时间的offset
TimeSpan offset = timeZone.GetUtcOffset(thisDate );
// 3 将时间转为DateTimeOffset
DateTimeOffset thisTime = new DateTimeOffset(thisDate, offset);
//打印
Console.WriteLine("{0} - {1}",timeZone.BaseUtcOffset, thisTime );
//遍历所有的timezone,打印该日期时间对应的offset
foreach (TimeZoneInfo timeZone in timeZones)
{
// 通过时区,找到时间的offset
TimeSpan offset = timeZone.GetUtcOffset(thisDate );
// 将时间转为DateTimeOffset
DateTimeOffset thisTime = new DateTimeOffset(thisDate, offset);
//打印
Console.WriteLine("{0} - {1}",timeZone.BaseUtcOffset, thisTime );
}
查询所有的时区:
// 查询打印所有的时区
using System.Collections.ObjectModel;
ReadOnlyCollection<TimeZoneInfo> timeZones;
timeZones = TimeZoneInfo.GetSystemTimeZones();
// Iterate time zones
foreach (TimeZoneInfo timeZone in timeZones)
{
Console.WriteLine("{0}: {1}",timeZone.Id,timeZone.DisplayName);
}
参考微软官方文档:https://docs.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime