ASP.NET MVC コントロールヘルプ
DateTime処理
コントロールの使用 > CollectionView > CollectionViewの使用 > DateTime処理

このトピックでは、FlexGridとAjax連結またはAjax編集を使用する場合に、サーバーとクライアントの両方でUTC形式のDateTimeプロパティを維持する方法を示します。それには、まずクライアント側とサーバー側でDateTime値がどのように処理されるかを理解する必要があります。

サーバー側では、DateTimeクラスを使用するたびに、Kindプロパティを指定する必要があります。このプロパティは、そのインスタンスで表される時刻がローカル時間か、協定世界時(UTC)か、またはそのどちらでもないかを示します。一方、DateTimeオブジェクトをクライアント側で使用する場合は、日付がNumberからDateオブジェクトに解析されるときに、ブラウザによってすべての日付が暗黙にローカル時間に変換されます。

たとえば、サーバー側でnew DateTime(2017, 0, 25, 7, 0, 0, DateTimeKind.Utc);のようにDateTimeインスタンスを作成したとします。この値をサーバーからクライアントに転送すると、異なるTimeZoneシステム設定を使用するさまざまなマシンのブラウザごとに、異なる文字列表現が表示されます。

UTC形式の時刻を維持するには、クライアントとサーバーの両方で日付に明示的な変換を適用する必要があります。

サーバーでは、すべてのDateTimeオブジェクトをUnspecifiedに変換し、必要に応じてCREATE、UPDATE、またはDELETE操作を使用してこれを元に戻します。次の2つの手順を実装する必要があります。

  1. データを読み取るときにUnspecified形式に変換する。
  2. CREATE、UPDATE、DELETEの各操作でUnspecified形式を元に戻す。

この変換を理解するために、Utc、Local、Unspecifiedなどの形式のDateTimeフィールドを含むFlexGridコントロールを例にして、このグリッドをデータ値と連結します。

新しいモデルの作成

  1. [モデル]フォルダに新しいクラスを追加します(例:DatesData.cs)。新しいモデルを追加する方法の詳細については、「コントロールの追加」を参照してください。
  2. 新しいモデルの次のコードを書き換えて、FlexGridコントロールのデータソースになるクラスを定義します。
    DatesData.cs
    コードのコピー
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace DateTimeFields.Models
    {
        public class DatesData
        {
             
            /// 主キー。
            public int Id { get; set; }
    
            /// KindがUtcのDateTimeフィールド。
            public DateTime UtcDateTime { get; set; }
    
            /// KindがUnspecifiedのDateTimeフィールド。
            public DateTime UnspecifiedDateTime { get; set; }
    
            /// KindがLocalのDateTimeフィールド。
            public DateTime LocalDateTime { get; set; }
            
            /// データを取得します。
            /// <param name="total"></param>
            /// <returns></returns>
            public static IEnumerable<DatesData> GetData(int total)
            {
                var rand = new Random(0);
                var dt = DateTime.Now;
                var list = Enumerable.Range(0, total).Select(i =>
                {
                    return new DatesData
                    {
                        Id = i + 1,
                        UtcDateTime = new DateTime(dt.Year, i % 12 + 1, 25, 7, 0, 0, DateTimeKind.Utc),
                        UnspecifiedDateTime = new DateTime(dt.Year, i % 12 + 1, 25, 7, 0, 0, DateTimeKind.Unspecified),
                        LocalDateTime = new DateTime(dt.Year, i % 12 + 1, 25, 7, 0, 0, DateTimeKind.Local)
                    };
                });
    
                return list;
            }
        }
    }
    

コントローラー

コード内 - HomeController.cs

C#
コードのコピー
using C1.Web.Mvc;
using C1.Web.Mvc.Serialization;
using <ApplicationName>.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

