1

In looking at Fuzzy Date Time Picker Control in C# .NET? Piotr Czaapla's answer to that question is exactly what I need.

Unfortunately, I'm a VB.NET guy and I'm not that familiar with lambda expresions, so my attempts to convert the code have resulted in hours of misplaced parenthesis and banging my head with a brick.

Any chance some bi-lingual wizard could convert the C# code to VB.net for me?

Here's the code in question:

class FuzzyDateTime
{

static List<string> dayList = new List<string>() { "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
static List<IDateTimePattern> parsers = new List<IDateTimePattern>()
{
   new RegexDateTimePattern (
        @"next +([2-9]\d*) +months",
        delegate (Match m) {
            var val = int.Parse(m.Groups[1].Value); 
            return DateTime.Now.AddMonths(val);
        }
   ),
   new RegexDateTimePattern (
        @"next +month",
        delegate (Match m) { 
            return DateTime.Now.AddMonths(1);
        }
   ),           
   new RegexDateTimePattern (
        @"next +([2-9]\d*) +days",
        delegate (Match m) {
            var val = int.Parse(m.Groups[1].Value); 
            return DateTime.Now.AddDays(val);
        }
   ),

   new RegexDateTimePattern (
        @"([2-9]\d*) +months +ago",
        delegate (Match m) {
            var val = int.Parse(m.Groups[1].Value); 
            return DateTime.Now.AddMonths(-val);
        }
   ),
   new RegexDateTimePattern (
        @"([2-9]\d*) days +ago",
        delegate (Match m) {
            var val = int.Parse(m.Groups[1].Value); 
            return DateTime.Now.AddDays(-val);
        }
   ),
   new RegexDateTimePattern (
        @"([2-9]\d*) *h(ours)? +ago",
        delegate (Match m) {
            var val = int.Parse(m.Groups[1].Value); 
            return DateTime.Now.AddMonths(-val);
        }
   ),
   new RegexDateTimePattern (
        @"tomorrow",
        delegate (Match m) {
            return DateTime.Now.AddDays(1);
        }
   ),
   new RegexDateTimePattern (
        @"today",
        delegate (Match m) {
            return DateTime.Now;
        }
   ),
   new RegexDateTimePattern (
        @"yesterday",
        delegate (Match m) {
            return DateTime.Now.AddDays(-1);
        }
   ),
   new RegexDateTimePattern (
        @"(last|next) *(year|month)",
        delegate (Match m) {
            int direction = (m.Groups[1].Value == "last")? -1 :1;
            switch(m.Groups[2].Value) 
            {
                case "year":
                    return new DateTime(DateTime.Now.Year+direction, 1,1);
                case "month":
                    return new DateTime(DateTime.Now.Year, DateTime.Now.Month+direction, 1);
            }
            return DateTime.MinValue;
        }
   ),
   new RegexDateTimePattern (
        String.Format(@"(last|next) *({0}).*", String.Join("|", dayList.ToArray())), //handle weekdays
        delegate (Match m) {
            var val = m.Groups[2].Value;
            var direction = (m.Groups[1].Value == "last")? -1 :1;
            var dayOfWeek = dayList.IndexOf(val.Substring(0,3));
            if (dayOfWeek >= 0) {
                var diff = direction*(dayOfWeek - (int)DateTime.Today.DayOfWeek);
                if (diff <= 0 ) { 
                    diff = 7 + diff;
                }
                return DateTime.Today.AddDays(direction * diff);
            }
            return DateTime.MinValue;
        }
   ),

   new RegexDateTimePattern (
        @"(last|next) *(.+)", // to parse months using DateTime.TryParse
        delegate (Match m) {
            DateTime dt;
            int direction = (m.Groups[1].Value == "last")? -1 :1;
            var s = String.Format("{0} {1}",m.Groups[2].Value, DateTime.Now.Year + direction);
            if (DateTime.TryParse(s, out dt)) {
                return dt;
            } else {
                return DateTime.MinValue;
            }
        }
   ),
   new RegexDateTimePattern (
        @".*", //as final resort parse using DateTime.TryParse
        delegate (Match m) {
            DateTime dt;
            var s = m.Groups[0].Value;
            if (DateTime.TryParse(s, out dt)) {
                return dt;
            } else {
                return DateTime.MinValue;
            }
        }
   ),
};

public static DateTime Parse(string text)
{
    text = text.Trim().ToLower();
    var dt = DateTime.Now;
    foreach (var parser in parsers)
    {
        dt = parser.Parse(text);
        if (dt != DateTime.MinValue)
            break;
    }
    return dt;
}
}

interface IDateTimePattern
{
    DateTime Parse(string text);
}

class RegexDateTimePattern : IDateTimePattern
{
    public delegate DateTime Interpreter(Match m);
    protected Regex regEx;
    protected Interpreter inter;
    public RegexDateTimePattern(string re, Interpreter inter)
    {
        this.regEx = new Regex(re);
        this.inter = inter;
    }
    public DateTime Parse(string text)
    {
        var m = regEx.Match(text);

        if (m.Success)
        {
            return inter(m);
        }
        return DateTime.MinValue;
    }
}
Community
  • 1
  • 1
G B
  • 53
  • 2
  • 6
  • Have you tried online c# to vb.net conversion tools like http://converter.telerik.com/ ? – Bala R Mar 23 '12 at 13:18
  • 2
    Why are you trying to convert it to vb? Compile it in c# and reference the assembly in your vb project. – L.B Mar 23 '12 at 13:18
  • @Bala - I did try a couple of c# to vb.net converters, but the lambda expression got mangled up. – G B Mar 23 '12 at 13:21
  • 1
    @L.B - I thought about that, but I'd like to enhance the code to include a bunch of other use cases, and since I could do that much more effectively and efficiently in VB - I'd prefer to have it converted. – G B Mar 23 '12 at 13:23
  • 1
    i would switch them to real lambdas from using `delegate`. – Daniel A. White Mar 23 '12 at 13:24
  • anonymous delegates != lambdas – stephenbayer Mar 23 '12 at 13:24
  • @BalaR The VB.net code from the Telerik converter also does NOT compile – G B Mar 23 '12 at 13:28
  • @DanielA.White & stephenbayer - It's exactly the anonymous delegates / lamdas distinction that is above my pay grade! – G B Mar 23 '12 at 13:39
  • Post what you've got (manual/tool) along with the error messages. – H H Mar 23 '12 at 13:41

3 Answers3

3

OK, there's a bunch of code there, I'm not going to translate it all but for example (assuming VS2010 or later) . . .

new RegexDateTimePattern (
    @"next +([2-9]\d*) +months",
    delegate (Match m) {
        var val = int.Parse(m.Groups[1].Value); 
        return DateTime.Now.AddMonths(val);
    }
)

will be

