理解.Net中的時間

前言

如果在你的項目中只使用Datetime 足以滿足一切需求,那你可能不需要點亮關於時間的技能點;
一旦你需要參與到一些國際化項目或者與定時調度相關的項目,則有必要對.Net中的時間處理方式進行一些系統的瞭解。

背景知識

• 時區:由於世界各國家與地區經度不同,地方時也有所不同,因此會劃分爲不同的時區。正式的時區劃分,其中包括24個時區,每一時區由一個英文字母表示。每隔經度15°劃分一個時區。1884年在華盛頓召開的一次國際經度會議(又稱國際子午線會議)上,規定將全球劃分爲24個時區(東、西各12個時區)。規定英國(格林尼治天文臺舊址)爲中時區(零時區)、東1-12區,西1-12區。每個時區橫跨經度15度,時間正好是1小時。最後的東、西第12區各跨經度7.5度,以東、西經180度爲界。

• 時區計算:計算的區時=已知區時-(已知區時的時區-要計算區時的時區),(注:東時區爲正,西時區爲負)。例如:
例1:已知東京(東九區)時間爲5月1日12:00,求北京(東八區)的區時?
答:北京時間=5/1 12:00時 -(9-8)時=5/1 11:00 時。
例2:已知北京時間爲5月1日12:00,求倫敦(中時區)的區時?
答:倫敦時間=5/1 12:00時-(8-0)時=5/1 4:00時。
例3:已知北京時間爲5月1日12:00,求紐約(西五區)的區時。
答:紐約時間=5/1 12:00-(8-(-5))=4/30 23:00時。
例4:已知紐約時間(西五區)爲5月1日12:00,求東京(東九區)的區時。
答:東京時間=5/1 12:00-(-5-9)=5/1 12+14 超過24時加一天 ,即5/2 2:00時。
判斷新舊兩天,要看兩條線,一是人爲日界線-180度國際日期變更線,二是自然分界線-當地時間爲0點的地區經線。自西向東越過國際日期變更線,日期應減1天,比如你在國際日期變更線西側,當地時間是20日的00:30,當你自西向東越過國際日期變更線後,你所在位置的當地時間是19日的00:30。如果是自東向西越過國際日期變更線,則應該加1天。

.Net 中的時間相關概念

TimeZone

TimeZone是一個Class,主要用來得到本地時區信息,並將時間轉換到協調世界時(UTC)。注意:這個API已過時,關於時區的操作都使用TimeZoneInfo類。
參考:https://docs.microsoft.com/zh-cn/dotnet/api/system.timezone?view=netcore-3.0

TimeZoneInfo

TimeZoneInfo 是一個class,一個TimeZoneInfo對象可以表示任何時區,可用於將一個時區的時間轉換爲其他時區中對應的事件。並且TimeZoneInfo的實例時不可變的,一旦已實例化一個對象(不能通過new實例化),不能修改其值。

    class Program
    {
        static void Main(string[] args)
        {           
            TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
            // 屬性 本機所在時區
            Console.WriteLine("本地時區名稱:{0}",timeZoneInfo.StandardName);
            Console.WriteLine("是否支持夏令時規則:{0}", timeZoneInfo.SupportsDaylightSavingTime);
            Console.WriteLine("與國際標準時(零時區)的時差:{0}", timeZoneInfo.BaseUtcOffset);
            Console.WriteLine("夏令時(中國1992年後已不再實施):{0}", timeZoneInfo.DaylightName);
            Console.WriteLine("本地時區顯示全名:{0}", timeZoneInfo.DisplayName);
            // 時間轉換 從本地時區到目標時區
            DateTime dateTime = DateTime.Now;
            TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
            TimeZoneInfo hwt = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time");
            var estTime= TimeZoneInfo.ConvertTime(dateTime, est);
            Console.WriteLine("本地時間:{0}", dateTime.ToString());
            Console.WriteLine("美國東部時間:{0}", estTime.ToString());
            /* 
             * 重載 表示把一個時間(來自本地的時間)從美國東部時間轉換成夏威夷時間
             * 注意執行此轉換時第一個DateTime參數的DaTimeKind必須和源DatetimeKind一致,否則會拋出異常。
             * 所以下面的轉換不會成功。
             * https://docs.microsoft.com/zh-cn/dotnet/api/system.timezoneinfo.converttime?view=netcore-3.0#System_TimeZoneInfo_ConvertTime_System_DateTime_System_TimeZoneInfo_
            */
            var tartetTime =TimeZoneInfo.ConvertTime(dateTime, est, hwt);
            Console.WriteLine("本地時間:{0}", tartetTime.ToString());
        }
    }