namespace DateTimeFields.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        private static List<DatesData> convertedData = DatesData.GetData(3).ToList();
        private static List<DatesData> ConvertToUnspecifiedData(IEnumerable<DatesData> sourceData)
        {
            return sourceData.Select(item => new DatesData
            {
                Id = item.Id,
                UnspecifiedDateTime = item.UnspecifiedDateTime,
                UtcDateTime = new DateTime(item.UtcDateTime.Ticks),
                LocalDateTime = new DateTime(item.LocalDateTime.Ticks)
            }).ToList();
        }

        private static void ConvertUpspecifiedBack(CollectionViewEditRequest<DatesData> requestData)
        {
            // UnspecifiedのDateTimeを元に戻します。
            foreach (var item in requestData.OperatingItems)
            {
                item.LocalDateTime = new DateTime(item.LocalDateTime.Ticks, DateTimeKind.Local);
                item.UtcDateTime = new DateTime(item.UtcDateTime.Ticks, DateTimeKind.Utc);
            }
        }
        public ActionResult Converted_ReadDatesData([C1JsonRequest] CollectionViewRequest<DatesData> requestData)
        {
            return this.C1Json(CollectionViewHelper.Read(requestData, ConvertToUnspecifiedData(convertedData)));
        }
        public ActionResult Converted_UpdateDatesData([C1JsonRequest]CollectionViewEditRequest<DatesData> requestData)
        {
            ConvertUpspecifiedBack(requestData);
            return Update(requestData, convertedData, ConvertToUnspecifiedData);
        }
        public ActionResult Converted_CreateDatesData([C1JsonRequest]CollectionViewEditRequest<DatesData> requestData)
        {
            ConvertUpspecifiedBack(requestData);
            return Create(requestData, convertedData, ConvertToUnspecifiedData);
        }
        public ActionResult Converted_DeleteDatesData([C1JsonRequest]CollectionViewEditRequest<DatesData> requestData)
        {
            ConvertUpspecifiedBack(requestData);
            return Delete(requestData, convertedData, ConvertToUnspecifiedData);
        }

        public ActionResult Update(CollectionViewEditRequest<DatesData> requestData, List<DatesData> sourceData, Func<IEnumerable<DatesData>, List<DatesData>> converter = null)
        {
            return this.C1Json(CollectionViewHelper.Edit(requestData, item =>
            {
                var error = string.Empty;
                var success = true;
                try
                {
                    var index = sourceData.FindIndex(u => u.Id == item.Id);
                    sourceData.RemoveAt(index);
                    sourceData.Insert(index, item);
                }
                catch (Exception e)
                {
                    error = e.Message;
                    success = false;
                }
                return new CollectionViewItemResult<DatesData>
                {
                    Error = error,
                    Success = success,
                    Data = item
                };
            }, () => converter != null ? converter(sourceData) : sourceData));
        }

        public ActionResult Create(CollectionViewEditRequest<DatesData> requestData, List<DatesData> sourceData, Func<IEnumerable<DatesData>, List<DatesData>> converter = null)
        {
            return this.C1Json(CollectionViewHelper.Edit(requestData, item =>
            {
                var error = string.Empty;
                var success = true;
                try
                {
                    sourceData.Add(item);
                    item.Id = sourceData.Max(u => u.Id) + 1;
                }
                catch (Exception e)
                {
                    error = e.Message;
                    success = false;
                }
                return new CollectionViewItemResult<DatesData>
                {
                    Error = error,
                    Success = success,
                    Data = item
                };
            }, () => converter != null ? converter(sourceData) : sourceData));
        }

        public ActionResult Delete(CollectionViewEditRequest<DatesData> requestData, List<DatesData> sourceData, Func<IEnumerable<DatesData>, List<DatesData>> converter = null)
        {
            return this.C1Json(CollectionViewHelper.Edit(requestData, item =>
            {
                var error = string.Empty;
                var success = true;
                try
                {
                    var index = sourceData.FindIndex(u => u.Id == item.Id);
                    sourceData.RemoveAt(index);
                }
                catch (Exception e)
                {
                    error = e.Message;
                    success = false;
                }
                return new CollectionViewItemResult<DatesData>
                {
                    Error = error,
                    Success = success,
                    Data = item
                };
            }, () => converter != null ? converter(sourceData) : sourceData));
        }
    }
}

ビューの追加

コード内 - Index.cshtml

DatesData.cs
コードのコピー
@(Html.C1().FlexGrid()
    .Id("convertedGrid")
    .AllowAddNew(true)
    .AllowDelete(true)
    .AutoGenerateColumns(false)
    .Bind(cvb => cvb.Bind(Url.Action("Converted_ReadDatesData"))
                    .Create(Url.Action("Converted_CreateDatesData"))
                    .Update(Url.Action("Converted_UpdateDatesData"))
                    .Delete(Url.Action("Converted_DeleteDatesData")))
    .Columns(columns =>
    {
        columns.Add(column => column.Binding("Id").IsReadOnly(true).Visible(false));
        columns.Add(column => column.Binding("UtcDateTime").Format("dd/MM/yyyy HH:mm:ss").Width("*"));
        columns.Add(column => column.Binding("LocalDateTime").Format("dd/MM/yyyy HH:mm:ss").Width("*"));
        columns.Add(column => column.Binding("UnspecifiedDateTime").Format("dd/MM/yyyy HH:mm:ss").Width("*"));
    })
    .Filterable(f => f.DefaultFilterType(FilterType.Both)))
クライアントでは、明示的な変換を行うために、OnClientReponseTextParsingOnClientRequestDataStringifyingの2つのクライアントイベントがComponentOne MVCコンポーネントに用意されています。
  • OnClientReponseTextParsing - サーバーからクライアントにデータが取得されるときに、すべてのデータがJSONテキストにシリアライズされ、そのテキストがクライアントに取得されます。クライアントでは、そのテキストがJavaScriptオブジェクトに解析されます。解析中に変換を実行する必要があります。次のイベント引数をサポートします。
    • Key - 解析される項目テキストの名前。
    • Value:解析される項目のテキスト。
    • Result:テキストの解析後の値を指定します。
    • Cancel:デフォルトの解析が必要ない場合は、この値をtrueに設定します。
  • OnClientRequestDataStringifying - サーバーからクライアントにデータが戻される場合。データがテキストにシリアライズされ、サーバーに送信されます。サーバーで、テキストがシリアライズ解除されてオブジェクトに変換されます。JavaScript Dateオブジェクトは、常にUTC形式のテキストにシリアライズされます。シリアライズをカスタマイズしない場合は、シリアライズ解除後にUTC形式のDateTimeオブジェクトが取得されます。これは、正しい実装方法ではありません。解析中にOnClientRequestDataStringifyingイベントを使用して変換を行う必要があります。
    • Key - シリアライズされる項目テキストの名前。
    • Value:シリアライズされる項目の名前。
    • Result:オブジェクトのシリアライズ後のテキストを指定します。
    • Cancel:デフォルトのシリアライズが必要ない場合は、これをtrueに設定します。

この変換を理解するために、Utc、Local、Unspecifiedなどの形式のDateTimeフィールドを含むFlexGridコントロールを例にして、このグリッドをデータ値と連結します。

新しいモデルの追加

  1. 新しいデータクラスを[モデル]フォルダに追加します。 この例では、サーバーセクションで作成した同じDatesData.csモデルを使用します。

新しいコントローラーの追加

コード内 - HomeController.cs

C#
コードのコピー
using C1.Web.Mvc;
using C1.Web.Mvc.Serialization;
using <ApplicationName>.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

namespace DateTimeFields.Controllers {
 public class HomeController: Controller {
  public ActionResult Index() {
   return View();
  }

  private static List <DatesData> customData = DatesData.GetData(3).ToList();
  public ActionResult Custom_ReadDatesData([C1JsonRequest] CollectionViewRequest <DatesData> requestData) {
   return this.C1Json(CollectionViewHelper.Read(requestData, customData));
  }

  public ActionResult Custom_UpdateDatesData([C1JsonRequest] CollectionViewEditRequest <DatesData> requestData) {
   return Update(requestData, customData);
  }

  public ActionResult Custom_CreateDatesData([C1JsonRequest] CollectionViewEditRequest <DatesData> requestData) {
   return Create(requestData, customData);
  }

  public ActionResult Custom_DeleteDatesData([C1JsonRequest] CollectionViewEditRequest <DatesData> requestData) {
   return Delete(requestData, customData);
  }

  public ActionResult Update(CollectionViewEditRequest <DatesData> requestData, List <DatesData> sourceData, Func <IEnumerable <DatesData> , List <DatesData>> converter = null) {
   return this.C1Json(CollectionViewHelper.Edit(requestData, item => {
    var error = string.Empty;
    var success = true;
    try {
     var index = sourceData.FindIndex(u => u.Id == item.Id);
     sourceData.RemoveAt(index);
     sourceData.Insert(index, item);
    } catch (Exception e) {
     error = e.Message;
     success = false;
    }
    return new CollectionViewItemResult <DatesData> {
     Error = error,
     Success = success,
     Data = item
    };
   }, () => converter != null ? converter(sourceData) : sourceData));
  }

  public ActionResult Create(CollectionViewEditRequest <DatesData> requestData, List <DatesData> sourceData, Func <IEnumerable <DatesData> , List <DatesData>> converter = null) {
   return this.C1Json(CollectionViewHelper.Edit(requestData, item => {
    var error = string.Empty;
    var success = true;
    try {
     sourceData.Add(item);
     item.Id = sourceData.Max(u => u.Id) + 1;
    } catch (Exception e) {
     error = e.Message;
     success = false;
    }
    return new CollectionViewItemResult <DatesData> {
     Error = error,
     Success = success,
     Data = item
    };
   }, () => converter != null ? converter(sourceData) : sourceData));
  }

  public ActionResult Delete(CollectionViewEditRequest <DatesData> requestData, List <DatesData> sourceData, Func <IEnumerable <DatesData> , List <DatesData>> converter = null) {
   return this.C1Json(CollectionViewHelper.Edit(requestData, item => {
    var error = string.Empty;
    var success = true;
    try {
     var index = sourceData.FindIndex(u => u.Id == item.Id);
     sourceData.RemoveAt(index);
    } catch (Exception e) {
     error = e.Message;
     success = false;
    }
    return new CollectionViewItemResult <DatesData> {
     Error = error,
     Success = success,
     Data = item
    };
   }, () => converter != null ? converter(sourceData) : sourceData));
  }
 }
}

JavaScriptファイルを追加します。

responseTextParsing関数とrequestDataStringfying関数を定義するJavaScriptファイル(例:app.js)を追加する必要があります。

app.js
コードのコピー
// DateTimeテキストの判別に使用されるRegExpオブジェクト。
var dateJsonRegx = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d*)?(Z|[\+\-]\d{2}:\d{2}|)$/;
function reponseTextParsing(sender, args) {
    var dateText = args.value;
    // これが有効なDateTimeテキストかどうかをチェックします。
    var matched = dateJsonRegx.exec(dateText);
    if (!matched) {
        return;
    }
    var timeZoneText = matched[2];
    var dateKind = getDateKind(timeZoneText);
    // UtcまたはLocal形式のDateオブジェクトの解析のみをカスタマイズします。
    if (dateKind == c1.mvc.DateKind.Unspecified) {
        return;
    }
    if (typeof dateText === 'string' && matched) {
        var index = dateText.indexOf(timeZoneText);
        // タイムゾーンのテキストを削除し、Dateオブジェクトを作成します。
        var date = new Date(dateText.substr(0, index));
        // 解析されたDateオブジェクトのdateKindを忘れずに設定します。
        // これがOnClientRequestDataStringifyingで使用されます。
        date.dateKind = dateKind;
        args.result = date;
        args.cancel = true;
    }
}

function getDateKind(timeZoneText) {
    if (!timeZoneText) {
        return c1.mvc.DateKind.Unspecified;
    }

    if (timeZoneText.toLowerCase() === 'z') {
        return c1.mvc.DateKind.Utc;
    }

    return c1.mvc.DateKind.Local;
}

function requestDataStringifying(sender, args) {
    if (args.value instanceof Date || args.parent[args.key] instanceof Date) {
        var date = args.value instanceof Date ? args.value : args.parent[args.key];
        // Utc形式のDateオブジェクトのシリアライズのみをカスタマイズします。
        if (!date.dateKind || date.dateKind == c1.mvc.DateKind.Unspecified) {
            return;
        }

        args.result = c1.mvc.Utils.formatNumber(date.getFullYear(), 4) + '-' +
                c1.mvc.Utils.formatNumber(date.getMonth() + 1, 2) + '-' +
                c1.mvc.Utils.formatNumber(date.getDate(), 2) + 'T' +
                c1.mvc.Utils.formatNumber(date.getHours(), 2) + ':' +
                c1.mvc.Utils.formatNumber(date.getMinutes(), 2) + ':' +
                c1.mvc.Utils.formatNumber(date.getSeconds(), 2) + '.' +
                c1.mvc.Utils.formatNumber(date.getMilliseconds(), 3)
                + (date.dateKind == c1.mvc.DateKind.Utc ? 'Z' : getLocalTimeZoneText());
        args.cancel = true;
    }
}

function getLocalTimeZoneText() {
    var date = new Date();
    var timeoffset = date.getTimezoneOffset();
    var result = '';
    if (timeoffset > 0) {
        result += '-';
    } else {
        result += '+';
        timeoffset *= -1;
    }
    var hour = Math.floor(timeoffset / 60);
    result += formatNumber(hour, 2);
    result += ":";
    result += formatNumber(timeoffset - hour * 60, 2);
    return result;
}

function formatNumber(n, k) {
    // 少なくともk桁になるように整数を書式設定します。
    var text = n.toString();
    while (text.length < k) {
        text = '0' + text;
    }
    return text;
}

ビューの追加

コード内 - Index.cshtml

DatesData.cs
コードのコピー
@(Html.C1().FlexGrid()
      .Id("customGrid")
      .AllowAddNew(true)
      .AllowDelete(true)
      .AutoGenerateColumns(false)
      .Bind(cvb => cvb.Bind(Url.Action("Custom_ReadDatesData"))
                 .Create(Url.Action("Custom_CreateDatesData"))
                 .Update(Url.Action("Custom_UpdateDatesData"))
                 .Delete(Url.Action("Custom_DeleteDatesData"))
                 .OnClientReponseTextParsing("reponseTextParsing")
                 .OnClientRequestDataStringifying("requestDataStringifying"))
      .Columns(columns =>
          {
            columns.Add(column => column.Binding("Id").IsReadOnly(true).Visible(false));
            columns.Add(column => column.Binding("UtcDateTime").Format("dd/MM/yyyy HH:mm:ss").Width("*"));
            columns.Add(column => column.Binding("LocalDateTime").Format("dd/MM/yyyy HH:mm:ss").Width("*"));
            columns.Add(column => column.Binding("UnspecifiedDateTime").Format("dd/MM/yyyy HH:mm:ss").Width("*"));
          })
       .Filterable(f => f.DefaultFilterType(FilterType.Both)))