    Dim p2 = New RegexDateTimePattern(
    "next +([2-9]\d*) +months",
    Function(m)
        Dim val = Int.Parse(m.Groups(1).Value)
        Return DateTime.Now.AddMonths(val)
    End Function
    )

and

new RegexDateTimePattern (
    String.Format(@"(last|next) *({0}).*", String.Join("|", dayList.ToArray())), 
    delegate (Match m) {
        var val = m.Groups[2].Value;
        var direction = (m.Groups[1].Value == "last")? -1 :1;
        var dayOfWeek = dayList.IndexOf(val.Substring(0,3));
        if (dayOfWeek >= 0) {
            var diff = direction*(dayOfWeek - (int)DateTime.Today.DayOfWeek);
            if (diff <= 0 ) { 
                diff = 7 + diff;
            }
            return DateTime.Today.AddDays(direction * diff);
        }
        return DateTime.MinValue;
    }
),

becomes

    Dim p = New RegexDateTimePattern(
       String.Format("(last|next) *({0}).*", String.Join("|", dayList.ToArray())),
       Function(m)
           Dim val = m.Groups(2).Value
           Dim direction = If(m.Groups(1).Value = "last", -1, 1)
           Dim dayOfWeek = dayList.IndexOf(val.Substring(0, 3))
           If (dayOfWeek >= 0) Then
               Dim diff = direction * (dayOfWeek - CType(DateTime.Today.DayOfWeek, Integer))
               If (diff <= 0) Then
                   diff = 7 + diff
               End If
               Return DateTime.Today.AddDays(direction * diff)
           End If
           Return DateTime.MinValue
       End Function
       )

Is there anything else in there that's twisting your mellon?

If it's not VS2010 (or even if it is and you don't like the lambda expressions), then all you need to do is take each of the lambda expressions (the "in line" functions in the VB code), create explicit named functions for each one (e.g. RegexDateTimePattern_Helper2 for the last one), and pass AddressOf RegexDateTimePattern_Helper2 instead of the lambda expression

