Home > OS >  AutoHotKey Date Parsing returning today's date
AutoHotKey Date Parsing returning today's date

Time:05-17

I had been working on a QoL macro to convert selected text dates into a desired format. For that purpose I was trying to utilize the DateParse function that is referenced from the AHK archive forum. After going through all 7 pages of that thread none of the implementations seem to be working for me. It appears that regardless of my date string sent into the first RegEx expression a blank value is returned. Beyond that its still not clear to me why it would be returning today's date. The FormateTime function would return today's date but that function is not used in DateParse.

Any help to achieve the desired output of converting selected text to YYYY-MM-DD from an unknown date format would be appreciated.

Some inputs I have tested so far are:

20220202
2022/02/02
May 1, 2022

All return the current date from the DateParse function. I am using AHK version 1.1.30.2.

Here is the code that I am testing on as its own standalone script:

/*
Function: DateParse
Converts almost any date format to a YYYYMMDDHH24MISS value.

Parameters:
str - a date/time stamp as a string

Returns:
A valid YYYYMMDDHH24MISS value which can be used by FormatTime, EnvAdd and other timecommands.

Example:
> time := DateParse("2:35 PM, 27 November, 2007")

License:
- Version 1.05 <https://ahknet.autoh...ene/#dateparse>
- Dedicated to the public domain (CC0 1.0) <http://creativecommo...main/zero/1.0/>
*/

