Unplanned
Last Updated: 13 May 2019 11:54 by ADMIN
Roman
Created on: 07 May 2019 11:27
Category: GridView
Type: Bug Report
2
RadGridView: DateTimeParser don't like time prior to date

Hello,

I am having issues with DateTime editor in the grid. We have custom date/time format for our dates - "HH:mm:ss dd-MMM-yyyy" and the datetime editor is set to free form date time with this mask. When user typed a value in the editor it is passed to FreeFormDateTimeProvider.Validate method, which calls DateInput.ParseDate. DateTimeLexer splits this kind of values just fine, but obviously time related tokens comes first and then the date related tokens.

Unfortunately DateTimeParser.Parse method is made the way that it parse date at first and then time from the tokens list. In my case this means time value is parsed and the date value is dropped to default. Why didn't you check that if after ParseDate and ParseTime calls the date portion is null but there are still remaining tokens in the list, so you may call ParseDate one more time?

Do you have any suggestion how could I resolve this issue? Everything related to date time parsing is not extendable at all, starting from DateInput, most of the classes and methods are not public and even some public methods are not virtual. Can I have the Telerik free form date time typing capabilities in the editor but still have time part before date part?

5 comments
ADMIN
Dess | Tech Support Engineer, Principal
Posted on: 13 May 2019 11:54
Hello, Roman,     

If the current text is successfully parsed to a valid DateTime with DateTime.TryParse you can skip the default validation logic. I would recommend you to leave the default logic in the Validate method if the time part is after the date part. Thus, you will ensure that the standard format (date/time) will be handled. Using the time separator in order to indicate whether time is before the date part is reliable approach. However, it can't guarantee that all formats will be handled properly.

The built-in date parsing mechanism in the FreeFormDateTimeProvider is rather complex and it is using formal grammar theory to create tokens and to translate the input into a meaningful date. You can also check the following blog post providing detailed information on how the free form date provider is working internally: https://www.telerik.com/blogs/radinput-2-0-and-date-parsing-where-compiler-theory-meets-user-needs 

Make sure that you follow the feedback item in order to get notified once any changes occur with the issue.

Should you have further questions please let me know.

Regards,
Dess | Tech Support Engineer, Sr.
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Roman
Posted on: 08 May 2019 14:24

Thanks Dess, I already did a similar workaround as you could imagine from my previous comment :)

In my CustomFreeFormDateTimeProvider I am calling my own Parse method (from Validate) which looks like this

private DateTime? Parse(string input)
      {
         if (input == null)
            return null;

         DateTime? result;
         var regex = new Regex(@"^(?<group1>[^\s]+)\s+(?<group2>[^\s]+)$");
         var match = regex.Match(input.Trim());
         if (match.Success)
         {
            var part1 = match.Groups["group1"].Value;
            var part2 = match.Groups["group2"].Value;

            var part1Val = Parser.ParseDate(part1, DateTime.MinValue);
            var part2Val = Parser.ParseDate(part2, DateTime.MinValue);

            if (part1Val == null || part2Val == null)
            {
               result = part1Val ?? part2Val;
            }
            else
            {
               if (part1.Contains(Parser.CultureInfo.DateTimeFormat.TimeSeparator))
               {
                  result = new DateTime(part2Val.Value.Year, part2Val.Value.Month, part2Val.Value.Day,
                     part1Val.Value.Hour, part1Val.Value.Minute, part1Val.Value.Second);
               }
               else
               {
                  result = new DateTime(part1Val.Value.Year, part1Val.Value.Month, part1Val.Value.Day,
                     part2Val.Value.Hour, part2Val.Value.Minute, part2Val.Value.Second);
               }
            }
         }
         else
         {
            result = Parser.ParseDate(input, new DateTime?());
         }

         return result;
      }

I still have to allow user to enter date time value in any format with time before date (as it is shown by default in the cell editor) or with date before time, etc. So this free format parsing is just a saver to me and I want to keep it as much as possible.

So my idea, as you may see above, was to split typed value by whitespace into 2 parts (time and date) and to parse them independently using your default methods. But as I "cannot" predict which part is time and which is date I thought that I may temporary set the format to only time and parse both of them as time with DateTime.Min date as a base date. Like this:

Parser.DateFormat = "T";
var part1Val = Parser.ParseDate(part1, DateTime.MinValue);
var part2Val = Parser.ParseDate(part2, DateTime.MinValue);

then check which of the values have DateTime.MinValue as the date, meaning that this part was the time and I may take it from there. And then return Parser.DateFormat back and parse the other part, which will be just date.

But as I wrote in my previous (a bit emotional) comment, you just ignoring the value passed as baseDate and returning Now + 2 hours (this I cannot understand at all, how it will work if I am at 23:00, will it return next day as date?).

So I cannot check which of the returned values have DateTime.MinValue as date because both will not have it. So to detect which part is time and which is date, I ended up by checking for the time separator in the string as you may see above. Unfortunately this is not fully reliable because it will not work if user typed only e.g. hour like "12 11-May-2019". But I wouldn't expect this case to be frequent so this workaround should work for me. Especially with the quite old version of the Telerik libraries I am using, because I will not able to get any of your fixes even if you will upload them tomorrow.

ADMIN
Dess | Tech Support Engineer, Principal
Posted on: 08 May 2019 13:51
Hello, Roman,     

Free DateTime parsing is a very complex task which we try to handle with the FreeFormDateTimeProvider. We will do our best to improve the parsing logic in order to behave correctly when the time part is before the date part. 