i.e.

    Function RegexDateTimePattern_Helper2(Match m) as DateTime ''# Dreadful name
        dim val = m.Groups[2].Value
        dim direction = if(m.Groups(1).Value = "last", -1 ,1)
        Dim dayOfWeek = dayList.IndexOf(Val.Substring(0, 3))
        If (dayOfWeek >= 0) Then
            Dim diff = direction * (dayOfWeek - CType(DateTime.Today.DayOfWeek, Integer))
            If (diff <= 0) Then
                diff = 7 + diff
            End If
            Return DateTime.Today.AddDays(direction * diff)
        End If
        Return DateTime.MinValue
    End Function

. . .

New RegexDateTimePattern (
    String.Format("(last|next) *({0}).*", String.Join("|", dayList.ToArray())), 
    AddressOf RegexDateTimePattern_Helper2)
N_A
  • 19,799
  • 4
  • 52
  • 98
Binary Worrier
  • 50,774
  • 20
  • 136
  • 184
  • FWIW I fixed the vb.net comment in your post. It now correctly ends the comment on that line instead of continuing on for a few lines. `''#` fixes it. – N_A Mar 23 '12 at 14:05
  • This looks like a great start - let me wrestle with it for a few hours and see if I have additional questions. Thanks again! – G B Mar 23 '12 at 15:34
0

The converter here spits this compilable chunk of code out (after removing a few "Do"s and a couple "var"s):

Imports System.Text.RegularExpressions

Class FuzzyDateTime

Shared dayList As New List(Of String)() From { _
 "sun", _
 "mon", _
 "tue", _
 "wed", _
 "thu", _
 "fri", _
 "sat" _
}
Shared parsers As New List(Of IDateTimePattern)() From { _
 New RegexDateTimePattern("next +([2-9]\d*) +months", Function(m As Match)
                                                          Dim val = Integer.Parse(m.Groups(1).Value)
                                                          Return DateTime.Now.AddMonths(val)
                                                      End Function), _
 New RegexDateTimePattern("next +month", Function(m As Match) DateTime.Now.AddMonths(1)), _
 New RegexDateTimePattern("next +([2-9]\d*) +days", Function(m As Match)
                                                        Dim val = Integer.Parse(m.Groups(1).Value)
                                                        Return DateTime.Now.AddDays(val)

                                                    End Function), _
 New RegexDateTimePattern("([2-9]\d*) +months +ago", Function(m As Match)
                                                         Dim val = Integer.Parse(m.Groups(1).Value)
                                                         Return DateTime.Now.AddMonths(-val)
                                                     End Function), _
 New RegexDateTimePattern("([2-9]\d*) days +ago", Function(m As Match)
                                                      Dim val = Integer.Parse(m.Groups(1).Value)
                                                      Return DateTime.Now.AddDays(-val)
                                                  End Function), _
 New RegexDateTimePattern("([2-9]\d*) *h(ours)? +ago", Function(m As Match)
                                                           Dim val = Integer.Parse(m.Groups(1).Value)
                                                           Return DateTime.Now.AddMonths(-val)
                                                       End Function), _
 New RegexDateTimePattern("tomorrow", Function(m As Match) DateTime.Now.AddDays(1)), _
 New RegexDateTimePattern("today", Function(m As Match) DateTime.Now), _
 New RegexDateTimePattern("yesterday", Function(m As Match) DateTime.Now.AddDays(-1)), _
 New RegexDateTimePattern("(last|next) *(year|month)", Function(m As Match)
                                                           Dim direction As Integer = If((m.Groups(1).Value = "last"), -1, 1)
                                                           Select Case m.Groups(2).Value
                                                               Case "year"
                                                                   Return New DateTime(DateTime.Now.Year + direction, 1, 1)
                                                               Case "month"
                                                                   Return New DateTime(DateTime.Now.Year, DateTime.Now.Month + direction, 1)
                                                           End Select
                                                           Return DateTime.MinValue
                                                           ''#handle weekdays
                                                       End Function), _
 New RegexDateTimePattern([String].Format("(last|next) *({0}).*", [String].Join("|", dayList.ToArray())), Function(m As Match)
                                                                                                              Dim val = m.Groups(2).Value
                                                                                                              Dim direction = If((m.Groups(1).Value = "last"), -1, 1)
                                                                                                              Dim dayOfWeek = dayList.IndexOf(val.Substring(0, 3))
                                                                                                              If dayOfWeek >= 0 Then
                                                                                                                  Dim diff = direction * (dayOfWeek - CInt(DateTime.Today.DayOfWeek))
                                                                                                                  If diff <= 0 Then
                                                                                                                      diff = 7 + diff
                                                                                                                  End If
                                                                                                                  Return DateTime.Today.AddDays(direction * diff)
                                                                                                              End If
                                                                                                              Return DateTime.MinValue

                                                                                                              ''# to parse months using DateTime.TryParse
                                                                                                          End Function), _
 New RegexDateTimePattern("(last|next) *(.+)", Function(m As Match)
                                                   Dim dt As DateTime
                                                   Dim direction As Integer = If((m.Groups(1).Value = "last"), -1, 1)
                                                   Dim s = [String].Format("{0} {1}", m.Groups(2).Value, DateTime.Now.Year + direction)
                                                   If DateTime.TryParse(s, dt) Then
                                                       Return dt
                                                   Else
                                                       Return DateTime.MinValue
                                                   End If
                                                   ''#as final resort parse using DateTime.TryParse

                                               End Function), _
 New RegexDateTimePattern(".*", Function(m As Match)
                                    Dim dt As DateTime
                                    Dim s = m.Groups(0).Value
                                    If DateTime.TryParse(s, dt) Then
                                        Return dt
                                    Else
                                        Return DateTime.MinValue
                                    End If
                                End Function) _
}

    Public Shared Function Parse(text As String) As DateTime
        text = text.Trim().ToLower()
        Dim dt = DateTime.Now
        For Each parser In parsers
            dt = parser.Parse(text)
            If dt <> DateTime.MinValue Then
                Exit For
            End If
        Next
        Return dt
    End Function