參考:https://docs.microsoft.com/zh-cn/dotnet/api/system.timezoneinfo?view=netcore-3.0

DateTime

DateTime是一個Struct,是最常用用於跟時間相關的操作的對象。主要關注的功能點:
• 使用構造函數初始化一個時間

// new DateTime()默認等於DateTime.MinValue  01/01/0001 00:00:00
// 可以直接靜態方法得到系統時間 DateTime.Now/DateTime.UtcNow/DateTime.Today ……
// 構造函數創建時間的重載方法
public DateTime(long ticks); // ticks 是以100納秒爲單位的數值,初始化時以0001/01/01 00:00:00 爲起點
public DateTime(long ticks, DateTimeKind kind);
public DateTime(int year, int month, int day);
public DateTime(int year, int month, int day, Calendar calendar);// Calendar表示系統使用的日曆,System.Globalization有其實現
public DateTime(int year, int month, int day, int hour, int minute, int second);
public DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind);
public DateTime(int year, int month, int day, int hour, int minute, int second, Calendar calendar);
public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond);
public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind);
public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar);
public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind);

• 將一定格式的字符串轉換成時間對象

// 可以使用Parse()  TryParse()方法將字符串轉換成DateTime。
// 支持的格式通過如下代碼查看:
            List<string> badFormats = new List<String>();
            System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CurrentCulture;
            foreach (var dateString in DateTime.Now.GetDateTimeFormats())
            {
                DateTime parsedDate;
                if (DateTime.TryParse(dateString, out parsedDate))
                    Console.WriteLine($"{dateString,-37} {DateTime.Parse(dateString),-19}");
                else
                    badFormats.Add(dateString);
            }
            if (badFormats.Count > 0)
            {
                Console.WriteLine("\nStrings that could not be parsed: ");
                foreach (var badFormat in badFormats)
                    Console.WriteLine($"   {badFormat}");
            }
            Console.ReadKey();
2019/6/28                             2019/6/28 0:00:00
2019-6-28                             2019/6/28 0:00:00
2019.6.28                             2019/6/28 0:00:00
2019/06/28                            2019/6/28 0:00:00
2019-06-28                            2019/6/28 0:00:00
2019.06.28                            2019/6/28 0:00:00
19/6/28                               2019/6/28 0:00:00
19-6-28                               2019/6/28 0:00:00
19.6.28                               2019/6/28 0:00:00
19/06/28                              2019/6/28 0:00:00
2019年6月28日                            2019/6/28 0:00:00
2019年6月28日, 星期五                       2019/6/28 0:00:00
星期五, 2019年6月28日                       2019/6/28 0:00:00
2019年6月28日                            2019/6/28 0:00:00
2019年6月28日, 星期五                       2019/6/28 0:00:00
2019年6月28日 14:35                      2019/6/28 14:35:00
2019年6月28日 14:35                      2019/6/28 14:35:00
2019年6月28日 下午 2:35                    2019/6/28 14:35:00
2019年6月28日 下午 02:35                   2019/6/28 14:35:00
2019年6月28日, 星期五 14:35                 2019/6/28 14:35:00
2019年6月28日, 星期五 14:35                 2019/6/28 14:35:00
2019年6月28日, 星期五 下午 2:35               2019/6/28 14:35:00
2019年6月28日, 星期五 下午 02:35              2019/6/28 14:35:00
星期五, 2019年6月28日 14:35                 2019/6/28 14:35:00
星期五, 2019年6月28日 14:35                 2019/6/28 14:35:00
星期五, 2019年6月28日 下午 2:35               2019/6/28 14:35:00
星期五, 2019年6月28日 下午 02:35              2019/6/28 14:35:00
2019年6月28日 14:35                      2019/6/28 14:35:00
2019年6月28日 14:35                      2019/6/28 14:35:00
2019年6月28日 下午 2:35                    2019/6/28 14:35:00
2019年6月28日 下午 02:35                   2019/6/28 14:35:00
2019年6月28日, 星期五 14:35                 2019/6/28 14:35:00
2019年6月28日, 星期五 14:35                 2019/6/28 14:35:00
2019年6月28日, 星期五 下午 2:35               2019/6/28 14:35:00
2019年6月28日, 星期五 下午 02:35              2019/6/28 14:35:00
2019年6月28日 14:35:13                   2019/6/28 14:35:13
2019年6月28日 14:35:13                   2019/6/28 14:35:13
2019年6月28日 下午 2:35:13                 2019/6/28 14:35:13
2019年6月28日 下午 02:35:13                2019/6/28 14:35:13
2019年6月28日, 星期五 14:35:13              2019/6/28 14:35:13
2019年6月28日, 星期五 14:35:13              2019/6/28 14:35:13
2019年6月28日, 星期五 下午 2:35:13            2019/6/28 14:35:13
2019年6月28日, 星期五 下午 02:35:13           2019/6/28 14:35:13
星期五, 2019年6月28日 14:35:13              2019/6/28 14:35:13
星期五, 2019年6月28日 14:35:13              2019/6/28 14:35:13
星期五, 2019年6月28日 下午 2:35:13            2019/6/28 14:35:13
星期五, 2019年6月28日 下午 02:35:13           2019/6/28 14:35:13
2019年6月28日 14:35:13                   2019/6/28 14:35:13
2019年6月28日 14:35:13                   2019/6/28 14:35:13
2019年6月28日 下午 2:35:13                 2019/6/28 14:35:13
2019年6月28日 下午 02:35:13                2019/6/28 14:35:13
2019年6月28日, 星期五 14:35:13              2019/6/28 14:35:13
2019年6月28日, 星期五 14:35:13              2019/6/28 14:35:13
2019年6月28日, 星期五 下午 2:35:13            2019/6/28 14:35:13
2019年6月28日, 星期五 下午 02:35:13           2019/6/28 14:35:13
2019/6/28 14:35                       2019/6/28 14:35:00
2019/6/28 14:35                       2019/6/28 14:35:00
2019/6/28 下午 2:35                     2019/6/28 14:35:00
2019/6/28 下午 02:35                    2019/6/28 14:35:00
2019-6-28 14:35                       2019/6/28 14:35:00
2019-6-28 14:35                       2019/6/28 14:35:00
2019-6-28 下午 2:35                     2019/6/28 14:35:00
2019-6-28 下午 02:35                    2019/6/28 14:35:00
2019.6.28 14:35                       2019/6/28 14:35:00
2019.6.28 14:35                       2019/6/28 14:35:00
2019.6.28 下午 2:35                     2019/6/28 14:35:00
2019.6.28 下午 02:35                    2019/6/28 14:35:00
2019/06/28 14:35                      2019/6/28 14:35:00
2019/06/28 14:35                      2019/6/28 14:35:00
2019/06/28 下午 2:35                    2019/6/28 14:35:00
2019/06/28 下午 02:35                   2019/6/28 14:35:00
2019-06-28 14:35                      2019/6/28 14:35:00
2019-06-28 14:35                      2019/6/28 14:35:00
2019-06-28 下午 2:35                    2019/6/28 14:35:00
2019-06-28 下午 02:35                   2019/6/28 14:35:00
2019.06.28 14:35                      2019/6/28 14:35:00
2019.06.28 14:35                      2019/6/28 14:35:00
2019.06.28 下午 2:35                    2019/6/28 14:35:00
2019.06.28 下午 02:35                   2019/6/28 14:35:00
19/6/28 14:35                         2019/6/28 14:35:00
19/6/28 14:35                         2019/6/28 14:35:00
19/6/28 下午 2:35                       2019/6/28 14:35:00
19/6/28 下午 02:35                      2019/6/28 14:35:00
19-6-28 14:35                         2019/6/28 14:35:00
19-6-28 14:35                         2019/6/28 14:35:00
19-6-28 下午 2:35                       2019/6/28 14:35:00
19-6-28 下午 02:35                      2019/6/28 14:35:00
19.6.28 14:35                         2019/6/28 14:35:00
19.6.28 14:35                         2019/6/28 14:35:00
19/06/28 14:35                        2019/6/28 14:35:00
19/06/28 14:35                        2019/6/28 14:35:00
19/06/28 下午 2:35                      2019/6/28 14:35:00
19/06/28 下午 02:35                     2019/6/28 14:35:00
2019/6/28 14:35:13                    2019/6/28 14:35:13
2019/6/28 14:35:13                    2019/6/28 14:35:13
2019/6/28 下午 2:35:13                  2019/6/28 14:35:13
2019/6/28 下午 02:35:13                 2019/6/28 14:35:13
2019-6-28 14:35:13                    2019/6/28 14:35:13
2019-6-28 14:35:13                    2019/6/28 14:35:13
2019-6-28 下午 2:35:13                  2019/6/28 14:35:13
2019-6-28 下午 02:35:13                 2019/6/28 14:35:13
2019.6.28 14:35:13                    2019/6/28 14:35:13
2019.6.28 14:35:13                    2019/6/28 14:35:13
2019.6.28 下午 2:35:13                  2019/6/28 14:35:13
2019.6.28 下午 02:35:13                 2019/6/28 14:35:13
2019/06/28 14:35:13                   2019/6/28 14:35:13
2019/06/28 14:35:13                   2019/6/28 14:35:13
2019/06/28 下午 2:35:13                 2019/6/28 14:35:13
2019/06/28 下午 02:35:13                2019/6/28 14:35:13
2019-06-28 14:35:13                   2019/6/28 14:35:13
2019-06-28 14:35:13                   2019/6/28 14:35:13
2019-06-28 下午 2:35:13                 2019/6/28 14:35:13
2019-06-28 下午 02:35:13                2019/6/28 14:35:13
2019.06.28 14:35:13                   2019/6/28 14:35:13
2019.06.28 14:35:13                   2019/6/28 14:35:13
2019.06.28 下午 2:35:13                 2019/6/28 14:35:13
2019.06.28 下午 02:35:13                2019/6/28 14:35:13
19/6/28 14:35:13                      2019/6/28 14:35:13
19/6/28 14:35:13                      2019/6/28 14:35:13
19/6/28 下午 2:35:13                    2019/6/28 14:35:13
19/6/28 下午 02:35:13                   2019/6/28 14:35:13
19-6-28 14:35:13                      2019/6/28 14:35:13
19-6-28 14:35:13                      2019/6/28 14:35:13
19-6-28 下午 2:35:13                    2019/6/28 14:35:13
19-6-28 下午 02:35:13                   2019/6/28 14:35:13
19.6.28 14:35:13                      2019/6/28 14:35:13
19.6.28 14:35:13                      2019/6/28 14:35:13
19/06/28 14:35:13                     2019/6/28 14:35:13
19/06/28 14:35:13                     2019/6/28 14:35:13
19/06/28 下午 2:35:13                   2019/6/28 14:35:13
19/06/28 下午 02:35:13                  2019/6/28 14:35:13
6月28日                                 2019/6/28 0:00:00
6月28日                                 2019/6/28 0:00:00
2019-06-28T14:35:13.8497646+08:00     2019/6/28 14:35:13
2019-06-28T14:35:13.8497646+08:00     2019/6/28 14:35:13
Fri, 28 Jun 2019 14:35:13 GMT         2019/6/28 22:35:13
Fri, 28 Jun 2019 14:35:13 GMT         2019/6/28 22:35:13
2019-06-28T14:35:13                   2019/6/28 14:35:13
14:35                                 2019/6/28 14:35:00
14:35                                 2019/6/28 14:35:00
下午 2:35                               2019/6/28 14:35:00
下午 02:35                              2019/6/28 14:35:00
14:35:13                              2019/6/28 14:35:13
14:35:13                              2019/6/28 14:35:13
下午 2:35:13                            2019/6/28 14:35:13
下午 02:35:13                           2019/6/28 14:35:13
2019-06-28 14:35:13Z                  2019/6/28 22:35:13
2019年6月28日 6:35:13                    2019/6/28 6:35:13
2019年6月28日 06:35:13                   2019/6/28 6:35:13
2019年6月28日 上午 6:35:13                 2019/6/28 6:35:13
2019年6月28日 上午 06:35:13                2019/6/28 6:35:13
2019年6月28日, 星期五 6:35:13               2019/6/28 6:35:13
2019年6月28日, 星期五 06:35:13              2019/6/28 6:35:13
2019年6月28日, 星期五 上午 6:35:13            2019/6/28 6:35:13
2019年6月28日, 星期五 上午 06:35:13           2019/6/28 6:35:13
星期五, 2019年6月28日 6:35:13               2019/6/28 6:35:13
星期五, 2019年6月28日 06:35:13              2019/6/28 6:35:13
星期五, 2019年6月28日 上午 6:35:13            2019/6/28 6:35:13
星期五, 2019年6月28日 上午 06:35:13           2019/6/28 6:35:13
2019年6月28日 6:35:13                    2019/6/28 6:35:13
2019年6月28日 06:35:13                   2019/6/28 6:35:13
2019年6月28日 上午 6:35:13                 2019/6/28 6:35:13
2019年6月28日 上午 06:35:13                2019/6/28 6:35:13
2019年6月28日, 星期五 6:35:13               2019/6/28 6:35:13
2019年6月28日, 星期五 06:35:13              2019/6/28 6:35:13
2019年6月28日, 星期五 上午 6:35:13            2019/6/28 6:35:13
2019年6月28日, 星期五 上午 06:35:13           2019/6/28 6:35:13
2019年6月                               2019/6/1 0:00:00
2019年6月                               2019/6/1 0:00:00
2019.6                                2019/6/1 0:00:00
2019年6月                               2019/6/1 0:00:00
2019年6月                               2019/6/1 0:00:00
2019.6                                2019/6/1 0:00:00
Strings that could not be parsed:
   19.6.28 下午 2:35
   19.6.28 下午 02:35
   19.6.28 下午 2:35:13
   19.6.28 下午 02:35:13
   2019年六月
   2019年六月