DateParse(str) {
     static e2 = "i)(? :(\d{1,2} )[\s\.\-\/,] )?(\d{1,2}| 
     (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\w*)[\s\.\-\/,] (\d{2,4})"
     str := RegExReplace(str, "((?:" . SubStr(e2, 42, 47) . ")\w*)(\s*)(\d{1,2})\b", "$3$2$1", "", 1)
     If RegExMatch(str, "i)^\s*(? :(\d{4})([\s\-:\/])(\d{1,2})\2(\d{1,2}))?"
          . "(?:\s*[T\s](\d{1,2})([\s\-:\/])(\d{1,2})(?:\6(\d{1,2})\s*(?:(Z)|(\ |\-)?"
          . "(\d{1,2})\6(\d{1,2})(?:\6(\d{1,2}))?)?)?)?\s*$", i)
          d3 := i1, d2 := i3, d1 := i4, t1 := i5, t2 := i7, t3 := i8
     Else If !RegExMatch(str, "^\W*(\d{1,2} )(\d{2})\W*$", t)
          RegExMatch(str, "i)(\d{1,2})\s*:\s*(\d{1,2})(?:\s*(\d{1,2}))?(?:\s*([ap]m))?", t)
          , RegExMatch(str, e2, d)
     f = %A_FormatFloat%
     SetFormat, Float, 02.0
     d := (d3 ? (StrLen(d3) = 2 ? 20 : "") . d3 : A_YYYY)
          . ((d2 := d2   0 ? d2 : (InStr(e2, SubStr(d2, 1, 3)) - 40) // 4   1.0) > 0
          ? d2   0.0 : A_MM) . ((d1  = 0.0) ? d1 : A_DD) . t1
            (t1 = 12 ? t4 = "am" ? -12.0 : 0.0 : t4 = "am" ? 0.0 : 12.0) . t2   0.0 . t3   0.0
     SetFormat, Float, %f%
     Return, d
 }

 ^!B::Reload

 ^`;::
      clipSave := Clipboard                                         
      Clipboard =                                                   ; Empty the clipboard so that ClipWait has something to detect
      SendInput, ^c                                                 ; copy selected text
      ClipWait
      StringReplace, Clipboard, Clipboard, `r`n, `n, All                ; Fix for SendInput sending Windows linebreaks

      msgbox Input is %Clipboard%
      msgbox % "Result is " vDate := DateParse(Clipboard)


      ;vDate := Clipboard
      ;Msgbox % vDate
      ;vDate := DateParse("2022-02-02")
      ;Msgbox % vDate
      ;FormatTime, vDate, %test%, d
      ;Msgbox % vDate
    
    
    
      ;FormatTime, %myDate, ahk_now, yyyy-MM-dd
      ;send %myDate%

      ;Len:= Strlen(Clipboard)                                      ;Set number of characters
      ;SendInput %Clipboard%                                            ;Send new string
      ;SendInput  {left %Len%}                                      ;Re-select text
      VarSetCapacity(OutputText, 0)                                     ;free memory
      Clipboard := clipSave                                             ;Restore previous clipboard
 return

Answer
Going off the suggestion from T_lube I searched for a more direct conversion of certain formats and found another code sample (Albeiro & Mikeyww) that got me very close to the final result.

ConvDate(dateString, dateType := "", locale := "Month first")
{   ; Version .: 19-09-2020
    ; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=80896
    ; From @mikeyww (modified Albireo)
    ;
    ; dateString - The date in some dateformat
    ;
    ; Convertformat .:
    ; dateType = 1 - handle .:
    ;   - Tue Aug 11 13:59:27 2020 to (yyyymmdd - 20200811)
    ; locale := "Month first" Assume that the month is placed first in the string (default)
    ; eg.
    ; ConvDate(dt, TypeDate, locale)
    ; ConvDate("Tue Aug 11 13:59:27 2020", "1")         => 20200811
    ; ConvDate("13.10.20")                              => 20201013
    ; ConvDate("20.07.03")                              => 20200703
    ; ConvDate("7.5.2020")                              => 20200705
    ; ConvDate("7.5.2020",,"Day first")                 => 20200507
    ; ConvDate("01192020")                              => 20200119
    ;
    ; ConvDate("19-8-2020")                             => 20200819 - Not affected by "Month first" or "Day first"
    ; ConvDate("2020.05.07")                            => 20200507 - Not affected by "Month first" or "Day first"
    ; ConvDate("19012020")                              => 20200119 - Not affected by "Month first" or "Day first"
    ; ConvDate("200928")                                => 20200928 - Not affected by "Month first" or "Day first"
    ; ConvDate("2020/10/05")                            => 20201005 - Not affected by "Month first" or "Day first"
    ; ConvDate("2020-10-05")                            => 20201005 - Not affected by "Month first" or "Day first"
    ; ConvDate("5 okt 2020")                            => 20201005 - Not affected by "Month first" or "Day first"
    ; ConvDate("05 okt 2020")                           => 20201005 - Not affected by "Month first" or "Day first"
    ; ConvDate("5 oktober 2020")                        => 20201005 - Not affected by "Month first" or "Day first"
    ; ConvDate("5-10-2020")                             => 20201005 - Not affected by "Month first" or "Day first"
    ; ConvDate("Okt 5, 2020")                           => 20201005 - Not affected by "Month first" or "Day first"
    ;
    ; ConvDate("20200119")                              => 20200119 - Not affected by "Month first" or "Day first"
    ; ConvDate("20200119")                              => 20200119 - Not affected by "Month first" or "Day first"
    ; ConvDate("01192020")                              => 20200119 - "Only first" or "Day first"
    ;
    ; ConvDate("10.8.20") ERROR                         => 1020 (- Input not handled)
    ; ConvDate("10.8.20",,"Day first") ERROR            => 0820 (- Input not handled)
    ; ConvDate("8.19.2020",,"Day first") ERROR          => 20201908 (- Wrong date input)
    ; ConvDate("01192020",, "Day first") ERROR          => 20201901 (- Wrong date input)
    
    ; different test dates 
    ;   vDate := ConvDate("Tue Aug 11 13:59:27 2020", 1, "Month first")
    ;   vDate := ConvDate("30 juli 2020",, "Day first")
    ;   vDate := ConvDate("09-10-20",, "Day first") ; dd-mm-yy
    ;   vDate := ConvDate("15-05-20",, "Day first") ; dd-mm-yy
    ;   vDate := ConvDate("200820",, "Day first") ; ddmmyy
    ;   vDate := ConvDate("200730") ; yymmdd
    ;   vDate := ConvDate("200818") ; yymmdd
    ;   vDate := ConvDate("200928") ; yymmdd
    ;   vDate := ConvDate("20201013")
    ;   vDate := ConvDate("20-05-06") ; yy-mm-dd
    ;   vDate := ConvDate("20.07.03") ; yy.mm.dd
    ;   vDate := ConvDate("18.08.2020",, "Day first")
    ;   vDate := ConvDate("13.10.2020",, "Day first")
    
    mos := {    jan: 1
        ,   feb: 2
        ,   mar: 3
        ,   apr: 4
        ,   maj: 5
        ,   may: 5
        ,   jun: 6
        ,   jul: 7
        ,   aug: 8
        ,   sep: 9
        ,   okt: 10
        ,   oct: 10
        ,   nov: 11
        ,   dec: 12}
    yeard := False
    part := []
    new := []
    dt := StrReplace(dateString, ",")   ; Rensa bort alla "kommatecken" 
    
    If dateType
    {   If dateType = 1
        {   ; To Convert - Tue Aug 11 13:59:27 2020 - to  (20200811)
            locale := "Day first"
            dt1 := StrSplit(dateString, A_Space)
            dt := dt1.3 " " dt1.2 " " dt1.5
        }
    }
        
    md := locale = "Month first" ? True : False
    
    If RegExMatch(dt, "^\d{2}[-.]\d{2}[-.]\d{2}$")
        dt := RegExReplace(dt, "[-.]")

    If RegExMatch(dt, "^\d{6}$")
    {   f2 := SubStr(dt, 1, 2)
        If (f2 > 19)
            dt := f2   2000 SubStr(dt, 3)
        else
            dt := SubStr(dt, 1, 4) Substr(dt, 5)   2000
    }
    
    If RegExMatch(dt, "\d{8}")
        ff := (SubStr(dt, 1, 4) > 1999) * 2, 
            . dt := SubStr(dt, 1, (ff   3) - 1) "-" 
            . SubStr(dt, ff   3, (ff   5)- (ff   3)) "-" 
            . SubStr(dt, ff   5)
    part := StrSplit(dt, [A_Space, "/", "-", "."])              ; Get year, month, day
    For index, this in part
    {   If this is alpha
        {   For moName, moNum in mos
            {   If SubStr(this, 1, 3) = moName                  ; Month matched a known string
                    part[index] := moNum, md := index = 1 ? True : False ; If string starts with alpha month, then day follows it
            }
        }
    }
    For index, this in part
    {   If !RegExMatch(this, "\d{4}")                           ; This part is not the year
        {   md := this > 12 ? (index > 1 ? True : False) : md   ; For numbers > 12, if it's second, then month precedes it
            new[new[3-md] ? md 2 : 3-md] := this                ; Populate the month or day slot
        } Else new[1] := this, md := index = 1 ? True : md      ; If year comes first, then the month follows it
    }
    
    Return Format("{}-{:02}-{:02}", new[1], new[2], new[3])
 }

CodePudding user response:

I am fairly familiar with regex, and I do not think that some of those expressions will compile correctly (specifically "?:" within a literal string). Try using ErrorLevel by msgbox after each regexMatch call. I'd be willing to bet it is throwing errors silently about compile issues.

Were I in this position, I would figure out a limited number of formats that you are going to run into and simply check through each one with regexMatch. I think this would be much more maintainable code.

  • Related