End Class

Interface IDateTimePattern
    Function Parse(text As String) As DateTime
End Interface

Class RegexDateTimePattern
    Implements IDateTimePattern
    Public Delegate Function Interpreter(m As Match) As DateTime
    Protected regEx As Regex
    Protected inter As Interpreter
    Public Sub New(re As String, inter As Interpreter)
        Me.regEx = New Regex(re)
        Me.inter = inter
    End Sub
    Public Function Parse(text As String) As DateTime Implements IDateTimePattern.Parse
        Dim m = regEx.Match(text)

        If m.Success Then
            Return inter(m)
        End If
        Return DateTime.MinValue
    End Function
End Class
N_A
  • 19,799
  • 4
  • 52
  • 98
0

I translated one pattern and the rest of the stuff. Now you just need to add the other patterns ( Which shouldn't be a big deal. Its tested in Module1.

Module Module1
    Sub Main()
        Dim res = FuzzyDateTime.Parse("next 2 months")
    End Sub
End Module

Public Interface IDateTimePattern
    Function Parse(text As String) As DateTime
End Interface

Public Class FuzzyDateTime
    Shared dayList As List(Of String) = New List(Of String)() From {"sun", "mon", "tue", "wed", "thu", "fri", "sat"}
    Shared parsers As List(Of IDateTimePattern) = New List(Of IDateTimePattern)()
    Shared Sub New()
        parsers.Add(New RegexDateTimePattern("next +([2-9]\d*) +months", New Func(Of Match, DateTime)(Function(x As Match)
                                                                                                          Dim val = Integer.Parse(x.Groups(1).Value)
                                                                                                          Return DateTime.Now.AddMonths(val)
                                                                                                      End Function)))
    End Sub
    Public Shared Function Parse(ByVal text As String) As DateTime
        text = text.Trim().ToLower()
        Dim dt = DateTime.Now
        For Each parser In parsers
            dt = parser.Parse(text)
            If Not dt = DateTime.MinValue Then
                Exit For
            End If
        Next
        Return dt
    End Function
End Class
Public Class RegexDateTimePattern : Implements IDateTimePattern
    Protected inter As Func(Of Match, DateTime)
    Protected regEx As Regex
    Public Function Parse(text As String) As Date Implements IDateTimePattern.Parse
        Dim m = regEx.Match(text)
        If m.Success Then Return inter(m)
        Return DateTime.MinValue
    End Function
    Public Sub New(ByVal re As String, ByVal inter As Func(Of Match, DateTime))
        Me.regEx = New Regex(re)
        Me.inter = inter
    End Sub
End Class

PS: I added the patterns in the static constructor (Add them wherever you like though), couldn't be assed with the inline initialization - its just plain ugly in my opinion.

Alex
  • 7,901
  • 1
  • 41
  • 56