• 計算時間之間的差值
DateTime 是一個可以計算的結構體,可以對其年月日時分秒進行數學計算,一般結合系統日曆輸出。如果計算時間差值一般使用TimeSpan對象。

DateTimeKind

DateTimeKind是一個枚舉,是DateTime的一個屬性,表示得到時間是Utc時間還是本地時間,或者是Unspecified。在做時間計算時需要保持一致。

            var dt = DateTime.Now;
            Console.WriteLine("時間:{0},DateTimeKind:{1}", dt, dt.Kind);
            var dt2 = DateTime.UtcNow;
            Console.WriteLine("時間:{0},DateTimeKind:{1}", dt, dt2.Kind);
            Console.ReadKey();
時間:2019/6/28 14:47:58,DateTimeKind:Local
時間:2019/6/28 14:47:58,DateTimeKind:Utc

TimeSpan

TimeSpan是一個結構體,表示一個時間間隔。如果需要進行比較精確的計算可以通過ticks(100納秒爲單位)來計算。

// 構造方式 顯然由於年和月不是一個固定的時間刻度,所以不能按年和月構造時間刻度。
public TimeSpan(long ticks);
public TimeSpan(int hours, int minutes, int seconds);
public TimeSpan(int days, int hours, int minutes, int seconds);
public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds);

