2014年7月11日 星期五

時區到各瀏覽器顯示時間統一處理方式

今天要討論的是,當日期時間資料送到瀏覽器時,顯示出來的結果往往讓人難以捉摸。

DateTimeDateTimeOffset轉成JSON以後的結果:
{
  "DateTimeOffset": "2014-07-11T00:00:00+08:00",
  "DateTime": "2014-07-11T00:00:00"
}
DateTimeOffset後面多了+08:00,表示這個時間是特定時區的時間,而DateTime是沒有的。

這時候丟到JavaScript的Date裡面:
  new Date(DateTimeOffset);
  new Date(DateTime);
呈現在畫面上就會變成:
Browser DateTimeOffset DateTime
Chrome Fri Jul 11 2014 00:00:00 GMT+0800 (台北標準時間) Fri Jul 11 2014 08:00:00 GMT+0800 (台北標準時間)
IE11 Fri Jul 11 2014 00:00:00 GMT+0800 (台北標準時間) Fri Jul 11 2014 00:00:00 GMT+0800 (台北標準時間)
FireFox Fri Jul 11 2014 00:00:00 GMT+0800 (台北標準時間) Fri Jul 11 2014 00:00:00 GMT+0800 (台北標準時間)
當沒有指定時區時,顯示出來的結果因各瀏覽器定義不同,而有不同的結果。

當不想要資料庫所有欄位都記時區的時候,就需要變更一下Json的DateTimeConverter

首先宣告一個UTCDateTimeConverter,並且繼承IsoDateTimeConverter ,然後在轉JSON的時候自動轉換成設定的格式

備註1:希望給瀏覽器自動轉換為Local時間,所以是轉換成UTC(國際標準時間)
備註2:避免日後Newtonsoft.Json對IsoDateTimeConverter更新,所以不重寫WriteJson,使用設定轉換格式並由IsoDateTimeConverter的WriteJson進行轉換。
/// <summary>
/// DateTime/DateTimeOffset 傳換為UTC日期格式 (Ex: 2014-07-11T12:53:00+00:00).
/// </summary>
public class UTCDateTimeConverter : IsoDateTimeConverter
{
  /// <summary>
  /// 日期轉成Json格式
  /// </summary>
  /// <param name="writer" >The JsonWriter to write to.</param>
  /// <param name="value" >The value.</param>
  /// <param name="serializer" >The calling serializer.</param>
  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  {
    //DateTimeOffset轉換時會自動加上時區,所以要先把他轉成UTC時間的DateTime
    if (value.GetType() == typeof(DateTimeOffset))
    {
      DateTimeOffset tmpDateTimeOffset = (DateTimeOffset)value;
      value = tmpDateTimeOffset.DateTime.Add(-tmpDateTimeOffset.Offset);
    }
    //設定想要轉換的格式
    base.DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.FFFFFFFK+00:00";
    //使用預設的WriteJson進行轉換
    base.WriteJson(writer: writer, value: value, serializer: serializer);
  }
}
因為希望傳出去時間格式是統一的,而使用的也是WebApi,所以直接在App_Start/WebApiConfig.cs進行設定:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new UTCDateTimeConverter());

轉出來的Json為:
{
  "DateTimeOffset": "2014-07-10T16:00:00+00:00",
  "DateTime": "2014-07-11T00:00:00+00:00"
}
DateTimeOffset已經是被剪了8小時。

呈現在畫面上就會變成:
Browser DateTimeOffset DateTime
Chrome Fri Jul 11 2014 00:00:00 GMT+0800 (台北標準時間) Fri Jul 11 2014 08:00:00 GMT+0800 (台北標準時間)
IE11 Fri Jul 11 2014 00:00:00 GMT+0800 (台北標準時間) Fri Jul 11 2014 08:00:00 GMT+0800 (台北標準時間)
FireFox Fri Jul 11 2014 00:00:00 GMT+0800 (台北標準時間) Fri Jul 11 2014 08:00:00 GMT+0800 (台北標準時間)


瀏覽器都統一了^^

參考:
IsoDateTimeConverter Source
Add different Json.NET DateTimeConverters through the JsonFormatter's SerializerSettings
Create a JSON.NET Date to String custom Converter

2014年7月8日 星期二

ASP .NET MVC4 WebApi-CultureInfo-文化特性排序

相信很多人都發生過,本機排序正常,一上到正式主機以後,排序出來的結果卻不一樣。

其實.NET的執行緒就跟SQL Server的資料表一樣有定序問題,所以排序結果會因為設定而有所不同,基本上執行緒的定序是跟著Server預設語系的。

在變更執行緒的語系之前,先看一下CultureInfo類別

MSDN的說明是:提供有關特定文化特性 (Culture) 的資訊 (文化特性在 Unmanaged 程式碼開發中稱為「地區設定」(Locale))。 提供的資訊包括文化特性的名稱、書寫系統、使用的曆法,以及日期和排序字串的格式。

最後面那一句話,排序字串的格式

了解了這個類別的用途,接著來看一下幾個語系排序的規則。

文化特性名稱
文化特性
預設的排序名稱和識別項
替代排序名稱和識別項
zh-TW
中文 (台灣)
筆劃:0x00000404
注音符號:0x00030404
zh-CN
中文 (中華人民共和國)
發音:0x00000804
筆劃:0x00020804
ja-JP
日文 (日本)
預設:0x00000411
Unicode:0x00010411

知道各語系排序規則以後,接著我們只需要在排序前加上一個指定就搞定了。

System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(language);

這樣是不是很簡單呢。(明明為了排序搞了很久)

參考:
CultureInfo與中文字串排序
CultureInfo 類別