Skip to content

Commit

Permalink
fix: Updates for Autohotkey 2.0.11 and added Unittests
Browse files Browse the repository at this point in the history
Refers to #1
  • Loading branch information
hoppfrosch committed Jan 23, 2024
1 parent 4bb9376 commit ad7128d
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 41 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# DateParse [![AutoHotkey2](https://img.shields.io/badge/Language-AutoHotkey2-red.svg)](https://autohotkey.com/)
Converts almost any date format to a YYYYMMDDHH24MISS value.
# DateParse

[![AutoHotkey2](https://img.shields.io/badge/Language-AutoHotkey2-green?style=plastic&logo=autohotkey)](https://autohotkey.com/)

This library uses *AutoHotkey Version 2*.
<sub><sup>This library uses [AutoHotkey Version 2](https://autohotkey.com/v2/). (Tested with [AHK v2.0-11](https://github.com/AutoHotkey/AutoHotkey/releases))</sup></sub>

This repository only offers released version of this library - **development is taking place unter [DateParse-Develop](https://github.com/hoppfrosch/DateParse-Develop)**
Converts almost any date format to a YYYYMMDDHH24MISS value.

## Usage

Expand All @@ -12,7 +13,7 @@ Include `DateParse.ahk`from the `lib` folder into your project using standard Au

## Examples

For more examples see module source.
For more examples see unittests.

```autohotkey
#include DateParse.ahk
Expand Down
108 changes: 72 additions & 36 deletions lib/DateParse.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
; Description ...: Converts almost any date format to a YYYYMMDDHH24MISS value.
; Modified ......: 2017.11.01
; Author ........: * Original - dougal/polyethene (original)
; ............... * 20171101 - hoppfrosch
; ............... * 20171101 - hoppfrosch
; .............................. * update to V2
; .............................. * added more dates to be parsed
; Licence .......: https://creativecommons.org/publicdomain/zero/1.0/
Expand Down Expand Up @@ -72,84 +72,120 @@ DateParse(str, americanOrder := 0) {
, dayAndMonthName := "(?:(?<Month>" . monthNames . ")[^a-zA-Z0-9:.]*(?<Day>\d{1,2})[^a-zA-Z0-9]+|(?<Day>\d{1,2})[^a-zA-Z0-9:.]*(?<Month>" . monthNames . "))"
, monthNameAndYear := "(?<Month>" . monthNames . ")[^a-zA-Z0-9:.]*(?<Year>(?:\d{4}|\d{2}))"

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) { ;ISO 8601 timestamps
ampm := "am"
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) { ;ISO 8601 timestamps
year := i.1, month := i.3, day := i.4, hour := i.5, minute := i.7, second := i.8
}
else if !RegExMatch(str, "^\W*(?<Hour>\d{1,2}+)(?<Minute>\d{2})\W*$", t){ ; NOT timestring only eg 1535
else if !RegExMatch(str, "^\W*(?<Hour>\d{1,2}+)(?<Minute>\d{2})\W*$", &t){ ; NOT timestring only eg 1535
; Try to extract the time parts
FoundPos := RegExMatch(str, "i)(\d{1,2})" ;hours
. "\s*:\s*(\d{1,2})" ;minutes
. "(?:\s*:\s*(\d{1,2}))?" ;seconds
. "(?:\s*([ap]m))?", timepart) ;am/pm
. "(?:\s*([ap]m))?", &timepart) ;am/pm
if (FoundPos) {
; Time is already parsed correctly from striing
hour := timepart.1
minute := timepart.2
second := timepart.3
ampm:= timepart.4
; Remove time to parse the date part only
str := StrReplace(str, timepart.value)
str := StrReplace(str, timepart.0)
}
; Now handle the remaining string without time and try to extract date ...
if RegExMatch(str, "Ji)" . dayAndMonthName . "[^a-zA-Z0-9]*(?<Year>(?:\d{4}|\d{2}))?", d) { ; named month eg 22May14; May 14, 2014; 22May, 2014
if RegExMatch(str, "Ji)" . dayAndMonthName . "[^a-zA-Z0-9]*(?<Year>(?:\d{4}|\d{2}))?", &d) { ; named month eg 22May14; May 14, 2014; 22May, 2014
year := d.Year, month := d.Month, day := d.Day
}
else if RegExMatch(str, "i)" . monthNameAndYear, d) { ; named month and year without day eg May14; May 2014
else if RegExMatch(str, "i)" . monthNameAndYear, &d) { ; named month and year without day eg May14; May 2014
year := d.Year, month := d.Month
}
else if RegExMatch(str, "i)" . "^\W*(?<Year>\d{4})(?<Month>\d{2})\W*$", d) { ; month and year as digit only eg 201710
else if RegExMatch(str, "i)" . "^\W*(?<Year>\d{4})(?<Month>\d{2})\W*$", &d) { ; month and year as digit only eg 201710
year := d.Year, month := d.Month
}
else {
if RegExMatch(str, "i)(\d{4})[^a-zA-Z0-9:.]+" . dayAndMonth, d) { ;2004/22/03
; Default values - if some parts are not given
if (not IsSet(day) and not IsSet(month) and not IsSet(year)) {
; No datepart is given - use today
year := A_YYYY
month := A_MM
day := A_DD
}
if RegExMatch(str, "i)(\d{4})[^a-zA-Z0-9:.]+" . dayAndMonth, &d) { ;2004/22/03
year := d.1, month := d.3, day := d.2
}
else if RegExMatch(str, "i)" . dayAndMonth . "(?:[^a-zA-Z0-9:.]+((?:\d{4}|\d{2})))?", d) { ;22/03/2004 or 22/03/04
else if RegExMatch(str, "i)" . dayAndMonth . "(?:[^a-zA-Z0-9:.]+((?:\d{4}|\d{2})))?", &d) { ;22/03/2004 or 22/03/04
year := d.3, month := d.2, day := d.1
}
if (RegExMatch(day, monthNames) or americanOrder and !RegExMatch(month, monthNames) or (month > 12 and day <= 12)) { ;try to infer day/month order
tmp := month, month := day, day := tmp
}
}
}
else if RegExMatch(str, "^\W*(?<Hour>\d{1,2}+)(?<Minute>\d{2})\W*$", timepart){ ; timestring only eg 1535
else if RegExMatch(str, "^\W*(?<Hour>\d{1,2}+)(?<Minute>\d{2})\W*$", &timepart){ ; timestring only eg 1535
hour := timepart.hour
minute := timepart.minute
; Default values - if some parts are not given
if (not IsSet(day) and not IsSet(month) and not IsSet(year)) {
; No datepart is given - use today
year := A_YYYY
month := A_MM
day := A_DD
}
}
if (day or month or year) and not (day and month and year) { ; partial date
if not month or not (day or month) or (hour and not day) { ; partial date must have month and day with time or day or year without time

if (IsSet(day) or IsSet(month) or IsSet(year)) and not (IsSet(day) and IsSet(month) and IsSet(year)) { ; partial date
if (IsSet(year) and not IsSet(month)) or not (IsSet(day) or IsSet(month)) or (IsSet(hour) and not IsSet(day)) { ; partial date must have month and day with time or day or year without time
return
}
else if not day { ; without time use 1st for day if not present
day := 1
}
}

; Default values - if some parts are not given
if (IsSet(year) and IsSet(month) and not IsSet(day)) {
; year and month given without day - use first day
day := 1
}

; Format the single parts
oYear := (StrLen(year) == 2 ? "20" . year : (year ? year : A_YYYY))
oYear := (StrLen(year) == 2 ? "20" . year : (year))
oYear := Format("{:02.0f}", oYear)

oMonth := ((month := month + 0 ? month : InStr(monthNames, SubStr(month, 1, 3)) // 4 ) > 0 ? month + 0.0 : A_MM)
oMonth := Format("{:02.0f}", oMonth)

oDay := ((day += 0.0) ? day : A_DD)

if (isInteger(month)) {
currMonthInt := month
} else {
currMonthInt := InStr(monthNames, SubStr(month, 1, 3)) // 4
}
; Original: oMonth := ((month := month + 0 ? month : InStr(monthNames, SubStr(month, 1, 3)) // 4 ) > 0 ? month + 0.0 : A_MM)
; oMonth := ((month := month + 0 ? month : currMonthInt ) > 0 ? month + 0.0 : A_MM)
; oMonth := Format("{:02.0f}", oMonth)
oMonth := Format("{:02.0f}", currMonthInt)

oDay := day
oDay := Format("{:02.0f}", oDay)

if (hour <> "") {
oHour := hour + (hour == 12 ? ampm = "am" ? -12.0 : 0.0 : ampm = "pm" ? 12.0 : 0.0)
oHour := Format("{:02.0f}", oHour)

if (minute <> "") {
oMinute := minute + 0.0
oMinute := Format("{:02.0f}", oMinute)

if (second <> "") {
oSecond := second + 0.0
oSecond := Format("{:02.0f}", oSecond)
if (IsSet(hour)) {
if (hour != "") {
oHour := hour + (hour == 12 ? ampm = "am" ? -12.0 : 0.0 : ampm = "pm" ? 12.0 : 0.0)
oHour := Format("{:02.0f}", oHour)

if (IsSet(minute)) {
oMinute := minute + 0.0
oMinute := Format("{:02.0f}", oMinute)

if (IsSet(second)) {
if (second != "") {
oSecond := second + 0.0
oSecond := Format("{:02.0f}", oSecond)
}
}
}
}
}

d := oYear . oMonth . oDay . oHour . oMinute . oSecond
return d

retVal := oYear . oMonth . oDay
if (IsSet(oHour)){
retVal := retVal . oHour . oMinute
if (IsSet(oSecond)) {
retVal := retVal . oSecond
}
}
return retVal
}
59 changes: 59 additions & 0 deletions test/unittest.ahk
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#Requires AutoHotkey v2.0-
#Warn
#SingleInstance force

#include "%A_ScriptDir%\..\lib\DateParse.ahk"

testcases := []
/*
> dt := DateParse("May1960") ; -> "19600501"
> dt := DateParse("25May1960") ; -> "19600525"
> dt := DateParse("201710") ; -> "20171001"
> ; YYYYMMDD is to be replaced with today
> dt := DateParse("1532") ; -> "YYYYMMDD1532"
> dt := DateParse("11:26") ; -> "YYYYMMDD1126"
> dt := DateParse("2:35 PM") ; -> "YYYYMMDD1435"
> dt := DateParse("11:22:24 AM") ; -> "YYYYMMDD112224"
*/

; date-time examples
testcases.Push(Map('data', "2:35 PM, 27 November, 2007", 'expected', "200711271435"))
testcases.Push(Map('data', "4:55 am Feb 27, 2004" , 'expected', "200402270455"))
testcases.Push(Map('data', "Mon, 17 Aug 2009 13:23:33 GMT", 'expected', "20090817132333"))
testcases.Push(Map('data', "07 Mar 2009 13:43:58", 'expected', "20090307134358"))
testcases.Push(Map('data', "2007-06-26T14:09:12Z", 'expected', "20070626140912"))
testcases.Push(Map('data', "2007-06-25 18:52", 'expected', "200706251852"))

; date-only examples
testcases.Push(Map('data', "19/2/05", 'expected', "20050219"))
testcases.Push(Map('data', "10/12/2007", 'expected', "20071210"))
testcases.Push(Map('data', "3/15/2009", 'expected', "20090315"))
testcases.Push(Map('data', "05-Jan-00", 'expected', "20000105"))
testcases.Push(Map('data', "Jan-06-00", 'expected', "20000106"))
testcases.Push(Map('data', "Dec-31-13", 'expected', "20131231"))
testcases.Push(Map('data', "Wed 6/27/2007", 'expected', "20070627"))
testcases.Push(Map('data', "May1960", 'expected', "19600501"))
testcases.Push(Map('data', "25May1960", 'expected', "19600525"))
testcases.Push(Map('data', "201710", 'expected', "20171001"))

; time only examples -> todays date should be added automatically in output
today := FormatTime(A_Now, "yyyyMMdd")
testcases.Push(Map('data', "1532", 'expected', today . "1532"))
testcases.Push(Map('data', "11:26", 'expected', today . "1126"))
testcases.Push(Map('data', "2:35 PM", 'expected', today . "1435"))
testcases.Push(Map('data', "11:22:33 AM", 'expected', today . "112233"))

For Index, Value in testcases {
dt := DateParse(value["data"])
str := Format("{:02.0f}", Index)
if (dt != value["expected"]) {
str:= str . " - ** FAILURE ** "
} else {
str:= str . " - SUCCESS "
}
str := str . " - Input: " Format("{:-30}",value["data"]) " * Expected: " Format("{:-15}",value["expected"]) " * Got: " Format("{:-15}",dt)
OutputDebug str "`n"
}
ExitApp()

0 comments on commit ad7128d

Please sign in to comment.