ASP.NET MVC Controls | ComponentOne
Working with Controls / CollectionView / Work with CollectionView / DateTime Processing
In This Topic
    DateTime Processing
    In This Topic

    This topic demonstrates how to keep a DateTime property in a UTC format on both server and client when using FlexGrid with Ajax Binding or Editing. To achieve this, we first need to understand how the DateTime values are processed at client side and server side.

    On the Server side, every time you use the DateTime class, you need to specify the Kind property, which indicates whether the time represented by this instance is based on local time, Coordinated Universal Time (UTC), or neither. However, when you are working with DateTime object at the Client side, the browser implicitly convert all dates according to the local time when the date is parsed from a Number to Date object.

    For example, when you create a DateTime instance on server side, such as new DateTime(2017, 0, 25, 7, 0, 0, DateTimeKind.Utc);. Once you transfer this value from server to client, browsers on different machines which use different TimeZone system settings show different string representations.

    To keep time in the UTC format, you need to apply an explicit transformation to the dates on both client and server.

    In case of server, you need to convert all the DateTime objects to Unspecified and, then convert them back when necessary using CREATE, UPDATE or DELETE operations. You need to implement the following two steps.

    1. Convert to Unspecified format during reading data.
    2. Convert Unspecified format back during CREATE, UPDATE and DELETE operations.

    To understand the transformation, we will take an example of the FlexGrid control, which include DateTime fields with different formats, such as: Utc, Local and Unspecified. Bind the grid with these data values.

    Create a new Model

    1. Add a new class to the folder Models (for example: DatesData.cs). For more information on how to add a new model, see Adding Controls.
    2. Replace the following code in the new model to define the classes that serve as a data source for the FlexGrid control.
      DatesData.cs
      Copy Code
      using System;
      using System.Collections.Generic;
      using System.Linq;
      
      namespace DateTimeFields.Models
      {
          public class DatesData
          {
               
              // The primary key.
              public int Id { get; set; }
      
              // A DateTime field which Kind is Utc.
              public DateTime UtcDateTime { get; set; }
      
              // A DateTime field which Kind is Unspecified.
              public DateTime UnspecifiedDateTime { get; set; }
      
              // A DateTime field which Kind is Local.
              public DateTime LocalDateTime { get; set; }
              
              // Get the data.
              // <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;
              }
          }
      }
      

    Controller

    In Code - HomeController.cs

    C#
    Copy Code
    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)
            {
               // Convert Unspecified DateTime back.
               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));
            }
        }
    }
    

    Add View

    In Code - Index.cshtml

    DatesData.cs
    Copy Code
    @(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)))
    
    In case of Client, C1 MVC components provide two client events OnClientReponseTextParsing and OnClientRequestDataStringifying to make explicit transformations.
    • OnClientReponseTextParsing - When the data is retrieved on the client from the server, all data is serialized into a JSON text and the text will be retrieved on client. On the client, the text will be parsed into JavaScript objects. You need to perform transformation during parsing. It supports the following event arguments.
      • Key - Name of the item text to be parsed.
      • Value: The text of the item to be parsed.
      • Result: Specifies its value with what you want the text to be parsed to.
      • Cancel: If you don’t want the default parsing, specify its value to true.
    • OnClientRequestDataStringifying - When the data is sent back to the server from the client. The data will be serialized into a text and sent to the server. On the server the text will be de-serialized into an object. The JavaScript Date object is always to be serialized into a text with UTC format. If we don't customize the serialization, a DateTime object in UTC format is retrieved after de-serialization. This is not the correct way of implementing, you need to do transformation during parsing, using the OnClientRequestDataStringifying event.
      • Key - Name of the item text to be serialized.
      • Value: Name of the item to be serialized.
      • Result: Specifies the text what you want the object to be serialized.
      • Cancel: If you don’t want the default serialization, specify it to true.

    To understand the transformation, we will take an example of the FlexGrid control, which include DateTime fields with different formats, such as: Utc, Local and Unspecified. Bind the grid with these data values

    Add a new Model

    1. Add a new data class to the Models folder. The example uses the same model DatesData.cs created in the Server section.

    Add a new Controller

    In Code - HomeController.cs

    C#
    Copy Code
    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));
      }
     }
    }
    

    Add a JavaScript file.

    You need to add a JavaScript file (For example: app.js) to define the responseTextParsing and requestDataStringfying function.

    app.js
    Copy Code
    // The RegExp object which is used to tell a DateTime text.
    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;
        // check whether it is a valid DateTime text.
        var matched = dateJsonRegx.exec(dateText);
        if (!matched) {
            return;
        }
        var timeZoneText = matched[2];
        var dateKind = getDateKind(timeZoneText);
        // only customize the parsing for the Date object in Utc or Local format.
        if (dateKind == c1.mvc.DateKind.Unspecified) {
            return;
        }
        if (typeof dateText === 'string' && matched)
    
        {
            var index = dateText.indexOf(timeZoneText);
            // remove the time zone text and create a Date object.
            var date = new Date(dateText.substr(0, index));
            // Don't forget to set the dateKind for the Date object parsed.
            // It could be used in 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];
            // only customize the serialization for the Date object in Utc format.
            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) {
        // Format integers to have at least k digits.
        var text = n.toString();
        while (text.length < k) {
            text = '0' + text;
        }
        return text;
    }
    

    Add View

    In Code - Index.cshtml

    DatesData.cs
    Copy Code
    @(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)))