Note that you can replace the default FreeFormDateTimeProvider and extend its Validate method. You don't have external access to the internal basic logic but if the basic validation fails, you can parse the string value by your custom logic. A sample approach is demonstrated in the following code snippet which tries to handle the case with the custom format: "HH:mm:ss dd-MMM-yyyy":

public RadForm1()
{
    InitializeComponent();
 
    GridViewDateTimeColumn dateTimeColumn = new GridViewDateTimeColumn("DateTimeColumn"); 
    dateTimeColumn.FormatString = "{0:HH:mm:ss dd-MMM-yyyy}";
    this.radGridView1.Columns.Add(dateTimeColumn);
    this.radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
 
    this.radGridView1.Rows.Add(DateTime.Now);
    this.radGridView1.CellEditorInitialized += radGridView1_CellEditorInitialized;
}
 
private void radGridView1_CellEditorInitialized(object sender, GridViewCellEventArgs e)
{
    RadDateTimeEditor dtEditor = e.ActiveEditor as RadDateTimeEditor;
    if (dtEditor != null)
    {
        RadDateTimeEditorElement el = dtEditor.EditorElement as RadDateTimeEditorElement;
        el.TextBoxElement.MaskType = MaskType.FreeFormDateTime;
        el.TextBoxElement.Provider = new CustomFreeFormDateTimeProvider(el.TextBoxElement.Mask, el.TextBoxElement.Culture, el.TextBoxElement);
    }
}
 
public class CustomFreeFormDateTimeProvider : FreeFormDateTimeProvider
{
    public CustomFreeFormDateTimeProvider(string mask, CultureInfo culture, RadMaskedEditBoxElement owner) : base(mask, culture, owner)
    {
    }
 
    public override bool Validate(string stringValue)
    {
        bool result = base.Validate(stringValue);
        if (result == false)
        {
            string[] tokens = stringValue.Split(new char[] { ' ', ':' }, StringSplitOptions.RemoveEmptyEntries);
            int timeSeparatorIndex = stringValue.IndexOf(":");
            int dateSeparatorIndex = stringValue.IndexOf("-");
            if (timeSeparatorIndex < dateSeparatorIndex) //time is before the date
            {
                DateTime parsedDate;
                if (DateTime.TryParse(stringValue, out parsedDate))
                {
                    this.Value = parsedDate;
                    return true;
                }
            }
        }
        return result;
    }
}

The attached gif file illustrates the achieved result.

I hope that this approach may be suitable for your scenario. 

Regards,
Dess | Tech Support Engineer, Sr.
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Attached Files:
Roman
Posted on: 08 May 2019 09:45

Oh guys, you killing me. I thought I could split the value myself and parse each part separately, passing DateTime.MinValue as the base to detect which of the parts contains time value. But you just ignoring the base date value at all! In DateTimeEntry class you have

public override DateTime Evaluate(DateTime date)
{
   return this.Time.Evaluate(this.Date.Evaluate(DateTime.Now.AddMilliseconds(7200000.0)));
}

Why? Why do you ignoring date value here and passing some magic value of Now + 2 hour?

As of your suggestion, I already have the custom format in the editor to display the value there by default in this format. By I need to allow users to paste date/time value from other sources to this editor, thus I cannot limit it to the strong mask and this free form date time feature was ideal for me.

ADMIN
Dess | Tech Support Engineer, Principal
Posted on: 08 May 2019 05:56
Hello, Roman,     

Following the provided information, I have prepared a sample project to test the behavior when MaskType.FreeFormDateTime is applied to the RadDateTimeEditor. When I enter the time part before the date like the specified format (HH:mm:ss dd-MMM-yyyy), the value seems to ignore the date part. You can find below my sample code snippet which result is illustrated in the attached gif file:

public RadForm1()
{
    InitializeComponent();
 
    GridViewDateTimeColumn dateTimeColumn = new GridViewDateTimeColumn("DateTimeColumn"); 
    dateTimeColumn.FormatString = "{0:HH:mm:ss dd-MMM-yyyy}";
    this.radGridView1.Columns.Add(dateTimeColumn);
    this.radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
 
    this.radGridView1.Rows.Add(DateTime.Now);
 
    this.radGridView1.CellEditorInitialized += radGridView1_CellEditorInitialized;
}
 
private void radGridView1_CellEditorInitialized(object sender, GridViewCellEventArgs e)
{
    RadDateTimeEditor dtEditor = e.ActiveEditor as RadDateTimeEditor;
    if (dtEditor != null)
    {
        RadDateTimeEditorElement el = dtEditor.EditorElement as RadDateTimeEditorElement;
        el.TextBoxElement.MaskType = MaskType.FreeFormDateTime;
    }
}

I have logged it in our feedback portal by making this thread public. You can track its progress, subscribe for status changes and add your comments on the following link - feedback item.

I have also updated your Telerik points.

Unfortunately, the issue is at a location that is too deep in the FreeFormDateTimeProvider functionality for any customization to have effect on it. I would recommend you to use the same custom format for the editor as well - HH:mm:ss dd-MMM-yyyy

GridViewDateTimeColumn dateTimeColumn = new GridViewDateTimeColumn("DateTimeColumn"); 
dateTimeColumn.FormatString = "{0:HH:mm:ss dd-MMM-yyyy}";
dateTimeColumn.Format = DateTimePickerFormat.Custom;
dateTimeColumn.CustomFormat = "HH:mm:ss dd-MMM-yyyy";
this.radGridView1.Columns.Add(dateTimeColumn);

We will do our best to introduce a fix accordingly. Please excuse us for the inconvenience caused.

I hope this information helps. If you need any further assistance please don't hesitate to contact me. 

Regards,
Dess | Tech Support Engineer, Sr.
Progress Telerik

Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Attached Files: