你須要瞭解的有關.NET日期時間的必要信息

引言

    DateTime數據類型是一個複雜的問題,複雜到足以讓你在編寫【將日期從Web服務器返回到瀏覽器】簡單代碼時感到困惑。javascript

ASP.NET MVC 5和 Web API 2/ASP.NETCore 以不一樣方式序列化日期,這可能會給在一個Web應用程序中同時使用這兩個序列化的開發人員帶來更多混淆。html

本文會盡可能覆蓋 ASP.NET / ASP.NETCore 中與 Date/Time有關的歧義、參數綁定、序列化相關的知識點和坑位。 java

 

Date/Time值歧義和應對策略

  使用Datetime最大的問題在於歧義,整個地球分爲24個時區,每一個時區都有本身的本地時間git

例如一個DateTime值指示了本地時間,這個值在同時區的其餘系統依然標示了同一時間點; 可是在其餘時區之外,這個DateTime值就會有各自的解釋。github

電郵中如有
Sun , 04, June 2019 9:45:29 +0800
說明信件發送第時間是2019年6月04日,星期日,上午9點45分29秒,該地區領先UTC8小時 (+800,就是東八區時間)。
對於發這封郵件的時間,北京本地時9點45,倫敦是凌晨1點45 ......

  正由於這個現狀,在Internet以及無線電通訊時,時間的統一很重要。瀏覽器

  業界提出了不一樣的格式來明確地表示單一時間點, Web開發行業提出了不一樣的解決方案以明肯定義時間值。服務器

  UTC時間

  在國際無線電通訊中,爲避免各自爲戰而廣泛使用一個標準時間,稱爲協調世界時(UTC)網站

 UTC是0時區的時間值,與格林威治標準時間(GMT)同樣,都與英國倫敦的當地時間相同spa

  UTC能準確表明單一時間點,UTC 時間對於住在加利福尼亞和中國的人來講都是同樣的。code

 若是把以上北京時間2019/06/04 09:45:29轉化爲UTC時間,可使用如下公式: UTC + 時區差 = 當地時間。

由於北京時間是東8區,因此這個點UTC時間是 2019/06/04 02:45:29(也可認爲該值是倫敦時間)

 

開發者能夠考慮的解決方案 是: 使用UTC時間存儲date/time、服務端維護、計算也使用UT時間、將UTC時間發送到瀏覽器,瀏覽器javascript在Web頁上轉化爲當地時間。

 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 8601: 2016-10-11T10:18:42z          == 2016-10-11T10:18:42+00:00

// Date: 2016-10-12 10:18:42 Pacific time, 表示該時間值相對於UTC時間值提早6小時
ISO 8601: 2016-10-11T10:18:42-06: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(2019, 6, 4, 9, 45, 29, DateTimeKind.Local);
var dt1_temp = dt1.ToUniversalTime();

var dt2 = new DateTime(2019, 6, 4, 9, 45, 29, DateTimeKind.Utc);
var dt2_temp = dt2.ToUniversalTime();

var dt3 = new DateTime(2019, 6, 4, 9, 45, 29, DateTimeKind.Unspecified);
var dt3_temp = dt3.ToUniversalTime();

var dt4 = new DateTime(2019, 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對象的DateTimKind是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

感謝您的認真閱讀,若有問題請大膽斧正;以爲有用,請下方或加關注。

本文歡迎轉載,但請保留此段聲明,且在文章頁面明顯位置註明本文的做者及原文連接。

相關文章
相關標籤/搜索