DateTimeOffset

DateTimeOffset是一個結構體,包含了Datetime值和Offset值,用於定義當前的時間和相對於零時區的偏移量。
它提供了以下幾個方面的功能:
• 日期和時間的計算
• 類型轉換 和DateTime類型互換
• 比較:兩個DateTimeOffset值的比較會通過轉換成UTC時間進行。

            var dt = DateTime.Now;
            var dtoffset = DateTimeOffset.Now;
            Console.WriteLine("當前系統時間:{0},DateTimeOffset:{1}",dt,dtoffset);
//當前系統時間:2019/6/28 15:12:45,DateTimeOffset:2019/6/28 15:12:45 +08:00
            var dateOffset1 = DateTimeOffset.Now;
            var dateOffset2 = DateTimeOffset.UtcNow;
            var difference = dateOffset1 - dateOffset2;
            Console.WriteLine("{0} - {1} = {2}",
                              dateOffset1, dateOffset2, difference);
//2019/6/28 15:19:26 +08:00 - 2019/6/28 7:19:26 +00:00 = -00:00:00.0068201
//構造方式:
public DateTimeOffset(DateTime dateTime);
public DateTimeOffset(long ticks, TimeSpan offset);
public DateTimeOffset(DateTime dateTime, TimeSpan offset);
public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, TimeSpan offset);
public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, TimeSpan offset);
public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, TimeSpan offset);

在SQL Server2008 以上版本提供了datetimeoffset(7)的字段類型,可以進行一系列排序或比較操作。
參考:https://docs.microsoft.com/zh-cn/dotnet/api/system.datetimeoffset?view=netframework-4.8

選擇DateTime還是DateTimeOffset?

DateTime 結構適用於執行以下操作的應用程序:
• 僅使用日期。
• 只使用時間。
• 使用抽象的日期和時間。
• 使用缺少時區信息的日期和時間。
• 只使用 UTC 日期和時間。
• 從.NET 中,外部的源,如 SQL 數據庫中檢索日期和時間信息。 通常,這些源按與 DateTime 結構兼容的簡單格式存儲日期和時間信息。
• 執行日期和時間算法,但不關注常規結果。 例如,在向特定日期和時間添加六個月的加法運算中,是否將結果調整爲夏令時通常並不重要。
DateTimeOffset 結構表示日期和時間值,以及指示此值與 UTC 的差異程度的偏移量。 因此,此值始終明確地標識單個時間點。它適合於應用程序執行以下操作:
• 唯一、明確地標識單個時間點。 DateTimeOffset 類型可用於明確定義“現在”的含義、記錄事務時間、記錄系統或應用程序事件時間以及記錄創建和修改時間。
• 執行常規日期和時間算法。
• 保留多個相關時間,只要這些時間存儲爲兩個單獨的值或結構中的兩個成員。

參考:https://docs.microsoft.com/zh-cn/dotnet/standard/datetime/choosing-between-datetime?view=netframework-4.8

DateTimeOffset 的使用範圍比DateTime更加廣泛,應該優先考慮使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章