重學c#系列——datetime 和 datetimeoffset[二十一]
前言
簡單介紹一下datetime和 datetimeoffset.
正文
了解一個國家的文化,就要了解一個國家的歷史。
要了解datetimeoffset,那麼很有必要了解一下datetime。
表示時間上的一刻,通常以日期和當天的時間表示。
繼承
Object-> ValueType-> DateTime
那麼可以看到DateTime 是值類型了。
實際上了解Datetime 有兩個重要的參數,一個是:ticks 另一個是:kind。
ticks
Int64
一個日期和時間,以公曆 0001 年 1 月 1 日 00:00:00.000 以來所經歷的以 100 納秒為間隔的間隔數來表示。
kind
DateTimeKind
枚舉值之一,該值指示 ticks 是指定了本地時間、協調世界時 (UTC),還是兩者皆未指定。
這裡有一個值得注意的是這個ticks 是以公曆 0001 年 1 月 1 日 00:00:00.000 開始計算的,且單位是100納秒。
比如說,我這個ticks 是200,那麼就是20000納秒了。
和秒的計算公式為:1 納秒=0.000000001 秒。
DateTimeKind 指定是本地,還是utc,或者不指定。
初始化如下:
public DateTime(long ticks, DateTimeKind kind)
{
if (ticks < 0L || ticks > 3155378975999999999L)
throw new ArgumentOutOfRangeException(nameof (ticks), SR.ArgumentOutOfRange_DateTimeBadTicks);
if (kind < DateTimeKind.Unspecified || kind > DateTimeKind.Local)
throw new ArgumentException(SR.Argument_InvalidDateTimeKind, nameof (kind));
this._dateData = (ulong) (ticks | (long) kind << 62);
}
而其他年月日其實也就是轉換為tickes。
public DateTime(int year, int month, int day)
{
this._dateData = (ulong) DateTime.DateToTicks(year, month, day);
}
然後DateToTicks:
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static long DateToTicks(int year, int month, int day)
{
if (year < 1 || year > 9999 || (month < 1 || month > 12) || day < 1)
ThrowHelper.ThrowArgumentOutOfRange_BadYearMonthDay();
int[] numArray = DateTime.IsLeapYear(year) ? DateTime.s_daysToMonth366 : DateTime.s_daysToMonth365;
if (day > numArray[month] - numArray[month - 1])
ThrowHelper.ThrowArgumentOutOfRange_BadYearMonthDay();
int num = year - 1;
return (long) (num * 365 + num / 4 - num / 100 + num / 400 + numArray[month - 1] + day - 1) * 864000000000L;
}
由上面可值,其他的轉換都是通過_dateData 來轉換。
那麼datetime 有什麼問題呢? 其實可以想像一個問題,就是這個設計的時候呢。
有一個local 還有 一個 UTC,那麼可能local就是UTC呢?完全可能,從這裡開始概念就開始出現偏差了。
public static DateTime Now
{
get
{
DateTime utc = UtcNow;
long offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out bool isAmbiguousLocalDst).Ticks;
long tick = utc.Ticks + offset;
if (tick > DateTime.MaxTicks)
{
return new DateTime(DateTime.MaxTicks, DateTimeKind.Local);
}
if (tick < DateTime.MinTicks)
{
return new DateTime(DateTime.MinTicks, DateTimeKind.Local);
}
return new DateTime(tick, DateTimeKind.Local, isAmbiguousLocalDst);
}
}
DateTime.Now,那麼就是本地時間了,然後看下面這個東西。
static void Main(string[] args)
{
DateTime d = DateTime.Now;
DateTime d2 = d.ToUniversalTime();
Console.WriteLine(d.Equals(d2));
Console.Read();
}
返回為false,理論上他們應該是同一個時間。
那麼這個equal 是幹什麼呢?
public bool Equals(DateTime value)
{
return InternalTicks == value.InternalTicks;
}
查看一下:
internal long InternalTicks => (long)(_dateData & TicksMask);
這個時候只要_dateData 不相等,那麼就不相等, 是的因為時區不同,這個自然不同。
// Returns the tick count for this DateTime. The returned value is
// the number of 100-nanosecond intervals that have elapsed since 1/1/0001
// 12:00am.
//
public long Ticks => InternalTicks;
static void Main(string[] args)
{
DateTime d = DateTime.Now;
DateTime d2 = d.ToUniversalTime();
Console.WriteLine(d2.Ticks);
Console.WriteLine(d.Ticks);
Console.WriteLine(d.Equals(d2));
Console.Read();
}
計算一下相差時間:
static void Main(string[] args)
{
DateTime d = DateTime.Now;
DateTime d2 = d.ToUniversalTime();
Console.WriteLine((d.Ticks - d2.Ticks)/60/60/10000000);
Console.WriteLine(d.Equals(d2));
Console.Read();
}
那麼把本地時間調成協調世界時會發生什麼呢?
這時候就是true了,所以一套程式碼在不同時區的機器上有不同的效果了。
當然,對於datetime 我們就需要做兼容了,就是要判斷其時區做處理,這裡就不演示了,
DateTimeOffset是什麼呢? 和datetime 有什麼關係呢。
首先來看時間相等情況:
static void Main(string[] args)
{
DateTimeOffset d = DateTimeOffset.Now;
DateTimeOffset d2 = d.ToUniversalTime();
Console.WriteLine(d.Equals(d2));
Console.Read();
}
它似乎解決了我們前面的問題,那麼其實怎麼做的呢?
DateTimeOffset 有兩個重要的參數:
ticks
Int64
一個日期和時間,以 0001 年 1 月 1 日午夜 12:00:00 以來所經歷的以 100 納秒為間隔的間隔數來表示。
offset
TimeSpan
與協調世界時 (UTC) 之間的時間偏移量。
實例化:
// Constructs a DateTimeOffset from a tick count and offset
public DateTimeOffset(long ticks, TimeSpan offset)
{
_offsetMinutes = ValidateOffset(offset);
// Let the DateTime constructor do the range checks
DateTime dateTime = new DateTime(ticks);
_dateTime = ValidateDate(dateTime, offset);
}
這裡可以看到DateTimeOffset 就沒有了kind 這個概念年了,而是直接指名了這個offset概念,就是和utc到底相差多少時差。
而其相等,那麼也是用UTc來計算的。
public bool Equals(DateTimeOffset other) =>
UtcDateTime.Equals(other.UtcDateTime);
這樣,其實就解決了這個時區概念了。
結
至此datetime 和 datetimeoffset 概念和比較到此結束,那麼遇到具體問題也可以根據其特徵來分析了,該系列持續更新。