Skip to content

Commit ad7128d

Browse files
committed
fix: Updates for Autohotkey 2.0.11 and added Unittests
Refers to #1
1 parent 4bb9376 commit ad7128d

File tree

3 files changed

+137
-41
lines changed

3 files changed

+137
-41
lines changed

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
# DateParse [![AutoHotkey2](https://img.shields.io/badge/Language-AutoHotkey2-red.svg)](https://autohotkey.com/)
2-
Converts almost any date format to a YYYYMMDDHH24MISS value.
1+
# DateParse
2+
3+
[![AutoHotkey2](https://img.shields.io/badge/Language-AutoHotkey2-green?style=plastic&logo=autohotkey)](https://autohotkey.com/)
34

4-
This library uses *AutoHotkey Version 2*.
5+
<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>
56

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

89
## Usage
910

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

1314
## Examples
1415

15-
For more examples see module source.
16+
For more examples see unittests.
1617

1718
```autohotkey
1819
#include DateParse.ahk

lib/DateParse.ahk

+72-36
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
; Description ...: Converts almost any date format to a YYYYMMDDHH24MISS value.
55
; Modified ......: 2017.11.01
66
; Author ........: * Original - dougal/polyethene (original)
7-
; ............... * 20171101 - hoppfrosch
7+
; ............... * 20171101 - hoppfrosch
88
; .............................. * update to V2
99
; .............................. * added more dates to be parsed
1010
; Licence .......: https://creativecommons.org/publicdomain/zero/1.0/
@@ -72,84 +72,120 @@ DateParse(str, americanOrder := 0) {
7272
, 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 . "))"
7373
, monthNameAndYear := "(?<Month>" . monthNames . ")[^a-zA-Z0-9:.]*(?<Year>(?:\d{4}|\d{2}))"
7474

75-
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
75+
ampm := "am"
76+
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
7677
year := i.1, month := i.3, day := i.4, hour := i.5, minute := i.7, second := i.8
7778
}
78-
else if !RegExMatch(str, "^\W*(?<Hour>\d{1,2}+)(?<Minute>\d{2})\W*$", t){ ; NOT timestring only eg 1535
79+
else if !RegExMatch(str, "^\W*(?<Hour>\d{1,2}+)(?<Minute>\d{2})\W*$", &t){ ; NOT timestring only eg 1535
7980
; Try to extract the time parts
8081
FoundPos := RegExMatch(str, "i)(\d{1,2})" ;hours
8182
. "\s*:\s*(\d{1,2})" ;minutes
8283
. "(?:\s*:\s*(\d{1,2}))?" ;seconds
83-
. "(?:\s*([ap]m))?", timepart) ;am/pm
84+
. "(?:\s*([ap]m))?", &timepart) ;am/pm
8485
if (FoundPos) {
8586
; Time is already parsed correctly from striing
8687
hour := timepart.1
8788
minute := timepart.2
8889
second := timepart.3
8990
ampm:= timepart.4
9091
; Remove time to parse the date part only
91-
str := StrReplace(str, timepart.value)
92+
str := StrReplace(str, timepart.0)
9293
}
9394
; Now handle the remaining string without time and try to extract date ...
94-
if RegExMatch(str, "Ji)" . dayAndMonthName . "[^a-zA-Z0-9]*(?<Year>(?:\d{4}|\d{2}))?", d) { ; named month eg 22May14; May 14, 2014; 22May, 2014
95+
if RegExMatch(str, "Ji)" . dayAndMonthName . "[^a-zA-Z0-9]*(?<Year>(?:\d{4}|\d{2}))?", &d) { ; named month eg 22May14; May 14, 2014; 22May, 2014
9596
year := d.Year, month := d.Month, day := d.Day
9697
}
97-
else if RegExMatch(str, "i)" . monthNameAndYear, d) { ; named month and year without day eg May14; May 2014
98+
else if RegExMatch(str, "i)" . monthNameAndYear, &d) { ; named month and year without day eg May14; May 2014
9899
year := d.Year, month := d.Month
99100
}
100-
else if RegExMatch(str, "i)" . "^\W*(?<Year>\d{4})(?<Month>\d{2})\W*$", d) { ; month and year as digit only eg 201710
101+
else if RegExMatch(str, "i)" . "^\W*(?<Year>\d{4})(?<Month>\d{2})\W*$", &d) { ; month and year as digit only eg 201710
101102
year := d.Year, month := d.Month
102103
}
103104
else {
104-
if RegExMatch(str, "i)(\d{4})[^a-zA-Z0-9:.]+" . dayAndMonth, d) { ;2004/22/03
105+
; Default values - if some parts are not given
106+
if (not IsSet(day) and not IsSet(month) and not IsSet(year)) {
107+
; No datepart is given - use today
108+
year := A_YYYY
109+
month := A_MM
110+
day := A_DD
111+
}
112+
if RegExMatch(str, "i)(\d{4})[^a-zA-Z0-9:.]+" . dayAndMonth, &d) { ;2004/22/03
105113
year := d.1, month := d.3, day := d.2
106114
}
107-
else if RegExMatch(str, "i)" . dayAndMonth . "(?:[^a-zA-Z0-9:.]+((?:\d{4}|\d{2})))?", d) { ;22/03/2004 or 22/03/04
115+
else if RegExMatch(str, "i)" . dayAndMonth . "(?:[^a-zA-Z0-9:.]+((?:\d{4}|\d{2})))?", &d) { ;22/03/2004 or 22/03/04
108116
year := d.3, month := d.2, day := d.1
109117
}
110118
if (RegExMatch(day, monthNames) or americanOrder and !RegExMatch(month, monthNames) or (month > 12 and day <= 12)) { ;try to infer day/month order
111119
tmp := month, month := day, day := tmp
112120
}
113121
}
114122
}
115-
else if RegExMatch(str, "^\W*(?<Hour>\d{1,2}+)(?<Minute>\d{2})\W*$", timepart){ ; timestring only eg 1535
123+
else if RegExMatch(str, "^\W*(?<Hour>\d{1,2}+)(?<Minute>\d{2})\W*$", &timepart){ ; timestring only eg 1535
116124
hour := timepart.hour
117125
minute := timepart.minute
126+
; Default values - if some parts are not given
127+
if (not IsSet(day) and not IsSet(month) and not IsSet(year)) {
128+
; No datepart is given - use today
129+
year := A_YYYY
130+
month := A_MM
131+
day := A_DD
132+
}
118133
}
119-
if (day or month or year) and not (day and month and year) { ; partial date
120-
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
134+
135+
if (IsSet(day) or IsSet(month) or IsSet(year)) and not (IsSet(day) and IsSet(month) and IsSet(year)) { ; partial date
136+
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
121137
return
122138
}
123-
else if not day { ; without time use 1st for day if not present
124-
day := 1
125-
}
139+
}
140+
141+
; Default values - if some parts are not given
142+
if (IsSet(year) and IsSet(month) and not IsSet(day)) {
143+
; year and month given without day - use first day
144+
day := 1
126145
}
127146

128147
; Format the single parts
129-
oYear := (StrLen(year) == 2 ? "20" . year : (year ? year : A_YYYY))
148+
oYear := (StrLen(year) == 2 ? "20" . year : (year))
130149
oYear := Format("{:02.0f}", oYear)
131-
132-
oMonth := ((month := month + 0 ? month : InStr(monthNames, SubStr(month, 1, 3)) // 4 ) > 0 ? month + 0.0 : A_MM)
133-
oMonth := Format("{:02.0f}", oMonth)
134-
135-
oDay := ((day += 0.0) ? day : A_DD)
150+
151+
if (isInteger(month)) {
152+
currMonthInt := month
153+
} else {
154+
currMonthInt := InStr(monthNames, SubStr(month, 1, 3)) // 4
155+
}
156+
; Original: oMonth := ((month := month + 0 ? month : InStr(monthNames, SubStr(month, 1, 3)) // 4 ) > 0 ? month + 0.0 : A_MM)
157+
; oMonth := ((month := month + 0 ? month : currMonthInt ) > 0 ? month + 0.0 : A_MM)
158+
; oMonth := Format("{:02.0f}", oMonth)
159+
oMonth := Format("{:02.0f}", currMonthInt)
160+
161+
oDay := day
136162
oDay := Format("{:02.0f}", oDay)
137-
138-
if (hour <> "") {
139-
oHour := hour + (hour == 12 ? ampm = "am" ? -12.0 : 0.0 : ampm = "pm" ? 12.0 : 0.0)
140-
oHour := Format("{:02.0f}", oHour)
141-
142-
if (minute <> "") {
143-
oMinute := minute + 0.0
144-
oMinute := Format("{:02.0f}", oMinute)
145163

146-
if (second <> "") {
147-
oSecond := second + 0.0
148-
oSecond := Format("{:02.0f}", oSecond)
164+
if (IsSet(hour)) {
165+
if (hour != "") {
166+
oHour := hour + (hour == 12 ? ampm = "am" ? -12.0 : 0.0 : ampm = "pm" ? 12.0 : 0.0)
167+
oHour := Format("{:02.0f}", oHour)
168+
169+
if (IsSet(minute)) {
170+
oMinute := minute + 0.0
171+
oMinute := Format("{:02.0f}", oMinute)
172+
173+
if (IsSet(second)) {
174+
if (second != "") {
175+
oSecond := second + 0.0
176+
oSecond := Format("{:02.0f}", oSecond)
177+
}
178+
}
149179
}
150180
}
151181
}
152-
153-
d := oYear . oMonth . oDay . oHour . oMinute . oSecond
154-
return d
182+
183+
retVal := oYear . oMonth . oDay
184+
if (IsSet(oHour)){
185+
retVal := retVal . oHour . oMinute
186+
if (IsSet(oSecond)) {
187+
retVal := retVal . oSecond
188+
}
189+
}
190+
return retVal
155191
}

test/unittest.ahk

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#Requires AutoHotkey v2.0-
2+
#Warn
3+
#SingleInstance force
4+
5+
#include "%A_ScriptDir%\..\lib\DateParse.ahk"
6+
7+
testcases := []
8+
/*
9+
10+
> dt := DateParse("May1960") ; -> "19600501"
11+
> dt := DateParse("25May1960") ; -> "19600525"
12+
> dt := DateParse("201710") ; -> "20171001"
13+
14+
> ; YYYYMMDD is to be replaced with today
15+
> dt := DateParse("1532") ; -> "YYYYMMDD1532"
16+
> dt := DateParse("11:26") ; -> "YYYYMMDD1126"
17+
> dt := DateParse("2:35 PM") ; -> "YYYYMMDD1435"
18+
> dt := DateParse("11:22:24 AM") ; -> "YYYYMMDD112224"
19+
*/
20+
21+
; date-time examples
22+
testcases.Push(Map('data', "2:35 PM, 27 November, 2007", 'expected', "200711271435"))
23+
testcases.Push(Map('data', "4:55 am Feb 27, 2004" , 'expected', "200402270455"))
24+
testcases.Push(Map('data', "Mon, 17 Aug 2009 13:23:33 GMT", 'expected', "20090817132333"))
25+
testcases.Push(Map('data', "07 Mar 2009 13:43:58", 'expected', "20090307134358"))
26+
testcases.Push(Map('data', "2007-06-26T14:09:12Z", 'expected', "20070626140912"))
27+
testcases.Push(Map('data', "2007-06-25 18:52", 'expected', "200706251852"))
28+
29+
; date-only examples
30+
testcases.Push(Map('data', "19/2/05", 'expected', "20050219"))
31+
testcases.Push(Map('data', "10/12/2007", 'expected', "20071210"))
32+
testcases.Push(Map('data', "3/15/2009", 'expected', "20090315"))
33+
testcases.Push(Map('data', "05-Jan-00", 'expected', "20000105"))
34+
testcases.Push(Map('data', "Jan-06-00", 'expected', "20000106"))
35+
testcases.Push(Map('data', "Dec-31-13", 'expected', "20131231"))
36+
testcases.Push(Map('data', "Wed 6/27/2007", 'expected', "20070627"))
37+
testcases.Push(Map('data', "May1960", 'expected', "19600501"))
38+
testcases.Push(Map('data', "25May1960", 'expected', "19600525"))
39+
testcases.Push(Map('data', "201710", 'expected', "20171001"))
40+
41+
; time only examples -> todays date should be added automatically in output
42+
today := FormatTime(A_Now, "yyyyMMdd")
43+
testcases.Push(Map('data', "1532", 'expected', today . "1532"))
44+
testcases.Push(Map('data', "11:26", 'expected', today . "1126"))
45+
testcases.Push(Map('data', "2:35 PM", 'expected', today . "1435"))
46+
testcases.Push(Map('data', "11:22:33 AM", 'expected', today . "112233"))
47+
48+
For Index, Value in testcases {
49+
dt := DateParse(value["data"])
50+
str := Format("{:02.0f}", Index)
51+
if (dt != value["expected"]) {
52+
str:= str . " - ** FAILURE ** "
53+
} else {
54+
str:= str . " - SUCCESS "
55+
}
56+
str := str . " - Input: " Format("{:-30}",value["data"]) " * Expected: " Format("{:-15}",value["expected"]) " * Got: " Format("{:-15}",dt)
57+
OutputDebug str "`n"
58+
}
59+
ExitApp()

0 commit comments

Comments
 (0)