parse strict refs #28

This commit is contained in:
Aaron Raddon 2018-03-24 17:49:27 -07:00
parent 934b3f146d
commit fd8f11d211
5 changed files with 169 additions and 70 deletions

View File

@ -19,6 +19,10 @@ Parse many date strings without knowing format in advance. Uses a scanner to re
// Normal parse. Equivalent Timezone rules as time.Parse()
t, err := dateparse.ParseAny("3/1/2014")
// Parse Strict, error on ambigous mm/dd vs dd/mm dates
t, err := dateparse.ParseStrict("3/1/2014")
> returns error
// Parse with Location, equivalent to time.ParseInLocation() timezone/offset
// rules. Using location arg, if timezone/offset info exists in the
// datestring, it uses the given location rules for any zone interpretation.

View File

@ -13,19 +13,27 @@ $ dateparse --timezone="America/Denver" "2017-07-19 03:21:00"
Your Current time.Local zone is PDT
+------------+---------------------------+-------------------------------+-------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+------------+---------------------------+-------------------------------+-------------------------------+
| ParseAny | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseAny | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 03:21:00 -0600 MDT | 2017-07-19 09:21:00 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 03:21:00 -0600 MDT | 2017-07-19 09:21:00 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
+------------+---------------------------+-------------------------------+-------------------------------+
Layout String: dateparse.ParseFormat() => 2006-01-02 15:04:05
Your Using time.Local set to location=America/Denver MDT
+-------------+---------------------------+-------------------------------+-------------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+-------------+---------------------------+-------------------------------+-------------------------------------+
| ParseAny | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseAny | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseIn | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 03:21:00 -0600 MDT | 2017-07-19 09:21:00 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 03:21:00 -0600 MDT | 2017-07-19 09:21:00 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
+-------------+---------------------------+-------------------------------+-------------------------------------+
# Note on this one that the outputed zone is always UTC/0 offset as opposed to above
@ -33,38 +41,53 @@ $ dateparse --timezone="America/Denver" "2017-07-19 03:21:51+00:00"
Your Current time.Local zone is PDT
+------------+---------------------------+---------------------------------+-------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+------------+---------------------------+---------------------------------+-------------------------------+
| ParseAny | time.Local = nil | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseAny | time.Local = timezone arg | 2017-07-19 03:21:51 +0000 +0000 | 2017-07-19 03:21:51 +0000 UTC |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseIn | time.Local = nil | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 03:21:51 +0000 +0000 | 2017-07-19 03:21:51 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 03:21:51 +0000 +0000 | 2017-07-19 03:21:51 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
+------------+---------------------------+---------------------------------+-------------------------------+
Layout String: dateparse.ParseFormat() => 2006-01-02 15:04:05-07:00
Your Using time.Local set to location=America/Denver MDT
+-------------+---------------------------+---------------------------------+-------------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+-------------+---------------------------+---------------------------------+-------------------------------------+
| ParseAny | time.Local = nil | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC day=3 |
| ParseAny | time.Local = timezone arg | 2017-07-19 03:21:51 +0000 +0000 | 2017-07-19 03:21:51 +0000 UTC day=3 |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC day=3 |
| ParseIn | time.Local = nil | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 03:21:51 +0000 +0000 | 2017-07-19 03:21:51 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 03:21:51 +0000 +0000 | 2017-07-19 03:21:51 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseStrict | time.Local = nil | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
| ParseStrict | time.Local = timezone arg | 2017-07-19 03:21:51 +0000 +0000 | 2017-07-19 03:21:51 +0000 UTC |
| ParseStrict | time.Local = time.UTC | 2017-07-19 03:21:51 +0000 UTC | 2017-07-19 03:21:51 +0000 UTC |
+-------------+---------------------------+---------------------------------+-------------------------------------+
$ dateparse --timezone="America/Denver" "Monday, 19-Jul-17 03:21:00 MDT"
Your Current time.Local zone is PDT
+------------+---------------------------+-------------------------------+-------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+------------+---------------------------+-------------------------------+-------------------------------+
| ParseAny | time.Local = nil | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseAny | time.Local = timezone arg | 2017-07-19 03:21:00 -0600 MDT | 2017-07-19 09:21:00 +0000 UTC |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = nil | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 03:21:00 -0600 MDT | 2017-07-19 09:21:00 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 03:21:00 -0600 MDT | 2017-07-19 09:21:00 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
+------------+---------------------------+-------------------------------+-------------------------------+
Layout String: dateparse.ParseFormat() => 02-Jan-06 15:04:05 MDT
Your Using time.Local set to location=America/Denver MDT
+-------------+---------------------------+-------------------------------+-------------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+-------------+---------------------------+-------------------------------+-------------------------------------+
| ParseStrict | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseAny | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseAny | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseIn | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 03:21:00 -0600 MDT | 2017-07-19 09:21:00 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 03:21:00 -0600 MDT | 2017-07-19 09:21:00 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
+-------------+---------------------------+-------------------------------+-------------------------------------+
# pass in a wrong timezone "MST" (should be MDT)
@ -72,19 +95,27 @@ $ dateparse --timezone="America/Denver" "Monday, 19-Jul-17 03:21:00 MST"
Your Current time.Local zone is PDT
+------------+---------------------------+-------------------------------+-------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+------------+---------------------------+-------------------------------+-------------------------------+
| ParseAny | time.Local = nil | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseAny | time.Local = timezone arg | 2017-07-19 04:21:00 -0600 MDT | 2017-07-19 10:21:00 +0000 UTC |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = nil | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 04:21:00 -0600 MDT | 2017-07-19 10:21:00 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 04:21:00 -0600 MDT | 2017-07-19 10:21:00 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
+------------+---------------------------+-------------------------------+-------------------------------+
Layout String: dateparse.ParseFormat() => 02-Jan-06 15:04:05 MST
Your Using time.Local set to location=America/Denver MDT
+-------------+---------------------------+-------------------------------+-------------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+-------------+---------------------------+-------------------------------+-------------------------------------+
| ParseAny | time.Local = nil | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseAny | time.Local = timezone arg | 2017-07-19 04:21:00 -0600 MDT | 2017-07-19 10:21:00 +0000 UTC day=3 |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseIn | time.Local = nil | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 04:21:00 -0600 MDT | 2017-07-19 10:21:00 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 04:21:00 -0600 MDT | 2017-07-19 10:21:00 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = nil | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = timezone arg | 2017-07-19 04:21:00 -0600 MDT | 2017-07-19 10:21:00 +0000 UTC |
| ParseStrict | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MST | 2017-07-19 03:21:00 +0000 UTC |
+-------------+---------------------------+-------------------------------+-------------------------------------+
# note, we are using America/New_York which doesn't recognize MDT so essentially ignores it
@ -92,19 +123,27 @@ $ dateparse --timezone="America/New_York" "Monday, 19-Jul-17 03:21:00 MDT"
Your Current time.Local zone is PDT
+------------+---------------------------+-------------------------------+-------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+------------+---------------------------+-------------------------------+-------------------------------+
| ParseAny | time.Local = nil | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseAny | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = nil | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 MDT | 2017-07-19 03:21:00 +0000 UTC |
+------------+---------------------------+-------------------------------+-------------------------------+
Layout String: dateparse.ParseFormat() => 02-Jan-06 15:04:05 MDT
Your Using time.Local set to location=America/New_York EDT
+-------------+---------------------------+-------------------------------+-------------------------------------+
| method | Zone Source | Parsed | Parsed: t.In(time.UTC) |
+-------------+---------------------------+-------------------------------+-------------------------------------+
| ParseAny | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseAny | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseAny | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC day=3 |
| ParseIn | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseIn | time.Local = timezone arg | 2017-07-19 03:21:00 -0400 EDT | 2017-07-19 07:21:00 +0000 UTC |
| ParseIn | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseLocal | time.Local = timezone arg | 2017-07-19 03:21:00 -0400 EDT | 2017-07-19 07:21:00 +0000 UTC |
| ParseLocal | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = nil | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = timezone arg | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
| ParseStrict | time.Local = time.UTC | 2017-07-19 03:21:00 +0000 UTC | 2017-07-19 03:21:00 +0000 UTC |
+-------------+---------------------------+-------------------------------+-------------------------------------+
```

View File

@ -48,7 +48,7 @@ func main() {
}
loc = l
zonename, _ := time.Now().In(l).Zone()
fmt.Printf("Your Using time.Local set to location=%s %v \n", timezone, zonename)
fmt.Printf("\nYour Using time.Local set to location=%s %v \n", timezone, zonename)
}
fmt.Printf("\n")
@ -57,9 +57,10 @@ func main() {
table.AddHeaders("method", "Zone Source", "Parsed", "Parsed: t.In(time.UTC)")
parsers := map[string]parser{
"ParseAny": parseAny,
"ParseIn": parseIn,
"ParseLocal": parseLocal,
"ParseAny": parseAny,
"ParseIn": parseIn,
"ParseLocal": parseLocal,
"ParseStrict": parseStrict,
}
for name, parser := range parsers {
@ -115,3 +116,14 @@ func parseAny(datestr string, loc *time.Location, utc bool) string {
}
return t.String()
}
func parseStrict(datestr string, loc *time.Location, utc bool) string {
t, err := dateparse.ParseStrict(datestr)
if err != nil {
return err.Error()
}
if utc {
return t.In(time.UTC).String()
}
return t.String()
}

View File

@ -84,7 +84,8 @@ const (
)
var (
shortDates = []string{"01/02/2006", "1/2/2006", "06/01/02", "01/02/06", "1/2/06"}
//shortDates = []string{"01/02/2006", "1/2/2006", "06/01/02", "01/02/06", "1/2/06"}
ErrAmbiguousMMDD = fmt.Errorf("This date has ambiguous mm/dd vs dd/mm type format")
)
// ParseAny parse an unknown date format, detect the layout, parse.
@ -160,6 +161,20 @@ func ParseFormat(datestr string) (string, error) {
return string(p.format), nil
}
// ParseStrict parse an unknown date format. IF the date is ambigous
// mm/dd vs dd/mm then return an error.
// These return errors: 3.3.2014 , 8/8/71 etc
func ParseStrict(datestr string) (time.Time, error) {
p, err := parseTime(datestr, nil)
if err != nil {
return time.Time{}, err
}
if p.ambiguousMD {
return time.Time{}, ErrAmbiguousMMDD
}
return p.parse()
}
type parser struct {
loc *time.Location
preferMonthFirst bool

View File

@ -547,3 +547,32 @@ func TestParseLayout(t *testing.T) {
_, err = ParseFormat("2009-15-12T22:15Z")
assert.NotEqual(t, nil, err)
}
var testParseStrict = []dateTest{
// mm.dd.yyyy
{in: "3.3.2014"},
// mm.dd.yy
{in: "08.09.71"},
// mm/dd/yyyy
{in: "3/5/2014"},
// mm/dd/yy
{in: "08/08/71"},
{in: "8/8/71"},
// mm/dd/yy hh:mm:ss
{in: "04/02/2014 04:08:09"},
{in: "4/2/2014 04:08:09"},
}
func TestParseStrict(t *testing.T) {
for _, th := range testParseStrict {
_, err := ParseStrict(th.in)
assert.NotEqual(t, nil, err)
}
_, err := ParseStrict(`{"hello"}`)
assert.NotEqual(t, nil, err)
_, err = ParseStrict("2009-08-12T22:15Z")
assert.Equal(t, nil, err)
}