diff --git a/changelog/unreleased/kql-search-query-language.md b/changelog/unreleased/kql-search-query-language.md index 0749e09df..f1724114c 100644 --- a/changelog/unreleased/kql-search-query-language.md +++ b/changelog/unreleased/kql-search-query-language.md @@ -2,16 +2,31 @@ Enhancement: Keyword Query Language (KQL) search syntax We've introduced support for [KQL](https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference) as the default oCIS search query language. -Some examples of a valid KQL query are: +Simple queries: -* `Tag`: `tag:golden tag:"silver"` -* `Filename`: `name:file.txt name:"file.docx"` -* `Content`: `content:ahab content:"captain aha*"` +* `tag:golden tag:"silver"` +* `name:file.txt name:"file.docx"` +* `content:ahab content:"captain aha*"` + +Date/-range queries + +* `Mtime:"2023-09-05T08:42:11.23554+02:00"` +* `Mtime>"2023-09-05T08:42:11.23554+02:00"` +* `Mtime>="2023-09-05T08:42:11.23554+02:00"` +* `Mtime<"2023-09-05T08:42:11.23554+02:00"` +* `Mtime<="2023-09-05T08:42:11.23554+02:00"` +* `Mtime:today` - range: start of today till end of today +* `Mtime:yesterday` - range: start of yesterday till end of yesterday +* `Mtime:"this week"` - range: start of this week till end of this week +* `Mtime:"this month"` - range: start of this month till end of this month +* `Mtime:"last month"` - range: start of last month till end of last month +* `Mtime:"this year"` - range: start of this year till end of this year +* `Mtime:"last year"` - range: start of last year till end of last year Conjunctive normal form queries: -* `Boolean`: `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver` -* `Group`: `(tag:book content:ahab*)`, `tag:(book pdf)` +* `tag:golden AND tag:"silver`, `tag:golden OR tag:"silver`, `tag:golden NOT tag:"silver` +* `(tag:book content:ahab*)`, `tag:(book pdf)` Complex queries: @@ -22,6 +37,7 @@ https://github.com/owncloud/ocis/pull/7043 https://github.com/owncloud/ocis/pull/7247 https://github.com/owncloud/ocis/pull/7248 https://github.com/owncloud/ocis/pull/7254 +https://github.com/owncloud/ocis/pull/7262 https://github.com/owncloud/web/pull/9653 https://github.com/owncloud/web/pull/9672 https://github.com/owncloud/ocis/issues/7042 diff --git a/go.mod b/go.mod index a64f307dd..20ceae213 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/Masterminds/semver v1.5.0 github.com/MicahParks/keyfunc v1.9.0 github.com/Nerzal/gocloak/v13 v13.8.0 - github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bbalet/stopwords v1.0.0 github.com/blevesearch/bleve/v2 v2.3.9 github.com/coreos/go-oidc v2.2.1+incompatible @@ -50,6 +49,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 github.com/jellydator/ttlcache/v2 v2.11.1 github.com/jellydator/ttlcache/v3 v3.1.0 + github.com/jinzhu/now v1.1.5 github.com/justinas/alice v1.2.0 github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 github.com/libregraph/idm v0.4.1-0.20230221143410-3503963047a5 diff --git a/go.sum b/go.sum index fbeb6004b..9af77a03b 100644 --- a/go.sum +++ b/go.sum @@ -859,8 +859,6 @@ github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4x github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= -github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -1525,6 +1523,8 @@ github.com/jellydator/ttlcache/v3 v3.1.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVK github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -1642,7 +1642,6 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -1866,7 +1865,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/riandyrn/otelchi v0.5.1 h1:0/45omeqpP7f/cvdL16GddQBfAEmZvUyl2QzLSE6uYo= github.com/riandyrn/otelchi v0.5.1/go.mod h1:ZxVxNEl+jQ9uHseRYIxKWRb3OY8YXFEu+EkNiiSNUEA= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -1898,7 +1896,6 @@ github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/sciencemesh/meshdirectory-web v1.0.4 h1:1YSctF6PAXhoHUYCaeRTj7rHaF7b3rYrZf2R0VXBIbo= github.com/sciencemesh/meshdirectory-web v1.0.4/go.mod h1:fJSThTS3xf+sTdL0iXQoaQJssLI7tn7DetHMHUl4SRk= -github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= diff --git a/services/search/pkg/query/kql/cast.go b/services/search/pkg/query/kql/cast.go index d9710d35c..e3b9ab581 100644 --- a/services/search/pkg/query/kql/cast.go +++ b/services/search/pkg/query/kql/cast.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/araddon/dateparse" + "github.com/jinzhu/now" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -21,21 +21,23 @@ func toNode[T ast.Node](in interface{}) (T, error) { func toNodes[T ast.Node](in interface{}) ([]T, error) { switch v := in.(type) { - case []T: - return v, nil case T: return []T{v}, nil + case []T: + return v, nil + case []*ast.OperatorNode, []*ast.DateTimeNode: + return toNodes[T](v) case []interface{}: - var ts []T - for _, inter := range v { - n, err := toNodes[T](inter) + var nodes []T + for _, el := range v { + node, err := toNodes[T](el) if err != nil { return nil, err } - ts = append(ts, n...) + nodes = append(nodes, node...) } - return ts, nil + return nodes, nil case nil: return nil, nil default: @@ -74,5 +76,53 @@ func toTime(in interface{}) (time.Time, error) { return time.Time{}, err } - return dateparse.ParseLocal(ts) + return now.Parse(ts) +} + +func toTimeRange(in interface{}) (*time.Time, *time.Time, error) { + var from, to time.Time + + value, err := toString(in) + if err != nil { + return &from, &to, UnsupportedTimeRangeError{} + } + + c := &now.Config{ + WeekStartDay: time.Monday, + } + + n := c.With(timeNow()) + + switch value { + case "today": + from = n.BeginningOfDay() + to = n.EndOfDay() + case "yesterday": + yesterday := n.With(n.AddDate(0, 0, -1)) + from = yesterday.BeginningOfDay() + to = yesterday.EndOfDay() + case "this week": + from = n.BeginningOfWeek() + to = n.EndOfWeek() + case "this month": + from = n.BeginningOfMonth() + to = n.EndOfMonth() + case "last month": + lastMonth := n.With(n.AddDate(0, -1, 0)) + from = lastMonth.BeginningOfMonth() + to = lastMonth.EndOfMonth() + case "this year": + from = n.BeginningOfYear() + to = n.EndOfYear() + case "last year": + lastYear := n.With(n.AddDate(-1, 0, 0)) + from = lastYear.BeginningOfYear() + to = lastYear.EndOfYear() + } + + if from.IsZero() || to.IsZero() { + return nil, nil, UnsupportedTimeRangeError{} + } + + return &from, &to, nil } diff --git a/services/search/pkg/query/kql/dictionary.peg b/services/search/pkg/query/kql/dictionary.peg index ca9658dfa..426e4d509 100644 --- a/services/search/pkg/query/kql/dictionary.peg +++ b/services/search/pkg/query/kql/dictionary.peg @@ -61,6 +61,12 @@ DateTimeRestrictionNode <- FullTime ) '"'? { return buildDateTimeNode(k, o, v, c.text, c.pos) + } / + k:Char+ ( + OperatorEqualNode / + OperatorColonNode + ) '"'? v:NaturalLanguageDateTime '"'? { + return buildNaturalLanguageDateTimeNodes(k, v, c.text, c.pos) } TextPropertyRestrictionNode <- @@ -185,11 +191,22 @@ FullTime <- return c.text, nil } -DateTime - = FullDate "T" FullTime { +DateTime <- + FullDate "T" FullTime { return c.text, nil } +NaturalLanguageDateTime <- + "today" / + "yesterday" / + "this week" / + "this month" / + "last month" / + "this year" / + "last year" { + return c.text, nil + } + //////////////////////////////////////////////////////// // misc //////////////////////////////////////////////////////// diff --git a/services/search/pkg/query/kql/dictionary_gen.go b/services/search/pkg/query/kql/dictionary_gen.go index afce6c5c8..4bcd4f83b 100644 --- a/services/search/pkg/query/kql/dictionary_gen.go +++ b/services/search/pkg/query/kql/dictionary_gen.go @@ -43,12 +43,12 @@ var g = &grammar{ pos: position{line: 19, col: 6, offset: 351}, exprs: []any{ &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, run: (*parser).callonNodes3, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -86,10 +86,10 @@ var g = &grammar{ expr: &oneOrMoreExpr{ pos: position{line: 46, col: 7, offset: 1044}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonNode7, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -102,20 +102,20 @@ var g = &grammar{ pos: position{line: 46, col: 14, offset: 1051}, alternatives: []any{ &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, run: (*parser).callonNode10, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonNode12, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", @@ -159,10 +159,10 @@ var g = &grammar{ expr: &oneOrMoreExpr{ pos: position{line: 51, col: 7, offset: 1205}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonNode22, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -178,60 +178,60 @@ var g = &grammar{ pos: position{line: 52, col: 9, offset: 1223}, alternatives: []any{ &actionExpr{ - pos: position{line: 139, col: 5, offset: 3168}, + pos: position{line: 145, col: 5, offset: 3358}, run: (*parser).callonNode26, expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3168}, + pos: position{line: 145, col: 5, offset: 3358}, val: ">=", ignoreCase: false, want: "\">=\"", }, }, &actionExpr{ - pos: position{line: 129, col: 5, offset: 2984}, + pos: position{line: 135, col: 5, offset: 3174}, run: (*parser).callonNode28, expr: &litMatcher{ - pos: position{line: 129, col: 5, offset: 2984}, + pos: position{line: 135, col: 5, offset: 3174}, val: "<=", ignoreCase: false, want: "\"<=\"", }, }, &actionExpr{ - pos: position{line: 134, col: 5, offset: 3073}, + pos: position{line: 140, col: 5, offset: 3263}, run: (*parser).callonNode30, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3073}, + pos: position{line: 140, col: 5, offset: 3263}, val: ">", ignoreCase: false, want: "\">\"", }, }, &actionExpr{ - pos: position{line: 124, col: 5, offset: 2892}, + pos: position{line: 130, col: 5, offset: 3082}, run: (*parser).callonNode32, expr: &litMatcher{ - pos: position{line: 124, col: 5, offset: 2892}, + pos: position{line: 130, col: 5, offset: 3082}, val: "<", ignoreCase: false, want: "\"<\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonNode34, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", }, }, &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, run: (*parser).callonNode36, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -256,28 +256,28 @@ var g = &grammar{ pos: position{line: 59, col: 9, offset: 1420}, alternatives: []any{ &actionExpr{ - pos: position{line: 189, col: 5, offset: 4003}, + pos: position{line: 195, col: 5, offset: 4197}, run: (*parser).callonNode42, expr: &seqExpr{ - pos: position{line: 189, col: 5, offset: 4003}, + pos: position{line: 195, col: 5, offset: 4197}, exprs: []any{ &actionExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, run: (*parser).callonNode44, expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, exprs: []any{ &actionExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, run: (*parser).callonNode46, expr: &seqExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode48, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -285,10 +285,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode50, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -296,10 +296,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode52, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -307,10 +307,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode54, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -321,22 +321,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 14, offset: 3779}, + pos: position{line: 185, col: 14, offset: 3969}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, run: (*parser).callonNode57, expr: &seqExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode59, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -344,10 +344,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode61, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -358,22 +358,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 28, offset: 3793}, + pos: position{line: 185, col: 28, offset: 3983}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, run: (*parser).callonNode64, expr: &seqExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode66, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -381,10 +381,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode68, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -398,28 +398,28 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 189, col: 14, offset: 4012}, + pos: position{line: 195, col: 14, offset: 4206}, val: "T", ignoreCase: false, want: "\"T\"", }, &actionExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, run: (*parser).callonNode71, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, exprs: []any{ &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode73, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode75, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -427,10 +427,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode77, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -441,22 +441,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 14, offset: 3866}, + pos: position{line: 190, col: 14, offset: 4056}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode80, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode82, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -464,10 +464,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode84, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -478,22 +478,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 29, offset: 3881}, + pos: position{line: 190, col: 29, offset: 4071}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, run: (*parser).callonNode87, expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode89, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -501,10 +501,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode91, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -515,23 +515,23 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 184, col: 44, offset: 3896}, + pos: position{line: 190, col: 44, offset: 4086}, expr: &seqExpr{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, exprs: []any{ &litMatcher{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 184, col: 49, offset: 3901}, + pos: position{line: 190, col: 49, offset: 4091}, expr: &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode97, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -543,35 +543,35 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, alternatives: []any{ &litMatcher{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 184, col: 65, offset: 3917}, + pos: position{line: 190, col: 65, offset: 4107}, exprs: []any{ &charClassMatcher{ - pos: position{line: 184, col: 66, offset: 3918}, + pos: position{line: 190, col: 66, offset: 4108}, val: "[+-]", chars: []rune{'+', '-'}, ignoreCase: false, inverted: false, }, &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode103, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode105, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -579,10 +579,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode107, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -593,22 +593,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 86, offset: 3938}, + pos: position{line: 190, col: 86, offset: 4128}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode110, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode112, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -616,10 +616,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode114, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -640,22 +640,22 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, run: (*parser).callonNode116, expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 3770}, + pos: position{line: 185, col: 5, offset: 3960}, exprs: []any{ &actionExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, run: (*parser).callonNode118, expr: &seqExpr{ - pos: position{line: 149, col: 5, offset: 3370}, + pos: position{line: 155, col: 5, offset: 3560}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode120, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -663,10 +663,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode122, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -674,10 +674,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode124, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -685,10 +685,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode126, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -699,22 +699,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 14, offset: 3779}, + pos: position{line: 185, col: 14, offset: 3969}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, run: (*parser).callonNode129, expr: &seqExpr{ - pos: position{line: 154, col: 5, offset: 3447}, + pos: position{line: 160, col: 5, offset: 3637}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode131, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -722,10 +722,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode133, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -736,22 +736,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 179, col: 28, offset: 3793}, + pos: position{line: 185, col: 28, offset: 3983}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, run: (*parser).callonNode136, expr: &seqExpr{ - pos: position{line: 159, col: 5, offset: 3510}, + pos: position{line: 165, col: 5, offset: 3700}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode138, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -759,10 +759,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode140, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -776,22 +776,22 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, run: (*parser).callonNode142, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 3857}, + pos: position{line: 190, col: 5, offset: 4047}, exprs: []any{ &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode144, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode146, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -799,10 +799,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode148, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -813,22 +813,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 14, offset: 3866}, + pos: position{line: 190, col: 14, offset: 4056}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode151, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode153, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -836,10 +836,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode155, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -850,22 +850,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 29, offset: 3881}, + pos: position{line: 190, col: 29, offset: 4071}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, run: (*parser).callonNode158, expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 3706}, + pos: position{line: 180, col: 5, offset: 3896}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode160, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -873,10 +873,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode162, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -887,23 +887,23 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 184, col: 44, offset: 3896}, + pos: position{line: 190, col: 44, offset: 4086}, expr: &seqExpr{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, exprs: []any{ &litMatcher{ - pos: position{line: 184, col: 45, offset: 3897}, + pos: position{line: 190, col: 45, offset: 4087}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 184, col: 49, offset: 3901}, + pos: position{line: 190, col: 49, offset: 4091}, expr: &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode168, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -915,35 +915,35 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, alternatives: []any{ &litMatcher{ - pos: position{line: 184, col: 59, offset: 3911}, + pos: position{line: 190, col: 59, offset: 4101}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 184, col: 65, offset: 3917}, + pos: position{line: 190, col: 65, offset: 4107}, exprs: []any{ &charClassMatcher{ - pos: position{line: 184, col: 66, offset: 3918}, + pos: position{line: 190, col: 66, offset: 4108}, val: "[+-]", chars: []rune{'+', '-'}, ignoreCase: false, inverted: false, }, &actionExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, run: (*parser).callonNode174, expr: &seqExpr{ - pos: position{line: 164, col: 5, offset: 3574}, + pos: position{line: 170, col: 5, offset: 3764}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode176, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -951,10 +951,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode178, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -965,22 +965,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 184, col: 86, offset: 3938}, + pos: position{line: 190, col: 86, offset: 4128}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, run: (*parser).callonNode181, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3640}, + pos: position{line: 175, col: 5, offset: 3830}, exprs: []any{ &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode183, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -988,10 +988,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, run: (*parser).callonNode185, expr: &charClassMatcher{ - pos: position{line: 208, col: 5, offset: 4313}, + pos: position{line: 225, col: 5, offset: 4694}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1024,21 +1024,21 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 67, col: 5, offset: 1579}, + pos: position{line: 65, col: 5, offset: 1549}, run: (*parser).callonNode189, expr: &seqExpr{ - pos: position{line: 67, col: 5, offset: 1579}, + pos: position{line: 65, col: 5, offset: 1549}, exprs: []any{ &labeledExpr{ - pos: position{line: 67, col: 5, offset: 1579}, + pos: position{line: 65, col: 5, offset: 1549}, label: "k", expr: &oneOrMoreExpr{ - pos: position{line: 67, col: 7, offset: 1581}, + pos: position{line: 65, col: 7, offset: 1551}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonNode193, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1048,23 +1048,148 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 67, col: 14, offset: 1588}, + pos: position{line: 66, col: 9, offset: 1567}, alternatives: []any{ &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonNode196, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 125, col: 5, offset: 2997}, + val: "=", + ignoreCase: false, + want: "\"=\"", + }, + }, + &actionExpr{ + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode198, + expr: &litMatcher{ + pos: position{line: 120, col: 5, offset: 2911}, + val: ":", + ignoreCase: false, + want: "\":\"", + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 68, col: 7, offset: 1619}, + expr: &litMatcher{ + pos: position{line: 68, col: 7, offset: 1619}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + &labeledExpr{ + pos: position{line: 68, col: 12, offset: 1624}, + label: "v", + expr: &choiceExpr{ + pos: position{line: 200, col: 5, offset: 4285}, + alternatives: []any{ + &litMatcher{ + pos: position{line: 200, col: 5, offset: 4285}, + val: "today", + ignoreCase: false, + want: "\"today\"", + }, + &litMatcher{ + pos: position{line: 201, col: 5, offset: 4299}, + val: "yesterday", + ignoreCase: false, + want: "\"yesterday\"", + }, + &litMatcher{ + pos: position{line: 202, col: 5, offset: 4317}, + val: "this week", + ignoreCase: false, + want: "\"this week\"", + }, + &litMatcher{ + pos: position{line: 203, col: 5, offset: 4335}, + val: "this month", + ignoreCase: false, + want: "\"this month\"", + }, + &litMatcher{ + pos: position{line: 204, col: 5, offset: 4354}, + val: "last month", + ignoreCase: false, + want: "\"last month\"", + }, + &litMatcher{ + pos: position{line: 205, col: 5, offset: 4373}, + val: "this year", + ignoreCase: false, + want: "\"this year\"", + }, + &actionExpr{ + pos: position{line: 206, col: 5, offset: 4391}, + run: (*parser).callonNode210, + expr: &litMatcher{ + pos: position{line: 206, col: 5, offset: 4391}, + val: "last year", + ignoreCase: false, + want: "\"last year\"", + }, + }, + }, + }, + }, + &zeroOrOneExpr{ + pos: position{line: 68, col: 38, offset: 1650}, + expr: &litMatcher{ + pos: position{line: 68, col: 38, offset: 1650}, + val: "\"", + ignoreCase: false, + want: "\"\\\"\"", + }, + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 73, col: 5, offset: 1769}, + run: (*parser).callonNode214, + expr: &seqExpr{ + pos: position{line: 73, col: 5, offset: 1769}, + exprs: []any{ + &labeledExpr{ + pos: position{line: 73, col: 5, offset: 1769}, + label: "k", + expr: &oneOrMoreExpr{ + pos: position{line: 73, col: 7, offset: 1771}, + expr: &actionExpr{ + pos: position{line: 215, col: 5, offset: 4575}, + run: (*parser).callonNode218, + expr: &charClassMatcher{ + pos: position{line: 215, col: 5, offset: 4575}, + val: "[A-Za-z]", + ranges: []rune{'A', 'Z', 'a', 'z'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + &choiceExpr{ + pos: position{line: 73, col: 14, offset: 1778}, + alternatives: []any{ + &actionExpr{ + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode221, + expr: &litMatcher{ + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, - run: (*parser).callonNode198, + pos: position{line: 125, col: 5, offset: 2997}, + run: (*parser).callonNode223, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", @@ -1073,30 +1198,30 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 67, col: 53, offset: 1627}, + pos: position{line: 73, col: 53, offset: 1817}, label: "v", expr: &choiceExpr{ - pos: position{line: 67, col: 56, offset: 1630}, + pos: position{line: 73, col: 56, offset: 1820}, alternatives: []any{ &actionExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - run: (*parser).callonNode202, + pos: position{line: 220, col: 5, offset: 4634}, + run: (*parser).callonNode227, expr: &seqExpr{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, exprs: []any{ &litMatcher{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 203, col: 9, offset: 4257}, + pos: position{line: 220, col: 9, offset: 4638}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, expr: &charClassMatcher{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1105,7 +1230,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 203, col: 17, offset: 4265}, + pos: position{line: 220, col: 17, offset: 4646}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1114,9 +1239,9 @@ var g = &grammar{ }, }, &oneOrMoreExpr{ - pos: position{line: 67, col: 65, offset: 1639}, + pos: position{line: 73, col: 65, offset: 1829}, expr: &charClassMatcher{ - pos: position{line: 67, col: 65, offset: 1639}, + pos: position{line: 73, col: 65, offset: 1829}, val: "[^ ()]", chars: []rune{' ', '(', ')'}, ignoreCase: false, @@ -1130,19 +1255,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 99, col: 5, offset: 2431}, - run: (*parser).callonNode211, + pos: position{line: 105, col: 5, offset: 2621}, + run: (*parser).callonNode236, expr: &choiceExpr{ - pos: position{line: 99, col: 6, offset: 2432}, + pos: position{line: 105, col: 6, offset: 2622}, alternatives: []any{ &litMatcher{ - pos: position{line: 99, col: 6, offset: 2432}, + pos: position{line: 105, col: 6, offset: 2622}, val: "AND", ignoreCase: false, want: "\"AND\"", }, &litMatcher{ - pos: position{line: 99, col: 14, offset: 2440}, + pos: position{line: 105, col: 14, offset: 2630}, val: "+", ignoreCase: false, want: "\"+\"", @@ -1151,19 +1276,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 104, col: 5, offset: 2532}, - run: (*parser).callonNode215, + pos: position{line: 110, col: 5, offset: 2722}, + run: (*parser).callonNode240, expr: &choiceExpr{ - pos: position{line: 104, col: 6, offset: 2533}, + pos: position{line: 110, col: 6, offset: 2723}, alternatives: []any{ &litMatcher{ - pos: position{line: 104, col: 6, offset: 2533}, + pos: position{line: 110, col: 6, offset: 2723}, val: "NOT", ignoreCase: false, want: "\"NOT\"", }, &litMatcher{ - pos: position{line: 104, col: 14, offset: 2541}, + pos: position{line: 110, col: 14, offset: 2731}, val: "-", ignoreCase: false, want: "\"-\"", @@ -1172,28 +1297,28 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 109, col: 5, offset: 2632}, - run: (*parser).callonNode219, + pos: position{line: 115, col: 5, offset: 2822}, + run: (*parser).callonNode244, expr: &litMatcher{ - pos: position{line: 109, col: 6, offset: 2633}, + pos: position{line: 115, col: 6, offset: 2823}, val: "OR", ignoreCase: false, want: "\"OR\"", }, }, &actionExpr{ - pos: position{line: 80, col: 6, offset: 1919}, - run: (*parser).callonNode221, + pos: position{line: 86, col: 6, offset: 2109}, + run: (*parser).callonNode246, expr: &seqExpr{ - pos: position{line: 80, col: 6, offset: 1919}, + pos: position{line: 86, col: 6, offset: 2109}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 80, col: 6, offset: 1919}, + pos: position{line: 86, col: 6, offset: 2109}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode224, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode249, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1201,12 +1326,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode226, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode251, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1215,27 +1340,27 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 80, col: 27, offset: 1940}, + pos: position{line: 86, col: 27, offset: 2130}, label: "v", expr: &actionExpr{ - pos: position{line: 203, col: 5, offset: 4253}, - run: (*parser).callonNode230, + pos: position{line: 220, col: 5, offset: 4634}, + run: (*parser).callonNode255, expr: &seqExpr{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, exprs: []any{ &litMatcher{ - pos: position{line: 203, col: 5, offset: 4253}, + pos: position{line: 220, col: 5, offset: 4634}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 203, col: 9, offset: 4257}, + pos: position{line: 220, col: 9, offset: 4638}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, expr: &charClassMatcher{ - pos: position{line: 203, col: 11, offset: 4259}, + pos: position{line: 220, col: 11, offset: 4640}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1244,7 +1369,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 203, col: 17, offset: 4265}, + pos: position{line: 220, col: 17, offset: 4646}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1254,12 +1379,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode237, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode262, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1268,12 +1393,12 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 80, col: 38, offset: 1951}, + pos: position{line: 86, col: 38, offset: 2141}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode241, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode266, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1284,18 +1409,18 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 85, col: 6, offset: 2049}, - run: (*parser).callonNode243, + pos: position{line: 91, col: 6, offset: 2239}, + run: (*parser).callonNode268, expr: &seqExpr{ - pos: position{line: 85, col: 6, offset: 2049}, + pos: position{line: 91, col: 6, offset: 2239}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 85, col: 6, offset: 2049}, + pos: position{line: 91, col: 6, offset: 2239}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode246, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode271, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1303,12 +1428,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode248, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode273, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1317,12 +1442,12 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 85, col: 27, offset: 2070}, + pos: position{line: 91, col: 27, offset: 2260}, label: "v", expr: &oneOrMoreExpr{ - pos: position{line: 85, col: 29, offset: 2072}, + pos: position{line: 91, col: 29, offset: 2262}, expr: &charClassMatcher{ - pos: position{line: 85, col: 29, offset: 2072}, + pos: position{line: 91, col: 29, offset: 2262}, val: "[^ :()]", chars: []rune{' ', ':', '(', ')'}, ignoreCase: false, @@ -1331,12 +1456,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 213, col: 5, offset: 4364}, - run: (*parser).callonNode254, + pos: position{line: 230, col: 5, offset: 4745}, + run: (*parser).callonNode279, expr: &zeroOrMoreExpr{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, expr: &charClassMatcher{ - pos: position{line: 213, col: 5, offset: 4364}, + pos: position{line: 230, col: 5, offset: 4745}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1345,12 +1470,12 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 85, col: 40, offset: 2083}, + pos: position{line: 91, col: 40, offset: 2273}, expr: &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, - run: (*parser).callonNode258, + pos: position{line: 120, col: 5, offset: 2911}, + run: (*parser).callonNode283, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", @@ -1380,10 +1505,10 @@ var g = &grammar{ expr: &oneOrMoreExpr{ pos: position{line: 32, col: 8, offset: 615}, expr: &actionExpr{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, run: (*parser).callonGroupNode6, expr: &charClassMatcher{ - pos: position{line: 198, col: 5, offset: 4194}, + pos: position{line: 215, col: 5, offset: 4575}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1399,20 +1524,20 @@ var g = &grammar{ pos: position{line: 32, col: 17, offset: 624}, alternatives: []any{ &actionExpr{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, run: (*parser).callonGroupNode10, expr: &litMatcher{ - pos: position{line: 114, col: 5, offset: 2721}, + pos: position{line: 120, col: 5, offset: 2911}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, run: (*parser).callonGroupNode12, expr: &litMatcher{ - pos: position{line: 119, col: 5, offset: 2807}, + pos: position{line: 125, col: 5, offset: 2997}, val: "=", ignoreCase: false, want: "\"=\"", @@ -2284,19 +2409,19 @@ func (p *parser) callonNode198() (any, error) { return p.cur.onNode198() } -func (c *current) onNode202(v any) (any, error) { - return v, nil +func (c *current) onNode210() (any, error) { + return c.text, nil } -func (p *parser) callonNode202() (any, error) { +func (p *parser) callonNode210() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode202(stack["v"]) + return p.cur.onNode210() } func (c *current) onNode189(k, v any) (any, error) { - return buildStringNode(k, v, c.text, c.pos) + return buildNaturalLanguageDateTimeNodes(k, v, c.text, c.pos) } @@ -2306,158 +2431,213 @@ func (p *parser) callonNode189() (any, error) { return p.cur.onNode189(stack["k"], stack["v"]) } -func (c *current) onNode211() (any, error) { +func (c *current) onNode218() (any, error) { + return c.text, nil + +} + +func (p *parser) callonNode218() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode218() +} + +func (c *current) onNode221() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode211() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode211() -} - -func (c *current) onNode215() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode215() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode215() -} - -func (c *current) onNode219() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode219() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode219() -} - -func (c *current) onNode224() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode224() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode224() -} - -func (c *current) onNode226() (any, error) { - return nil, nil - -} - -func (p *parser) callonNode226() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode226() -} - -func (c *current) onNode230(v any) (any, error) { - return v, nil - -} - -func (p *parser) callonNode230() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode230(stack["v"]) -} - -func (c *current) onNode237(v any) (any, error) { - return nil, nil - -} - -func (p *parser) callonNode237() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode237(stack["v"]) -} - -func (c *current) onNode241() (any, error) { - return buildOperatorNode(c.text, c.pos) - -} - -func (p *parser) callonNode241() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode241() -} - -func (c *current) onNode221(v any) (any, error) { - return buildStringNode("", v, c.text, c.pos) - -} - func (p *parser) callonNode221() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode221(stack["v"]) + return p.cur.onNode221() } -func (c *current) onNode246() (any, error) { +func (c *current) onNode223() (any, error) { return buildOperatorNode(c.text, c.pos) } +func (p *parser) callonNode223() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode223() +} + +func (c *current) onNode227(v any) (any, error) { + return v, nil + +} + +func (p *parser) callonNode227() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode227(stack["v"]) +} + +func (c *current) onNode214(k, v any) (any, error) { + return buildStringNode(k, v, c.text, c.pos) + +} + +func (p *parser) callonNode214() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode214(stack["k"], stack["v"]) +} + +func (c *current) onNode236() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode236() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode236() +} + +func (c *current) onNode240() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode240() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode240() +} + +func (c *current) onNode244() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode244() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode244() +} + +func (c *current) onNode249() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode249() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode249() +} + +func (c *current) onNode251() (any, error) { + return nil, nil + +} + +func (p *parser) callonNode251() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode251() +} + +func (c *current) onNode255(v any) (any, error) { + return v, nil + +} + +func (p *parser) callonNode255() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode255(stack["v"]) +} + +func (c *current) onNode262(v any) (any, error) { + return nil, nil + +} + +func (p *parser) callonNode262() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode262(stack["v"]) +} + +func (c *current) onNode266() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode266() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode266() +} + +func (c *current) onNode246(v any) (any, error) { + return buildStringNode("", v, c.text, c.pos) + +} + func (p *parser) callonNode246() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode246() + return p.cur.onNode246(stack["v"]) } -func (c *current) onNode248() (any, error) { - return nil, nil - -} - -func (p *parser) callonNode248() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode248() -} - -func (c *current) onNode254(v any) (any, error) { - return nil, nil - -} - -func (p *parser) callonNode254() (any, error) { - stack := p.vstack[len(p.vstack)-1] - _ = stack - return p.cur.onNode254(stack["v"]) -} - -func (c *current) onNode258() (any, error) { +func (c *current) onNode271() (any, error) { return buildOperatorNode(c.text, c.pos) } -func (p *parser) callonNode258() (any, error) { +func (p *parser) callonNode271() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode258() + return p.cur.onNode271() } -func (c *current) onNode243(v any) (any, error) { +func (c *current) onNode273() (any, error) { + return nil, nil + +} + +func (p *parser) callonNode273() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode273() +} + +func (c *current) onNode279(v any) (any, error) { + return nil, nil + +} + +func (p *parser) callonNode279() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode279(stack["v"]) +} + +func (c *current) onNode283() (any, error) { + return buildOperatorNode(c.text, c.pos) + +} + +func (p *parser) callonNode283() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onNode283() +} + +func (c *current) onNode268(v any) (any, error) { return buildStringNode("", v, c.text, c.pos) } -func (p *parser) callonNode243() (any, error) { +func (p *parser) callonNode268() (any, error) { stack := p.vstack[len(p.vstack)-1] _ = stack - return p.cur.onNode243(stack["v"]) + return p.cur.onNode268(stack["v"]) } func (c *current) onGroupNode6() (any, error) { diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index 7e86456a9..05e3cf561 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/araddon/dateparse" + "github.com/jinzhu/now" tAssert "github.com/stretchr/testify/assert" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" @@ -13,66 +13,18 @@ import ( "github.com/owncloud/ocis/v2/services/search/pkg/query/kql" ) -var mustParseTime = func(t *testing.T, ts string) time.Time { - tp, err := dateparse.ParseLocal(ts) - if err != nil { - t.Fatalf("time.Parse(...) error = %v", err) - } - - return tp -} - -var mustJoin = func(v []string) string { - return strings.Join(v, " ") -} - -var FullDictionary = []string{ - `federated search`, - `federat* search`, - `search fed*`, - `author:"John Smith"`, - `filetype:docx`, - `filename:budget.xlsx`, - `author: "John Smith"`, - `author :"John Smith"`, - `author : "John Smith"`, - `author "John Smith"`, - `author "John Smith"`, - `author:Shakespear`, - `author:Paul`, - `author:Shakesp*`, - `title:"Advanced Search"`, - `title:"Advanced Sear*"`, - `title:"Advan* Search"`, - `title:"*anced Search"`, - `author:"John Smith" OR author:"Jane Smith"`, - `author:"John Smith" AND filetype:docx`, - `author:("John Smith" "Jane Smith")`, - `author:("John Smith" OR "Jane Smith")`, - `(DepartmentId:* OR RelatedHubSites:*) AND contentclass:sts_site NOT IsHubSite:false`, - `author:"John Smith" (filetype:docx title:"Advanced Search")`, -} - -func TestParse(t *testing.T) { - tests := []struct { - name string - skip bool - givenQuery string - expectedAst *ast.Ast - expectedError error - }{ - // SPEC ////////////////////////////////////////////////////////////////////////////// - // - // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf - // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 - // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference - // - // ++ +func TestParse_Spec(t *testing.T) { + // SPEC ////////////////////////////////////////////////////////////////////////////// + // + // https://msopenspecs.azureedge.net/files/MS-KQL/%5bMS-KQL%5d.pdf + // https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-kql/3bbf06cd-8fc1-4277-bd92-8661ccd3c9b0 + // https://learn.microsoft.com/en-us/sharepoint/dev/general-development/keyword-query-language-kql-syntax-reference + tests := []testCase{ // 2.1.2 AND Operator // 3.1.2 AND Operator { name: `cat AND dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -82,22 +34,21 @@ func TestParse(t *testing.T) { }, { name: `AND`, - expectedError: kql.StartsWithBinaryOperatorError{ + error: kql.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, { name: `AND cat AND dog`, - expectedError: kql.StartsWithBinaryOperatorError{ + error: kql.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, - // ++ // 2.1.6 NOT Operator // 3.1.6 NOT Operator { name: `cat NOT dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -108,19 +59,18 @@ func TestParse(t *testing.T) { }, { name: `NOT dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, &ast.StringNode{Value: "dog"}, }, }, }, - // ++ // 2.1.8 OR Operator // 3.1.8 OR Operator { name: `cat OR dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolOR}, @@ -130,21 +80,20 @@ func TestParse(t *testing.T) { }, { name: `OR`, - expectedError: kql.StartsWithBinaryOperatorError{ + error: kql.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, { name: `OR cat AND dog`, - expectedError: kql.StartsWithBinaryOperatorError{ + error: kql.StartsWithBinaryOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, - // ++ // 3.1.11 Implicit Operator { name: `cat dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -154,7 +103,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND (dog OR fox)`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -168,7 +117,7 @@ func TestParse(t *testing.T) { }, { name: `cat (dog OR fox)`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -180,12 +129,11 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 2.1.12 Parentheses // 3.1.12 Parentheses { name: `(cat OR dog) AND fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, @@ -197,11 +145,10 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 3.2.3 Implicit Operator for Property Restriction { name: `author:"John Smith" filetype:docx`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -211,7 +158,7 @@ func TestParse(t *testing.T) { }, { name: `author:"John Smith" AND filetype:docx`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -221,7 +168,7 @@ func TestParse(t *testing.T) { }, { name: `author:"John Smith" author:"Jane Smith"`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolOR}, @@ -231,7 +178,7 @@ func TestParse(t *testing.T) { }, { name: `author:"John Smith" OR author:"Jane Smith"`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Key: "author", Value: "John Smith"}, &ast.OperatorNode{Value: kql.BoolOR}, @@ -241,7 +188,7 @@ func TestParse(t *testing.T) { }, { name: `cat filetype:docx`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -251,7 +198,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND filetype:docx`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -259,11 +206,10 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 3.3.1.1.1 Implicit AND Operator { name: `cat +dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -273,7 +219,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -283,7 +229,7 @@ func TestParse(t *testing.T) { }, { name: `cat -dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -294,7 +240,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND NOT dog`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -305,7 +251,7 @@ func TestParse(t *testing.T) { }, { name: `cat +dog -fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -318,7 +264,7 @@ func TestParse(t *testing.T) { }, { name: `cat AND dog AND NOT fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -331,7 +277,7 @@ func TestParse(t *testing.T) { }, { name: `cat dog +fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -343,7 +289,7 @@ func TestParse(t *testing.T) { }, { name: `fox OR (fox AND (cat OR dog))`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "fox"}, &ast.OperatorNode{Value: kql.BoolOR}, @@ -361,7 +307,7 @@ func TestParse(t *testing.T) { }, { name: `cat dog -fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -374,7 +320,7 @@ func TestParse(t *testing.T) { }, { name: `(NOT fox) AND (cat OR dog)`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, @@ -391,7 +337,7 @@ func TestParse(t *testing.T) { }, { name: `cat +dog -fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -404,7 +350,7 @@ func TestParse(t *testing.T) { }, { name: `(NOT fox) AND (dog OR (dog AND cat))`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, @@ -423,12 +369,11 @@ func TestParse(t *testing.T) { }, }, }, - // ++ // 2.3.5 Date Tokens // 3.3.5 Date Tokens { name: `Modified:2023-09-05`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.DateTimeNode{ Key: "Modified", @@ -440,7 +385,7 @@ func TestParse(t *testing.T) { }, { name: `Modified:"2008-01-29"`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.DateTimeNode{ Key: "Modified", @@ -451,15 +396,400 @@ func TestParse(t *testing.T) { }, }, { - name: `Modified:today`, - skip: true, + name: `Modified:today`, + patch: patchNow(t, "2023-09-10"), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Modified", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, + }, }, - ////////////////////////////////////////////////////////////////////////////////////// - // everything else + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + testKQL(t, tc) + }) + } +} + +func TestParse_DateTimeRestrictionNode(t *testing.T) { + tests := []testCase{ { - name: "FullDictionary", - givenQuery: mustJoin(FullDictionary), - expectedAst: &ast.Ast{ + name: "format", + query: join([]string{ + `Mtime:"2023-09-05T08:42:11.23554+02:00"`, + `Mtime:2023-09-05T08:42:11.23554+02:00`, + `Mtime="2023-09-05T08:42:11.23554+02:00"`, + `Mtime=2023-09-05T08:42:11.23554+02:00`, + `Mtime<"2023-09-05T08:42:11.23554+02:00"`, + `Mtime<2023-09-05T08:42:11.23554+02:00`, + `Mtime<="2023-09-05T08:42:11.23554+02:00"`, + `Mtime<=2023-09-05T08:42:11.23554+02:00`, + `Mtime>"2023-09-05T08:42:11.23554+02:00"`, + `Mtime>2023-09-05T08:42:11.23554+02:00`, + `Mtime>="2023-09-05T08:42:11.23554+02:00"`, + `Mtime>=2023-09-05T08:42:11.23554+02:00`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ":"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">"}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - today", + patch: setWorldClock(t, "2023-09-10"), + query: join([]string{ + `Mtime:today`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-10"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - yesterday", + patch: setWorldClock(t, "2023-09-10"), + query: join([]string{ + `Mtime:yesterday`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-09"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-09 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - yesterday - the beginning of the month", + patch: setWorldClock(t, "2023-09-01"), + query: join([]string{ + `Mtime:yesterday`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-31"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this week", + patch: setWorldClock(t, "2023-09-06"), + query: join([]string{ + `Mtime:"this week"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-04"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-10 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this month", + patch: setWorldClock(t, "2023-09-02"), + query: join([]string{ + `Mtime:"this month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-09-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-09-30 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last month", + patch: setWorldClock(t, "2023-09-02"), + query: join([]string{ + `Mtime:"last month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-08-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-08-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last month - the beginning of the year", + patch: setWorldClock(t, "2023-01-01"), + query: join([]string{ + `Mtime:"last month"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-12-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - this year", + patch: setWorldClock(t, "2023-06-18"), + query: join([]string{ + `Mtime:"this year"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2023-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2023-12-31 23:59:59.999999999"), + }, + }, + }, + }, + { + name: "NaturalLanguage DateTimeNode - last year", + patch: setWorldClock(t, "2023-01-01"), + query: join([]string{ + `Mtime:"last year"`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: ">="}, + Value: mustParseTime(t, "2022-01-01"), + }, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.DateTimeNode{ + Key: "Mtime", + Operator: &ast.OperatorNode{Value: "<="}, + Value: mustParseTime(t, "2022-12-31 23:59:59.999999999"), + }, + }, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + testKQL(t, tc) + }) + } +} + +func TestParse_Errors(t *testing.T) { + tests := []testCase{ + { + query: "animal:(mammal:cat mammal:dog reptile:turtle)", + error: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "cat"}, + }, + }, + { + query: "animal:(cat mammal:dog turtle)", + error: kql.NamedGroupInvalidNodesError{ + Node: &ast.StringNode{Key: "mammal", Value: "dog"}, + }, + }, + { + query: "animal:(AND cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: "animal:(OR cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + query: "(AND cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: "(OR cat)", + error: kql.StartsWithBinaryOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + query: `cat dog`, + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Value: "cat"}, + &ast.OperatorNode{Value: kql.BoolAND}, + &ast.StringNode{Value: "dog"}, + }, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + testKQL(t, tc) + }) + } +} + +func TestParse_Stress(t *testing.T) { + tests := []testCase{ + { + name: "FullDictionary", + query: join(FullDictionary), + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "federated"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -565,13 +895,13 @@ func TestParse(t *testing.T) { }, }, { - name: "Group", - givenQuery: mustJoin([]string{ + name: "complex", + query: join([]string{ `(name:"moby di*" OR tag:bestseller) AND tag:book NOT tag:read`, `author:("John Smith" Jane)`, `author:("John Smith" OR Jane)`, }), - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Nodes: []ast.Node{ @@ -608,7 +938,7 @@ func TestParse(t *testing.T) { }, { name: `author:("John Smith" Jane) author:"Jack" AND author:"Oggy"`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "author", @@ -627,7 +957,7 @@ func TestParse(t *testing.T) { }, { name: `author:("John Smith" OR Jane)`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "author", @@ -642,7 +972,7 @@ func TestParse(t *testing.T) { }, { name: `NOT "John Smith" NOT Jane`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, &ast.StringNode{Value: "John Smith"}, @@ -654,7 +984,7 @@ func TestParse(t *testing.T) { }, { name: `NOT author:"John Smith" NOT author:"Jane Smith" NOT tag:sifi`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.OperatorNode{Value: kql.BoolNOT}, &ast.StringNode{Key: "author", Value: "John Smith"}, @@ -669,7 +999,7 @@ func TestParse(t *testing.T) { }, { name: `scope:"/new folder/subfolder" file`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{ Key: "scope", @@ -684,7 +1014,7 @@ func TestParse(t *testing.T) { }, { name: ` 😂 "*😀 😁*" name:😂💁👌🎍😍 name:😂💁👌 😍`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{ Value: "😂", @@ -710,121 +1040,9 @@ func TestParse(t *testing.T) { }, }, }, - { - name: "DateTimeRestrictionNode", - givenQuery: mustJoin([]string{ - `Mtime:"2023-09-05T08:42:11.23554+02:00"`, - `Mtime:2023-09-05T08:42:11.23554+02:00`, - `Mtime="2023-09-05T08:42:11.23554+02:00"`, - `Mtime=2023-09-05T08:42:11.23554+02:00`, - `Mtime<"2023-09-05T08:42:11.23554+02:00"`, - `Mtime<2023-09-05T08:42:11.23554+02:00`, - `Mtime<="2023-09-05T08:42:11.23554+02:00"`, - `Mtime<=2023-09-05T08:42:11.23554+02:00`, - `Mtime>"2023-09-05T08:42:11.23554+02:00"`, - `Mtime>2023-09-05T08:42:11.23554+02:00`, - `Mtime>="2023-09-05T08:42:11.23554+02:00"`, - `Mtime>=2023-09-05T08:42:11.23554+02:00`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ":"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: "<="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">"}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.DateTimeNode{ - Key: "Mtime", - Operator: &ast.OperatorNode{Value: ">="}, - Value: mustParseTime(t, "2023-09-05T08:42:11.23554+02:00"), - }, - }, - }, - }, - { - name: "id", - givenQuery: mustJoin([]string{ - `id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, - `ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, - }), - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{ - Key: "id", - Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", - }, - &ast.OperatorNode{Value: kql.BoolOR}, - &ast.StringNode{ - Key: "ID", - Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", - }, - }, - }, - }, { name: "animal:(cat dog turtle)", - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "animal", @@ -847,7 +1065,7 @@ func TestParse(t *testing.T) { }, { name: "(cat dog turtle)", - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Nodes: []ast.Node{ @@ -867,55 +1085,9 @@ func TestParse(t *testing.T) { }, }, }, - { - name: "animal:(mammal:cat mammal:dog reptile:turtle)", - expectedError: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "cat"}, - }, - }, - { - name: "animal:(cat mammal:dog turtle)", - expectedError: kql.NamedGroupInvalidNodesError{ - Node: &ast.StringNode{Key: "mammal", Value: "dog"}, - }, - }, - { - name: "animal:(AND cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: "animal:(OR cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: "(AND cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolAND}, - }, - }, - { - name: "(OR cat)", - expectedError: kql.StartsWithBinaryOperatorError{ - Node: &ast.OperatorNode{Value: kql.BoolOR}, - }, - }, - { - name: `cat dog`, - expectedAst: &ast.Ast{ - Nodes: []ast.Node{ - &ast.StringNode{Value: "cat"}, - &ast.OperatorNode{Value: kql.BoolAND}, - &ast.StringNode{Value: "dog"}, - }, - }, - }, { name: `cat dog fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Value: "cat"}, &ast.OperatorNode{Value: kql.BoolAND}, @@ -927,7 +1099,7 @@ func TestParse(t *testing.T) { }, { name: `(cat dog) fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Nodes: []ast.Node{ @@ -943,7 +1115,7 @@ func TestParse(t *testing.T) { }, { name: `(mammal:cat mammal:dog) fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Nodes: []ast.Node{ @@ -959,7 +1131,7 @@ func TestParse(t *testing.T) { }, { name: `mammal:(cat dog) fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "mammal", @@ -976,7 +1148,7 @@ func TestParse(t *testing.T) { }, { name: `mammal:(cat dog) mammal:fox`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "mammal", @@ -993,7 +1165,7 @@ func TestParse(t *testing.T) { }, { name: `title:((Advanced OR Search OR Query) -"Advanced Search Query")`, - expectedAst: &ast.Ast{ + ast: &ast.Ast{ Nodes: []ast.Node{ &ast.GroupNode{ Key: "title", @@ -1015,39 +1187,56 @@ func TestParse(t *testing.T) { }, }, }, + { + name: "ids", + query: join([]string{ + `id:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, + `ID:b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c`, + }), + ast: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{ + Key: "id", + Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", + }, + &ast.OperatorNode{Value: kql.BoolOR}, + &ast.StringNode{ + Key: "ID", + Value: "b27d3bf1-b254-459f-92e8-bdba668d6d3f$d0648459-25fb-4ed8-8684-bc62c7dca29c!d0648459-25fb-4ed8-8684-bc62c7dca29c", + }, + }, + }, + }, + { + name: `"test:test" test:"test:test" "more:*+#!/°^§$%&&/()=?<><<<<="}, + }, + &ast.OperatorNode{Value: BoolAND}, + &ast.DateTimeNode{ + Base: b, + Value: *to, + Key: key, + Operator: &ast.OperatorNode{Value: "<="}, + }, + }, nil + +} func buildBooleanNode(k, v interface{}, text []byte, pos position) (*ast.BooleanNode, error) { b, err := base(text, pos) diff --git a/services/search/pkg/query/kql/kql.go b/services/search/pkg/query/kql/kql.go index 8719223b8..2cf03a7f0 100644 --- a/services/search/pkg/query/kql/kql.go +++ b/services/search/pkg/query/kql/kql.go @@ -3,6 +3,7 @@ package kql import ( "errors" + "time" "github.com/owncloud/ocis/v2/services/search/pkg/query/ast" ) @@ -42,3 +43,7 @@ func (b Builder) Build(q string) (*ast.Ast, error) { return f.(*ast.Ast), nil } + +// timeNow mirrors time.Now by default, the only reason why this exists +// is to monkey patch it from the tests. See PatchTimeNow +var timeNow = time.Now diff --git a/vendor/github.com/araddon/dateparse/.travis.yml b/vendor/github.com/araddon/dateparse/.travis.yml deleted file mode 100644 index 3b4b17777..000000000 --- a/vendor/github.com/araddon/dateparse/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: go - -go: - - 1.13.x - -before_install: - - go get -t -v ./... - -script: - - go test -race -coverprofile=coverage.txt -covermode=atomic - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/araddon/dateparse/README.md b/vendor/github.com/araddon/dateparse/README.md deleted file mode 100644 index fe682dd56..000000000 --- a/vendor/github.com/araddon/dateparse/README.md +++ /dev/null @@ -1,323 +0,0 @@ -Go Date Parser ---------------------------- - -Parse many date strings without knowing format in advance. Uses a scanner to read bytes and use a state machine to find format. Much faster than shotgun based parse methods. See [bench_test.go](https://github.com/araddon/dateparse/blob/master/bench_test.go) for performance comparison. - - -[![Code Coverage](https://codecov.io/gh/araddon/dateparse/branch/master/graph/badge.svg)](https://codecov.io/gh/araddon/dateparse) -[![GoDoc](https://godoc.org/github.com/araddon/dateparse?status.svg)](http://godoc.org/github.com/araddon/dateparse) -[![Build Status](https://travis-ci.org/araddon/dateparse.svg?branch=master)](https://travis-ci.org/araddon/dateparse) -[![Go ReportCard](https://goreportcard.com/badge/araddon/dateparse)](https://goreportcard.com/report/araddon/dateparse) - -**MM/DD/YYYY VS DD/MM/YYYY** Right now this uses mm/dd/yyyy WHEN ambiguous if this is not desired behavior, use `ParseStrict` which will fail on ambiguous date strings. - -**Timezones** The location your server is configured affects the results! See example or https://play.golang.org/p/IDHRalIyXh and last paragraph here https://golang.org/pkg/time/#Parse. - - -```go - -// 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 - -// Return a string that represents the layout to parse the given date-time. -layout, err := dateparse.ParseFormat("May 8, 2009 5:57:51 PM") -> "Jan 2, 2006 3:04:05 PM" - -``` - -cli tool for testing dateformats ----------------------------------- - -[Date Parse CLI](https://github.com/araddon/dateparse/blob/master/dateparse) - - -Extended example -------------------- - -https://github.com/araddon/dateparse/blob/master/example/main.go - -```go -package main - -import ( - "flag" - "fmt" - "time" - - "github.com/scylladb/termtables" - "github.com/araddon/dateparse" -) - -var examples = []string{ - "May 8, 2009 5:57:51 PM", - "oct 7, 1970", - "oct 7, '70", - "oct. 7, 1970", - "oct. 7, 70", - "Mon Jan 2 15:04:05 2006", - "Mon Jan 2 15:04:05 MST 2006", - "Mon Jan 02 15:04:05 -0700 2006", - "Monday, 02-Jan-06 15:04:05 MST", - "Mon, 02 Jan 2006 15:04:05 MST", - "Tue, 11 Jul 2017 16:28:13 +0200 (CEST)", - "Mon, 02 Jan 2006 15:04:05 -0700", - "Mon 30 Sep 2018 09:09:09 PM UTC", - "Mon Aug 10 15:44:11 UTC+0100 2015", - "Thu, 4 Jan 2018 17:53:36 +0000", - "Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time)", - "Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00)", - "September 17, 2012 10:09am", - "September 17, 2012 at 10:09am PST-08", - "September 17, 2012, 10:10:09", - "October 7, 1970", - "October 7th, 1970", - "12 Feb 2006, 19:17", - "12 Feb 2006 19:17", - "14 May 2019 19:11:40.164", - "7 oct 70", - "7 oct 1970", - "03 February 2013", - "1 July 2013", - "2013-Feb-03", - // dd/Mon/yyy alpha Months - "06/Jan/2008:15:04:05 -0700", - "06/Jan/2008 15:04:05 -0700", - // mm/dd/yy - "3/31/2014", - "03/31/2014", - "08/21/71", - "8/1/71", - "4/8/2014 22:05", - "04/08/2014 22:05", - "4/8/14 22:05", - "04/2/2014 03:00:51", - "8/8/1965 12:00:00 AM", - "8/8/1965 01:00:01 PM", - "8/8/1965 01:00 PM", - "8/8/1965 1:00 PM", - "8/8/1965 12:00 AM", - "4/02/2014 03:00:51", - "03/19/2012 10:11:59", - "03/19/2012 10:11:59.3186369", - // yyyy/mm/dd - "2014/3/31", - "2014/03/31", - "2014/4/8 22:05", - "2014/04/08 22:05", - "2014/04/2 03:00:51", - "2014/4/02 03:00:51", - "2012/03/19 10:11:59", - "2012/03/19 10:11:59.3186369", - // yyyy:mm:dd - "2014:3:31", - "2014:03:31", - "2014:4:8 22:05", - "2014:04:08 22:05", - "2014:04:2 03:00:51", - "2014:4:02 03:00:51", - "2012:03:19 10:11:59", - "2012:03:19 10:11:59.3186369", - // Chinese - "2014年04月08日", - // yyyy-mm-ddThh - "2006-01-02T15:04:05+0000", - "2009-08-12T22:15:09-07:00", - "2009-08-12T22:15:09", - "2009-08-12T22:15:09.988", - "2009-08-12T22:15:09Z", - "2017-07-19T03:21:51:897+0100", - "2019-05-29T08:41-04", // no seconds, 2 digit TZ offset - // yyyy-mm-dd hh:mm:ss - "2014-04-26 17:24:37.3186369", - "2012-08-03 18:31:59.257000000", - "2014-04-26 17:24:37.123", - "2013-04-01 22:43", - "2013-04-01 22:43:22", - "2014-12-16 06:20:00 UTC", - "2014-12-16 06:20:00 GMT", - "2014-04-26 05:24:37 PM", - "2014-04-26 13:13:43 +0800", - "2014-04-26 13:13:43 +0800 +08", - "2014-04-26 13:13:44 +09:00", - "2012-08-03 18:31:59.257000000 +0000 UTC", - "2015-09-30 18:48:56.35272715 +0000 UTC", - "2015-02-18 00:12:00 +0000 GMT", - "2015-02-18 00:12:00 +0000 UTC", - "2015-02-08 03:02:00 +0300 MSK m=+0.000000001", - "2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001", - "2017-07-19 03:21:51+00:00", - "2014-04-26", - "2014-04", - "2014", - "2014-05-11 08:20:13,787", - // yyyy-mm-dd-07:00 - "2020-07-20+08:00", - // mm.dd.yy - "3.31.2014", - "03.31.2014", - "08.21.71", - "2014.03", - "2014.03.30", - // yyyymmdd and similar - "20140601", - "20140722105203", - // yymmdd hh:mm:yy mysql log - // 080313 05:21:55 mysqld started - "171113 14:14:20", - // unix seconds, ms, micro, nano - "1332151919", - "1384216367189", - "1384216367111222", - "1384216367111222333", -} - -var ( - timezone = "" -) - -func main() { - flag.StringVar(&timezone, "timezone", "UTC", "Timezone aka `America/Los_Angeles` formatted time-zone") - flag.Parse() - - if timezone != "" { - // NOTE: This is very, very important to understand - // time-parsing in go - loc, err := time.LoadLocation(timezone) - if err != nil { - panic(err.Error()) - } - time.Local = loc - } - - table := termtables.CreateTable() - - table.AddHeaders("Input", "Parsed, and Output as %v") - for _, dateExample := range examples { - t, err := dateparse.ParseLocal(dateExample) - if err != nil { - panic(err.Error()) - } - table.AddRow(dateExample, fmt.Sprintf("%v", t)) - } - fmt.Println(table.Render()) -} - -/* -+-------------------------------------------------------+-----------------------------------------+ -| Input | Parsed, and Output as %v | -+-------------------------------------------------------+-----------------------------------------+ -| May 8, 2009 5:57:51 PM | 2009-05-08 17:57:51 +0000 UTC | -| oct 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| oct 7, '70 | 1970-10-07 00:00:00 +0000 UTC | -| oct. 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| oct. 7, 70 | 1970-10-07 00:00:00 +0000 UTC | -| Mon Jan 2 15:04:05 2006 | 2006-01-02 15:04:05 +0000 UTC | -| Mon Jan 2 15:04:05 MST 2006 | 2006-01-02 15:04:05 +0000 MST | -| Mon Jan 02 15:04:05 -0700 2006 | 2006-01-02 15:04:05 -0700 -0700 | -| Monday, 02-Jan-06 15:04:05 MST | 2006-01-02 15:04:05 +0000 MST | -| Mon, 02 Jan 2006 15:04:05 MST | 2006-01-02 15:04:05 +0000 MST | -| Tue, 11 Jul 2017 16:28:13 +0200 (CEST) | 2017-07-11 16:28:13 +0200 +0200 | -| Mon, 02 Jan 2006 15:04:05 -0700 | 2006-01-02 15:04:05 -0700 -0700 | -| Mon 30 Sep 2018 09:09:09 PM UTC | 2018-09-30 21:09:09 +0000 UTC | -| Mon Aug 10 15:44:11 UTC+0100 2015 | 2015-08-10 15:44:11 +0000 UTC | -| Thu, 4 Jan 2018 17:53:36 +0000 | 2018-01-04 17:53:36 +0000 UTC | -| Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) | 2015-07-03 18:04:07 +0100 GMT | -| Sun, 3 Jan 2021 00:12:23 +0800 (GMT+08:00) | 2021-01-03 00:12:23 +0800 +0800 | -| September 17, 2012 10:09am | 2012-09-17 10:09:00 +0000 UTC | -| September 17, 2012 at 10:09am PST-08 | 2012-09-17 10:09:00 -0800 PST | -| September 17, 2012, 10:10:09 | 2012-09-17 10:10:09 +0000 UTC | -| October 7, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| October 7th, 1970 | 1970-10-07 00:00:00 +0000 UTC | -| 12 Feb 2006, 19:17 | 2006-02-12 19:17:00 +0000 UTC | -| 12 Feb 2006 19:17 | 2006-02-12 19:17:00 +0000 UTC | -| 14 May 2019 19:11:40.164 | 2019-05-14 19:11:40.164 +0000 UTC | -| 7 oct 70 | 1970-10-07 00:00:00 +0000 UTC | -| 7 oct 1970 | 1970-10-07 00:00:00 +0000 UTC | -| 03 February 2013 | 2013-02-03 00:00:00 +0000 UTC | -| 1 July 2013 | 2013-07-01 00:00:00 +0000 UTC | -| 2013-Feb-03 | 2013-02-03 00:00:00 +0000 UTC | -| 06/Jan/2008:15:04:05 -0700 | 2008-01-06 15:04:05 -0700 -0700 | -| 06/Jan/2008 15:04:05 -0700 | 2008-01-06 15:04:05 -0700 -0700 | -| 3/31/2014 | 2014-03-31 00:00:00 +0000 UTC | -| 03/31/2014 | 2014-03-31 00:00:00 +0000 UTC | -| 08/21/71 | 1971-08-21 00:00:00 +0000 UTC | -| 8/1/71 | 1971-08-01 00:00:00 +0000 UTC | -| 4/8/2014 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 04/08/2014 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 4/8/14 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 04/2/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 8/8/1965 12:00:00 AM | 1965-08-08 00:00:00 +0000 UTC | -| 8/8/1965 01:00:01 PM | 1965-08-08 13:00:01 +0000 UTC | -| 8/8/1965 01:00 PM | 1965-08-08 13:00:00 +0000 UTC | -| 8/8/1965 1:00 PM | 1965-08-08 13:00:00 +0000 UTC | -| 8/8/1965 12:00 AM | 1965-08-08 00:00:00 +0000 UTC | -| 4/02/2014 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 03/19/2012 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | -| 03/19/2012 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | -| 2014/3/31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014/03/31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014/4/8 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014/04/08 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014/04/2 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2014/4/02 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2012/03/19 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | -| 2012/03/19 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | -| 2014:3:31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014:03:31 | 2014-03-31 00:00:00 +0000 UTC | -| 2014:4:8 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014:04:08 22:05 | 2014-04-08 22:05:00 +0000 UTC | -| 2014:04:2 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2014:4:02 03:00:51 | 2014-04-02 03:00:51 +0000 UTC | -| 2012:03:19 10:11:59 | 2012-03-19 10:11:59 +0000 UTC | -| 2012:03:19 10:11:59.3186369 | 2012-03-19 10:11:59.3186369 +0000 UTC | -| 2014年04月08日 | 2014-04-08 00:00:00 +0000 UTC | -| 2006-01-02T15:04:05+0000 | 2006-01-02 15:04:05 +0000 UTC | -| 2009-08-12T22:15:09-07:00 | 2009-08-12 22:15:09 -0700 -0700 | -| 2009-08-12T22:15:09 | 2009-08-12 22:15:09 +0000 UTC | -| 2009-08-12T22:15:09.988 | 2009-08-12 22:15:09.988 +0000 UTC | -| 2009-08-12T22:15:09Z | 2009-08-12 22:15:09 +0000 UTC | -| 2017-07-19T03:21:51:897+0100 | 2017-07-19 03:21:51.897 +0100 +0100 | -| 2019-05-29T08:41-04 | 2019-05-29 08:41:00 -0400 -0400 | -| 2014-04-26 17:24:37.3186369 | 2014-04-26 17:24:37.3186369 +0000 UTC | -| 2012-08-03 18:31:59.257000000 | 2012-08-03 18:31:59.257 +0000 UTC | -| 2014-04-26 17:24:37.123 | 2014-04-26 17:24:37.123 +0000 UTC | -| 2013-04-01 22:43 | 2013-04-01 22:43:00 +0000 UTC | -| 2013-04-01 22:43:22 | 2013-04-01 22:43:22 +0000 UTC | -| 2014-12-16 06:20:00 UTC | 2014-12-16 06:20:00 +0000 UTC | -| 2014-12-16 06:20:00 GMT | 2014-12-16 06:20:00 +0000 UTC | -| 2014-04-26 05:24:37 PM | 2014-04-26 17:24:37 +0000 UTC | -| 2014-04-26 13:13:43 +0800 | 2014-04-26 13:13:43 +0800 +0800 | -| 2014-04-26 13:13:43 +0800 +08 | 2014-04-26 13:13:43 +0800 +0800 | -| 2014-04-26 13:13:44 +09:00 | 2014-04-26 13:13:44 +0900 +0900 | -| 2012-08-03 18:31:59.257000000 +0000 UTC | 2012-08-03 18:31:59.257 +0000 UTC | -| 2015-09-30 18:48:56.35272715 +0000 UTC | 2015-09-30 18:48:56.35272715 +0000 UTC | -| 2015-02-18 00:12:00 +0000 GMT | 2015-02-18 00:12:00 +0000 UTC | -| 2015-02-18 00:12:00 +0000 UTC | 2015-02-18 00:12:00 +0000 UTC | -| 2015-02-08 03:02:00 +0300 MSK m=+0.000000001 | 2015-02-08 03:02:00 +0300 +0300 | -| 2015-02-08 03:02:00.001 +0300 MSK m=+0.000000001 | 2015-02-08 03:02:00.001 +0300 +0300 | -| 2017-07-19 03:21:51+00:00 | 2017-07-19 03:21:51 +0000 UTC | -| 2014-04-26 | 2014-04-26 00:00:00 +0000 UTC | -| 2014-04 | 2014-04-01 00:00:00 +0000 UTC | -| 2014 | 2014-01-01 00:00:00 +0000 UTC | -| 2014-05-11 08:20:13,787 | 2014-05-11 08:20:13.787 +0000 UTC | -| 2020-07-20+08:00 | 2020-07-20 00:00:00 +0800 +0800 | -| 3.31.2014 | 2014-03-31 00:00:00 +0000 UTC | -| 03.31.2014 | 2014-03-31 00:00:00 +0000 UTC | -| 08.21.71 | 1971-08-21 00:00:00 +0000 UTC | -| 2014.03 | 2014-03-01 00:00:00 +0000 UTC | -| 2014.03.30 | 2014-03-30 00:00:00 +0000 UTC | -| 20140601 | 2014-06-01 00:00:00 +0000 UTC | -| 20140722105203 | 2014-07-22 10:52:03 +0000 UTC | -| 171113 14:14:20 | 2017-11-13 14:14:20 +0000 UTC | -| 1332151919 | 2012-03-19 10:11:59 +0000 UTC | -| 1384216367189 | 2013-11-12 00:32:47.189 +0000 UTC | -| 1384216367111222 | 2013-11-12 00:32:47.111222 +0000 UTC | -| 1384216367111222333 | 2013-11-12 00:32:47.111222333 +0000 UTC | -+-------------------------------------------------------+-----------------------------------------+ -*/ - -``` diff --git a/vendor/github.com/araddon/dateparse/parseany.go b/vendor/github.com/araddon/dateparse/parseany.go deleted file mode 100644 index b9668b21b..000000000 --- a/vendor/github.com/araddon/dateparse/parseany.go +++ /dev/null @@ -1,2189 +0,0 @@ -// Package dateparse parses date-strings without knowing the format -// in advance, using a fast lex based approach to eliminate shotgun -// attempts. It leans towards US style dates when there is a conflict. -package dateparse - -import ( - "fmt" - "strconv" - "strings" - "time" - "unicode" - "unicode/utf8" -) - -// func init() { -// gou.SetupLogging("debug") -// gou.SetColorOutput() -// } - -var days = []string{ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun", - "monday", - "tuesday", - "wednesday", - "thursday", - "friday", - "saturday", - "sunday", -} - -var months = []string{ - "january", - "february", - "march", - "april", - "may", - "june", - "july", - "august", - "september", - "october", - "november", - "december", -} - -type dateState uint8 -type timeState uint8 - -const ( - dateStart dateState = iota // 0 - dateDigit - dateDigitSt - dateYearDash - dateYearDashAlphaDash - dateYearDashDash - dateYearDashDashWs // 5 - dateYearDashDashT - dateYearDashDashOffset - dateDigitDash - dateDigitDashAlpha - dateDigitDashAlphaDash // 10 - dateDigitDot - dateDigitDotDot - dateDigitSlash - dateDigitYearSlash - dateDigitSlashAlpha // 15 - dateDigitColon - dateDigitChineseYear - dateDigitChineseYearWs - dateDigitWs - dateDigitWsMoYear // 20 - dateDigitWsMolong - dateAlpha - dateAlphaWs - dateAlphaWsDigit - dateAlphaWsDigitMore // 25 - dateAlphaWsDigitMoreWs - dateAlphaWsDigitMoreWsYear - dateAlphaWsMonth - dateAlphaWsDigitYearmaybe - dateAlphaWsMonthMore - dateAlphaWsMonthSuffix - dateAlphaWsMore - dateAlphaWsAtTime - dateAlphaWsAlpha - dateAlphaWsAlphaYearmaybe // 35 - dateAlphaPeriodWsDigit - dateWeekdayComma - dateWeekdayAbbrevComma -) -const ( - // Time state - timeIgnore timeState = iota // 0 - timeStart - timeWs - timeWsAlpha - timeWsAlphaWs - timeWsAlphaZoneOffset // 5 - timeWsAlphaZoneOffsetWs - timeWsAlphaZoneOffsetWsYear - timeWsAlphaZoneOffsetWsExtra - timeWsAMPMMaybe - timeWsAMPM // 10 - timeWsOffset - timeWsOffsetWs // 12 - timeWsOffsetColonAlpha - timeWsOffsetColon - timeWsYear // 15 - timeOffset - timeOffsetColon - timeAlpha - timePeriod - timePeriodOffset // 20 - timePeriodOffsetColon - timePeriodOffsetColonWs - timePeriodWs - timePeriodWsAlpha - timePeriodWsOffset // 25 - timePeriodWsOffsetWs - timePeriodWsOffsetWsAlpha - timePeriodWsOffsetColon - timePeriodWsOffsetColonAlpha - timeZ - timeZDigit -) - -var ( - // ErrAmbiguousMMDD for date formats such as 04/02/2014 the mm/dd vs dd/mm are - // ambiguous, so it is an error for strict parse rules. - ErrAmbiguousMMDD = fmt.Errorf("This date has ambiguous mm/dd vs dd/mm type format") -) - -func unknownErr(datestr string) error { - return fmt.Errorf("Could not find format for %q", datestr) -} - -// ParseAny parse an unknown date format, detect the layout. -// Normal parse. Equivalent Timezone rules as time.Parse(). -// NOTE: please see readme on mmdd vs ddmm ambiguous dates. -func ParseAny(datestr string, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - return time.Time{}, err - } - return p.parse() -} - -// ParseIn 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. -// That is, MST means one thing when using America/Denver and something else -// in other locations. -func ParseIn(datestr string, loc *time.Location, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, loc, opts...) - if err != nil { - return time.Time{}, err - } - return p.parse() -} - -// ParseLocal Given an unknown date format, detect the layout, -// using time.Local, parse. -// -// Set Location to time.Local. Same as ParseIn Location but lazily uses -// the global time.Local variable for Location argument. -// -// denverLoc, _ := time.LoadLocation("America/Denver") -// time.Local = denverLoc -// -// t, err := dateparse.ParseLocal("3/1/2014") -// -// Equivalent to: -// -// t, err := dateparse.ParseIn("3/1/2014", denverLoc) -// -func ParseLocal(datestr string, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, time.Local, opts...) - if err != nil { - return time.Time{}, err - } - return p.parse() -} - -// MustParse parse a date, and panic if it can't be parsed. Used for testing. -// Not recommended for most use-cases. -func MustParse(datestr string, opts ...ParserOption) time.Time { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - panic(err.Error()) - } - t, err := p.parse() - if err != nil { - panic(err.Error()) - } - return t -} - -// ParseFormat parse's an unknown date-time string and returns a layout -// string that can parse this (and exact same format) other date-time strings. -// -// layout, err := dateparse.ParseFormat("2013-02-01 00:00:00") -// // layout = "2006-01-02 15:04:05" -// -func ParseFormat(datestr string, opts ...ParserOption) (string, error) { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - return "", err - } - _, err = p.parse() - if err != nil { - return "", err - } - 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, opts ...ParserOption) (time.Time, error) { - p, err := parseTime(datestr, nil, opts...) - if err != nil { - return time.Time{}, err - } - if p.ambiguousMD { - return time.Time{}, ErrAmbiguousMMDD - } - return p.parse() -} - -func parseTime(datestr string, loc *time.Location, opts ...ParserOption) (p *parser, err error) { - - p = newParser(datestr, loc, opts...) - if p.retryAmbiguousDateWithSwap { - // month out of range signifies that a day/month swap is the correct solution to an ambiguous date - // this is because it means that a day is being interpreted as a month and overflowing the valid value for that - // by retrying in this case, we can fix a common situation with no assumptions - defer func() { - if p != nil && p.ambiguousMD { - // if it errors out with the following error, swap before we - // get out of this function to reduce scope it needs to be applied on - _, err := p.parse() - if err != nil && strings.Contains(err.Error(), "month out of range") { - // create the option to reverse the preference - preferMonthFirst := PreferMonthFirst(!p.preferMonthFirst) - // turn off the retry to avoid endless recursion - retryAmbiguousDateWithSwap := RetryAmbiguousDateWithSwap(false) - modifiedOpts := append(opts, preferMonthFirst, retryAmbiguousDateWithSwap) - p, err = parseTime(datestr, time.Local, modifiedOpts...) - } - } - - }() - } - - i := 0 - - // General strategy is to read rune by rune through the date looking for - // certain hints of what type of date we are dealing with. - // Hopefully we only need to read about 5 or 6 bytes before - // we figure it out and then attempt a parse -iterRunes: - for ; i < len(datestr); i++ { - //r := rune(datestr[i]) - r, bytesConsumed := utf8.DecodeRuneInString(datestr[i:]) - if bytesConsumed > 1 { - i += (bytesConsumed - 1) - } - - // gou.Debugf("i=%d r=%s state=%d %s", i, string(r), p.stateDate, datestr) - switch p.stateDate { - case dateStart: - if unicode.IsDigit(r) { - p.stateDate = dateDigit - } else if unicode.IsLetter(r) { - p.stateDate = dateAlpha - } else { - return nil, unknownErr(datestr) - } - case dateDigit: - - switch r { - case '-', '\u2212': - // 2006-01-02 - // 2013-Feb-03 - // 13-Feb-03 - // 29-Jun-2016 - if i == 4 { - p.stateDate = dateYearDash - p.yeari = 0 - p.yearlen = i - p.moi = i + 1 - p.set(0, "2006") - } else { - p.stateDate = dateDigitDash - } - case '/': - // 08/May/2005 - // 03/31/2005 - // 2014/02/24 - p.stateDate = dateDigitSlash - if i == 4 { - // 2014/02/24 - Year first / - p.yearlen = i // since it was start of datestr, i=len - p.moi = i + 1 - p.setYear() - p.stateDate = dateDigitYearSlash - } else { - // Either Ambiguous dd/mm vs mm/dd OR dd/month/yy - // 08/May/2005 - // 03/31/2005 - // 31/03/2005 - if i+2 < len(p.datestr) && unicode.IsLetter(rune(datestr[i+1])) { - // 08/May/2005 - p.stateDate = dateDigitSlashAlpha - p.moi = i + 1 - p.daylen = 2 - p.dayi = 0 - p.setDay() - continue - } - // Ambiguous dd/mm vs mm/dd the bane of date-parsing - // 03/31/2005 - // 31/03/2005 - p.ambiguousMD = true - if p.preferMonthFirst { - if p.molen == 0 { - // 03/31/2005 - p.molen = i - p.setMonth() - p.dayi = i + 1 - } - } else { - if p.daylen == 0 { - p.daylen = i - p.setDay() - p.moi = i + 1 - } - } - - } - - case ':': - // 03/31/2005 - // 2014/02/24 - p.stateDate = dateDigitColon - if i == 4 { - p.yearlen = i - p.moi = i + 1 - p.setYear() - } else { - p.ambiguousMD = true - if p.preferMonthFirst { - if p.molen == 0 { - p.molen = i - p.setMonth() - p.dayi = i + 1 - } - } - } - - case '.': - // 3.31.2014 - // 08.21.71 - // 2014.05 - p.stateDate = dateDigitDot - if i == 4 { - p.yearlen = i - p.moi = i + 1 - p.setYear() - } else { - p.ambiguousMD = true - p.moi = 0 - p.molen = i - p.setMonth() - p.dayi = i + 1 - } - - case ' ': - // 18 January 2018 - // 8 January 2018 - // 8 jan 2018 - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - if i == 6 { - p.stateDate = dateDigitSt - } else { - p.stateDate = dateDigitWs - p.dayi = 0 - p.daylen = i - } - case '年': - // Chinese Year - p.stateDate = dateDigitChineseYear - case ',': - return nil, unknownErr(datestr) - default: - continue - } - p.part1Len = i - - case dateDigitSt: - p.set(0, "060102") - i = i - 1 - p.stateTime = timeStart - break iterRunes - case dateYearDash: - // dateYearDashDashT - // 2006-01-02T15:04:05Z07:00 - // 2020-08-17T17:00:00:000+0100 - // dateYearDashDashWs - // 2013-04-01 22:43:22 - // dateYearDashAlphaDash - // 2013-Feb-03 - switch r { - case '-': - p.molen = i - p.moi - p.dayi = i + 1 - p.stateDate = dateYearDashDash - p.setMonth() - default: - if unicode.IsLetter(r) { - p.stateDate = dateYearDashAlphaDash - } - } - - case dateYearDashDash: - // dateYearDashDashT - // 2006-01-02T15:04:05Z07:00 - // dateYearDashDashWs - // 2013-04-01 22:43:22 - // dateYearDashDashOffset - // 2020-07-20+00:00 - switch r { - case '+', '-': - p.offseti = i - p.daylen = i - p.dayi - p.stateDate = dateYearDashDashOffset - p.setDay() - case ' ': - p.daylen = i - p.dayi - p.stateDate = dateYearDashDashWs - p.stateTime = timeStart - p.setDay() - break iterRunes - case 'T': - p.daylen = i - p.dayi - p.stateDate = dateYearDashDashT - p.stateTime = timeStart - p.setDay() - break iterRunes - } - - case dateYearDashDashT: - // dateYearDashDashT - // 2006-01-02T15:04:05Z07:00 - // 2020-08-17T17:00:00:000+0100 - - case dateYearDashDashOffset: - // 2020-07-20+00:00 - switch r { - case ':': - p.set(p.offseti, "-07:00") - // case ' ': - // return nil, unknownErr(datestr) - } - - case dateYearDashAlphaDash: - // 2013-Feb-03 - switch r { - case '-': - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.dayi = i + 1 - } - case dateDigitDash: - // 13-Feb-03 - // 29-Jun-2016 - if unicode.IsLetter(r) { - p.stateDate = dateDigitDashAlpha - p.moi = i - } else { - return nil, unknownErr(datestr) - } - case dateDigitDashAlpha: - // 13-Feb-03 - // 28-Feb-03 - // 29-Jun-2016 - switch r { - case '-': - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.yeari = i + 1 - p.stateDate = dateDigitDashAlphaDash - } - - case dateDigitDashAlphaDash: - // 13-Feb-03 ambiguous - // 28-Feb-03 ambiguous - // 29-Jun-2016 dd-month(alpha)-yyyy - switch r { - case ' ': - // we need to find if this was 4 digits, aka year - // or 2 digits which makes it ambiguous year/day - length := i - (p.moi + p.molen + 1) - if length == 4 { - p.yearlen = 4 - p.set(p.yeari, "2006") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } else if length == 2 { - // We have no idea if this is - // yy-mon-dd OR dd-mon-yy - // - // We are going to ASSUME (bad, bad) that it is dd-mon-yy which is a horible assumption - p.ambiguousMD = true - p.yearlen = 2 - p.set(p.yeari, "06") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } - p.stateTime = timeStart - break iterRunes - } - - case dateDigitYearSlash: - // 2014/07/10 06:55:38.156283 - // I honestly don't know if this format ever shows up as yyyy/ - - switch r { - case ' ', ':': - p.stateTime = timeStart - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - } - break iterRunes - case '/': - if p.molen == 0 { - p.molen = i - p.moi - p.setMonth() - p.dayi = i + 1 - } - } - - case dateDigitSlashAlpha: - // 06/May/2008 - - switch r { - case '/': - // | - // 06/May/2008 - if p.molen == 0 { - p.set(p.moi, "Jan") - p.yeari = i + 1 - } - // We aren't breaking because we are going to re-use this case - // to find where the date starts, and possible time begins - case ' ', ':': - p.stateTime = timeStart - if p.yearlen == 0 { - p.yearlen = i - p.yeari - p.setYear() - } - break iterRunes - } - - case dateDigitSlash: - // 03/19/2012 10:11:59 - // 04/2/2014 03:00:37 - // 3/1/2012 10:11:59 - // 4/8/2014 22:05 - // 3/1/2014 - // 10/13/2014 - // 01/02/2006 - // 1/2/06 - - switch r { - case '/': - // This is the 2nd / so now we should know start pts of all of the dd, mm, yy - if p.preferMonthFirst { - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - } - } else { - if p.molen == 0 { - p.molen = i - p.moi - p.setMonth() - p.yeari = i + 1 - } - } - // Note no break, we are going to pass by and re-enter this dateDigitSlash - // and look for ending (space) or not (just date) - case ' ': - p.stateTime = timeStart - if p.yearlen == 0 { - p.yearlen = i - p.yeari - p.setYear() - } - break iterRunes - } - - case dateDigitColon: - // 2014:07:10 06:55:38.156283 - // 03:19:2012 10:11:59 - // 04:2:2014 03:00:37 - // 3:1:2012 10:11:59 - // 4:8:2014 22:05 - // 3:1:2014 - // 10:13:2014 - // 01:02:2006 - // 1:2:06 - - switch r { - case ' ': - p.stateTime = timeStart - if p.yearlen == 0 { - p.yearlen = i - p.yeari - p.setYear() - } else if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - } - break iterRunes - case ':': - if p.yearlen > 0 { - // 2014:07:10 06:55:38.156283 - if p.molen == 0 { - p.molen = i - p.moi - p.setMonth() - p.dayi = i + 1 - } - } else if p.preferMonthFirst { - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - } - } - } - - case dateDigitWs: - // 18 January 2018 - // 8 January 2018 - // 8 jan 2018 - // 1 jan 18 - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - switch r { - case ' ': - p.yeari = i + 1 - //p.yearlen = 4 - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - p.stateTime = timeStart - if i > p.daylen+len(" Sep") { // November etc - // If len greather than space + 3 it must be full month - p.stateDate = dateDigitWsMolong - } else { - // If len=3, the might be Feb or May? Ie ambigous abbreviated but - // we can parse may with either. BUT, that means the - // format may not be correct? - // mo := strings.ToLower(datestr[p.daylen+1 : i]) - p.moi = p.daylen + 1 - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.stateDate = dateDigitWsMoYear - } - } - - case dateDigitWsMoYear: - // 8 jan 2018 - // 02 Jan 2018 23:59 - // 02 Jan 2018 23:59:34 - // 12 Feb 2006, 19:17 - // 12 Feb 2006, 19:17:22 - switch r { - case ',': - p.yearlen = i - p.yeari - p.setYear() - i++ - break iterRunes - case ' ': - p.yearlen = i - p.yeari - p.setYear() - break iterRunes - } - case dateDigitWsMolong: - // 18 January 2018 - // 8 January 2018 - - case dateDigitChineseYear: - // dateDigitChineseYear - // 2014年04月08日 - // weekday %Y年%m月%e日 %A %I:%M %p - // 2013年07月18日 星期四 10:27 上午 - if r == ' ' { - p.stateDate = dateDigitChineseYearWs - break - } - case dateDigitDot: - // This is the 2nd period - // 3.31.2014 - // 08.21.71 - // 2014.05 - // 2018.09.30 - if r == '.' { - if p.moi == 0 { - // 3.31.2014 - p.daylen = i - p.dayi - p.yeari = i + 1 - p.setDay() - p.stateDate = dateDigitDotDot - } else { - // 2018.09.30 - //p.molen = 2 - p.molen = i - p.moi - p.dayi = i + 1 - p.setMonth() - p.stateDate = dateDigitDotDot - } - } - case dateDigitDotDot: - // iterate all the way through - case dateAlpha: - // dateAlphaWS - // Mon Jan _2 15:04:05 2006 - // Mon Jan _2 15:04:05 MST 2006 - // Mon Jan 02 15:04:05 -0700 2006 - // Mon Aug 10 15:44:11 UTC+0100 2015 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - // dateAlphaWSDigit - // May 8, 2009 5:57:51 PM - // oct 1, 1970 - // dateAlphaWsMonth - // April 8, 2009 - // dateAlphaWsMore - // dateAlphaWsAtTime - // January 02, 2006 at 3:04pm MST-07 - // - // dateAlphaPeriodWsDigit - // oct. 1, 1970 - // dateWeekdayComma - // Monday, 02 Jan 2006 15:04:05 MST - // Monday, 02-Jan-06 15:04:05 MST - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // dateWeekdayAbbrevComma - // Mon, 02 Jan 2006 15:04:05 MST - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - // Mon, 02-Jan-06 15:04:05 MST - switch { - case r == ' ': - // X - // April 8, 2009 - if i > 3 { - // Check to see if the alpha is name of month? or Day? - month := strings.ToLower(datestr[0:i]) - if isMonthFull(month) { - p.fullMonth = month - // len(" 31, 2018") = 9 - if len(datestr[i:]) < 10 { - // April 8, 2009 - p.stateDate = dateAlphaWsMonth - } else { - p.stateDate = dateAlphaWsMore - } - p.dayi = i + 1 - break - } - - } else { - // This is possibly ambiguous? May will parse as either though. - // So, it could return in-correct format. - // dateAlphaWs - // May 05, 2005, 05:05:05 - // May 05 2005, 05:05:05 - // Jul 05, 2005, 05:05:05 - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // skip & return to dateStart - // Tue 05 May 2020, 05:05:05 - // Mon Jan 2 15:04:05 2006 - - maybeDay := strings.ToLower(datestr[0:i]) - if isDay(maybeDay) { - // using skip throws off indices used by other code; saner to restart - return parseTime(datestr[i+1:], loc) - } - p.stateDate = dateAlphaWs - } - - case r == ',': - // Mon, 02 Jan 2006 - - if i == 3 { - p.stateDate = dateWeekdayAbbrevComma - p.set(0, "Mon") - } else { - p.stateDate = dateWeekdayComma - p.skip = i + 2 - i++ - // TODO: lets just make this "skip" as we don't need - // the mon, monday, they are all superfelous and not needed - // just lay down the skip, no need to fill and then skip - } - case r == '.': - // sept. 28, 2017 - // jan. 28, 2017 - p.stateDate = dateAlphaPeriodWsDigit - if i == 3 { - p.molen = i - p.set(0, "Jan") - } else if i == 4 { - // gross - datestr = datestr[0:i-1] + datestr[i:] - return parseTime(datestr, loc, opts...) - } else { - return nil, unknownErr(datestr) - } - } - - case dateAlphaWs: - // dateAlphaWsAlpha - // Mon Jan _2 15:04:05 2006 - // Mon Jan _2 15:04:05 MST 2006 - // Mon Jan 02 15:04:05 -0700 2006 - // Fri Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - // Mon Aug 10 15:44:11 UTC+0100 2015 - // dateAlphaWsDigit - // May 8, 2009 5:57:51 PM - // May 8 2009 5:57:51 PM - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // May 08 17:57:51 2009 - // oct 1, 1970 - // oct 7, '70 - switch { - case unicode.IsLetter(r): - p.set(0, "Mon") - p.stateDate = dateAlphaWsAlpha - p.set(i, "Jan") - case unicode.IsDigit(r): - p.set(0, "Jan") - p.stateDate = dateAlphaWsDigit - p.dayi = i - } - - case dateAlphaWsDigit: - // May 8, 2009 5:57:51 PM - // May 8 2009 5:57:51 PM - // oct 1, 1970 - // oct 7, '70 - // oct. 7, 1970 - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // May 08 17:57:51 2009 - if r == ',' { - p.daylen = i - p.dayi - p.setDay() - p.stateDate = dateAlphaWsDigitMore - } else if r == ' ' { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - p.stateDate = dateAlphaWsDigitYearmaybe - p.stateTime = timeStart - } else if unicode.IsLetter(r) { - p.stateDate = dateAlphaWsMonthSuffix - i-- - } - case dateAlphaWsDigitYearmaybe: - // x - // May 8 2009 5:57:51 PM - // May 8 17:57:51 2009 - // May 8 17:57:51 2009 - // May 08 17:57:51 2009 - // Jul 03 2015 18:04:07 GMT+0100 (GMT Daylight Time) - if r == ':' { - // Guessed wrong; was not a year - i = i - 3 - p.stateDate = dateAlphaWsDigit - p.yeari = 0 - break iterRunes - } else if r == ' ' { - // must be year format, not 15:04 - p.yearlen = i - p.yeari - p.setYear() - break iterRunes - } - case dateAlphaWsDigitMore: - // x - // May 8, 2009 5:57:51 PM - // May 05, 2005, 05:05:05 - // May 05 2005, 05:05:05 - // oct 1, 1970 - // oct 7, '70 - if r == ' ' { - p.yeari = i + 1 - p.stateDate = dateAlphaWsDigitMoreWs - } - case dateAlphaWsDigitMoreWs: - // x - // May 8, 2009 5:57:51 PM - // May 05, 2005, 05:05:05 - // oct 1, 1970 - // oct 7, '70 - switch r { - case '\'': - p.yeari = i + 1 - case ' ', ',': - // x - // May 8, 2009 5:57:51 PM - // x - // May 8, 2009, 5:57:51 PM - p.stateDate = dateAlphaWsDigitMoreWsYear - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - break iterRunes - } - - case dateAlphaWsMonth: - // April 8, 2009 - // April 8 2009 - switch r { - case ' ', ',': - // x - // June 8, 2009 - // x - // June 8 2009 - if p.daylen == 0 { - p.daylen = i - p.dayi - p.setDay() - } - case 's', 'S', 'r', 'R', 't', 'T', 'n', 'N': - // st, rd, nd, st - i-- - p.stateDate = dateAlphaWsMonthSuffix - default: - if p.daylen > 0 && p.yeari == 0 { - p.yeari = i - } - } - case dateAlphaWsMonthMore: - // X - // January 02, 2006, 15:04:05 - // January 02 2006, 15:04:05 - // January 02, 2006 15:04:05 - // January 02 2006 15:04:05 - switch r { - case ',': - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - i++ - break iterRunes - case ' ': - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - break iterRunes - } - case dateAlphaWsMonthSuffix: - // x - // April 8th, 2009 - // April 8th 2009 - switch r { - case 't', 'T': - if p.nextIs(i, 'h') || p.nextIs(i, 'H') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - case 'n', 'N': - if p.nextIs(i, 'd') || p.nextIs(i, 'D') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - case 's', 'S': - if p.nextIs(i, 't') || p.nextIs(i, 'T') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - case 'r', 'R': - if p.nextIs(i, 'd') || p.nextIs(i, 'D') { - if len(datestr) > i+2 { - return parseTime(fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+2:]), loc, opts...) - } - } - } - case dateAlphaWsMore: - // January 02, 2006, 15:04:05 - // January 02 2006, 15:04:05 - // January 2nd, 2006, 15:04:05 - // January 2nd 2006, 15:04:05 - // September 17, 2012 at 5:00pm UTC-05 - switch { - case r == ',': - // x - // January 02, 2006, 15:04:05 - if p.nextIs(i, ' ') { - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 2 - p.stateDate = dateAlphaWsMonthMore - i++ - } - - case r == ' ': - // x - // January 02 2006, 15:04:05 - p.daylen = i - p.dayi - p.setDay() - p.yeari = i + 1 - p.stateDate = dateAlphaWsMonthMore - case unicode.IsDigit(r): - // XX - // January 02, 2006, 15:04:05 - continue - case unicode.IsLetter(r): - // X - // January 2nd, 2006, 15:04:05 - p.daylen = i - p.dayi - p.setDay() - p.stateDate = dateAlphaWsMonthSuffix - i-- - } - - case dateAlphaPeriodWsDigit: - // oct. 7, '70 - switch { - case r == ' ': - // continue - case unicode.IsDigit(r): - p.stateDate = dateAlphaWsDigit - p.dayi = i - default: - return p, unknownErr(datestr) - } - case dateWeekdayComma: - // Monday, 02 Jan 2006 15:04:05 MST - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // Monday, 02-Jan-06 15:04:05 MST - if p.dayi == 0 { - p.dayi = i - } - switch r { - case ' ', '-': - if p.moi == 0 { - p.moi = i + 1 - p.daylen = i - p.dayi - p.setDay() - } else if p.yeari == 0 { - p.yeari = i + 1 - p.molen = i - p.moi - p.set(p.moi, "Jan") - } else { - p.stateTime = timeStart - break iterRunes - } - } - case dateWeekdayAbbrevComma: - // Mon, 02 Jan 2006 15:04:05 MST - // Mon, 02 Jan 2006 15:04:05 -0700 - // Thu, 13 Jul 2017 08:58:40 +0100 - // Thu, 4 Jan 2018 17:53:36 +0000 - // Tue, 11 Jul 2017 16:28:13 +0200 (CEST) - // Mon, 02-Jan-06 15:04:05 MST - switch r { - case ' ', '-': - if p.dayi == 0 { - p.dayi = i + 1 - } else if p.moi == 0 { - p.daylen = i - p.dayi - p.setDay() - p.moi = i + 1 - } else if p.yeari == 0 { - p.molen = i - p.moi - p.set(p.moi, "Jan") - p.yeari = i + 1 - } else { - p.yearlen = i - p.yeari - p.setYear() - p.stateTime = timeStart - break iterRunes - } - } - - default: - break iterRunes - } - } - p.coalesceDate(i) - if p.stateTime == timeStart { - // increment first one, since the i++ occurs at end of loop - if i < len(p.datestr) { - i++ - } - // ensure we skip any whitespace prefix - for ; i < len(datestr); i++ { - r := rune(datestr[i]) - if r != ' ' { - break - } - } - - iterTimeRunes: - for ; i < len(datestr); i++ { - r := rune(datestr[i]) - - // gou.Debugf("i=%d r=%s state=%d iterTimeRunes %s %s", i, string(r), p.stateTime, p.ds(), p.ts()) - - switch p.stateTime { - case timeStart: - // 22:43:22 - // 22:43 - // timeComma - // 08:20:13,787 - // timeWs - // 05:24:37 PM - // 06:20:00 UTC - // 06:20:00 UTC-05 - // 00:12:00 +0000 UTC - // 22:18:00 +0000 UTC m=+0.000000001 - // 15:04:05 -0700 - // 15:04:05 -07:00 - // 15:04:05 2008 - // timeOffset - // 03:21:51+00:00 - // 19:55:00+0100 - // timePeriod - // 17:24:37.3186369 - // 00:07:31.945167 - // 18:31:59.257000000 - // 00:00:00.000 - // timePeriodOffset - // 19:55:00.799+0100 - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 22:18:00.001 +0000 UTC m=+0.000000001 - // 00:00:00.000 +0000 UTC - // timePeriodWsAlpha - // 06:20:00.000 UTC - if p.houri == 0 { - p.houri = i - } - switch r { - case ',': - // hm, lets just swap out comma for period. for some reason go - // won't parse it. - // 2014-05-11 08:20:13,787 - ds := []byte(p.datestr) - ds[i] = '.' - return parseTime(string(ds), loc, opts...) - case '-', '+': - // 03:21:51+00:00 - p.stateTime = timeOffset - if p.seci == 0 { - // 22:18+0530 - p.minlen = i - p.mini - } else { - if p.seclen == 0 { - p.seclen = i - p.seci - } - if p.msi > 0 && p.mslen == 0 { - p.mslen = i - p.msi - } - } - p.offseti = i - case '.': - p.stateTime = timePeriod - p.seclen = i - p.seci - p.msi = i + 1 - case 'Z': - p.stateTime = timeZ - if p.seci == 0 { - p.minlen = i - p.mini - } else { - p.seclen = i - p.seci - } - // (Z)ulu time - p.loc = time.UTC - case 'a', 'A': - if p.nextIs(i, 't') || p.nextIs(i, 'T') { - // x - // September 17, 2012 at 5:00pm UTC-05 - i++ // skip t - if p.nextIs(i, ' ') { - // x - // September 17, 2012 at 5:00pm UTC-05 - i++ // skip ' - p.houri = 0 // reset hour - } - } else { - switch { - case r == 'a' && p.nextIs(i, 'm'): - p.coalesceTime(i) - p.set(i, "am") - case r == 'A' && p.nextIs(i, 'M'): - p.coalesceTime(i) - p.set(i, "PM") - } - } - - case 'p', 'P': - // Could be AM/PM - switch { - case r == 'p' && p.nextIs(i, 'm'): - p.coalesceTime(i) - p.set(i, "pm") - case r == 'P' && p.nextIs(i, 'M'): - p.coalesceTime(i) - p.set(i, "PM") - } - case ' ': - p.coalesceTime(i) - p.stateTime = timeWs - case ':': - if p.mini == 0 { - p.mini = i + 1 - p.hourlen = i - p.houri - } else if p.seci == 0 { - p.seci = i + 1 - p.minlen = i - p.mini - } else if p.seci > 0 { - // 18:31:59:257 ms uses colon, wtf - p.seclen = i - p.seci - p.set(p.seci, "05") - p.msi = i + 1 - - // gross, gross, gross. manipulating the datestr is horrible. - // https://github.com/araddon/dateparse/issues/117 - // Could not get the parsing to work using golang time.Parse() without - // replacing that colon with period. - p.set(i, ".") - datestr = datestr[0:i] + "." + datestr[i+1:] - p.datestr = datestr - } - } - case timeOffset: - // 19:55:00+0100 - // timeOffsetColon - // 15:04:05+07:00 - // 15:04:05-07:00 - if r == ':' { - p.stateTime = timeOffsetColon - } - case timeWs: - // timeWsAlpha - // 06:20:00 UTC - // 06:20:00 UTC-05 - // 15:44:11 UTC+0100 2015 - // 18:04:07 GMT+0100 (GMT Daylight Time) - // 17:57:51 MST 2009 - // timeWsAMPMMaybe - // 05:24:37 PM - // timeWsOffset - // 15:04:05 -0700 - // 00:12:00 +0000 UTC - // timeWsOffsetColon - // 15:04:05 -07:00 - // 17:57:51 -0700 2009 - // timeWsOffsetColonAlpha - // 00:12:00 +00:00 UTC - // timeWsYear - // 00:12:00 2008 - // timeZ - // 15:04:05.99Z - switch r { - case 'A', 'P': - // Could be AM/PM or could be PST or similar - p.tzi = i - p.stateTime = timeWsAMPMMaybe - case '+', '-': - p.offseti = i - p.stateTime = timeWsOffset - default: - if unicode.IsLetter(r) { - // 06:20:00 UTC - // 06:20:00 UTC-05 - // 15:44:11 UTC+0100 2015 - // 17:57:51 MST 2009 - p.tzi = i - p.stateTime = timeWsAlpha - } else if unicode.IsDigit(r) { - // 00:12:00 2008 - p.stateTime = timeWsYear - p.yeari = i - } - } - case timeWsAlpha: - // 06:20:00 UTC - // 06:20:00 UTC-05 - // timeWsAlphaWs - // 17:57:51 MST 2009 - // timeWsAlphaZoneOffset - // timeWsAlphaZoneOffsetWs - // timeWsAlphaZoneOffsetWsExtra - // 18:04:07 GMT+0100 (GMT Daylight Time) - // timeWsAlphaZoneOffsetWsYear - // 15:44:11 UTC+0100 2015 - switch r { - case '+', '-': - p.tzlen = i - p.tzi - if p.tzlen == 4 { - p.set(p.tzi, " MST") - } else if p.tzlen == 3 { - p.set(p.tzi, "MST") - } - p.stateTime = timeWsAlphaZoneOffset - p.offseti = i - case ' ': - // 17:57:51 MST 2009 - // 17:57:51 MST - p.tzlen = i - p.tzi - if p.tzlen == 4 { - p.set(p.tzi, " MST") - } else if p.tzlen == 3 { - p.set(p.tzi, "MST") - } - p.stateTime = timeWsAlphaWs - p.yeari = i + 1 - } - case timeWsAlphaWs: - // 17:57:51 MST 2009 - - case timeWsAlphaZoneOffset: - // 06:20:00 UTC-05 - // timeWsAlphaZoneOffset - // timeWsAlphaZoneOffsetWs - // timeWsAlphaZoneOffsetWsExtra - // 18:04:07 GMT+0100 (GMT Daylight Time) - // timeWsAlphaZoneOffsetWsYear - // 15:44:11 UTC+0100 2015 - switch r { - case ' ': - p.set(p.offseti, "-0700") - if p.yeari == 0 { - p.yeari = i + 1 - } - p.stateTime = timeWsAlphaZoneOffsetWs - } - case timeWsAlphaZoneOffsetWs: - // timeWsAlphaZoneOffsetWs - // timeWsAlphaZoneOffsetWsExtra - // 18:04:07 GMT+0100 (GMT Daylight Time) - // timeWsAlphaZoneOffsetWsYear - // 15:44:11 UTC+0100 2015 - if unicode.IsDigit(r) { - p.stateTime = timeWsAlphaZoneOffsetWsYear - } else { - p.extra = i - 1 - p.stateTime = timeWsAlphaZoneOffsetWsExtra - } - case timeWsAlphaZoneOffsetWsYear: - // 15:44:11 UTC+0100 2015 - if unicode.IsDigit(r) { - p.yearlen = i - p.yeari + 1 - if p.yearlen == 4 { - p.setYear() - } - } - case timeWsAMPMMaybe: - // timeWsAMPMMaybe - // timeWsAMPM - // 05:24:37 PM - // timeWsAlpha - // 00:12:00 PST - // 15:44:11 UTC+0100 2015 - if r == 'M' { - //return parse("2006-01-02 03:04:05 PM", datestr, loc) - p.stateTime = timeWsAMPM - p.set(i-1, "PM") - if p.hourlen == 2 { - p.set(p.houri, "03") - } else if p.hourlen == 1 { - p.set(p.houri, "3") - } - } else { - p.stateTime = timeWsAlpha - } - - case timeWsOffset: - // timeWsOffset - // 15:04:05 -0700 - // timeWsOffsetWsOffset - // 17:57:51 -0700 -07 - // timeWsOffsetWs - // 17:57:51 -0700 2009 - // 00:12:00 +0000 UTC - // timeWsOffsetColon - // 15:04:05 -07:00 - // timeWsOffsetColonAlpha - // 00:12:00 +00:00 UTC - switch r { - case ':': - p.stateTime = timeWsOffsetColon - case ' ': - p.set(p.offseti, "-0700") - p.yeari = i + 1 - p.stateTime = timeWsOffsetWs - } - case timeWsOffsetWs: - // 17:57:51 -0700 2009 - // 00:12:00 +0000 UTC - // 22:18:00.001 +0000 UTC m=+0.000000001 - // w Extra - // 17:57:51 -0700 -07 - switch r { - case '=': - // eff you golang - if datestr[i-1] == 'm' { - p.extra = i - 2 - p.trimExtra() - break - } - case '+', '-', '(': - // This really doesn't seem valid, but for some reason when round-tripping a go date - // their is an extra +03 printed out. seems like go bug to me, but, parsing anyway. - // 00:00:00 +0300 +03 - // 00:00:00 +0300 +0300 - p.extra = i - 1 - p.stateTime = timeWsOffset - p.trimExtra() - break - default: - switch { - case unicode.IsDigit(r): - p.yearlen = i - p.yeari + 1 - if p.yearlen == 4 { - p.setYear() - } - case unicode.IsLetter(r): - // 15:04:05 -0700 MST - if p.tzi == 0 { - p.tzi = i - } - } - } - - case timeWsOffsetColon: - // timeWsOffsetColon - // 15:04:05 -07:00 - // timeWsOffsetColonAlpha - // 2015-02-18 00:12:00 +00:00 UTC - if unicode.IsLetter(r) { - // 2015-02-18 00:12:00 +00:00 UTC - p.stateTime = timeWsOffsetColonAlpha - break iterTimeRunes - } - case timePeriod: - // 15:04:05.999999999+07:00 - // 15:04:05.999999999-07:00 - // 15:04:05.999999+07:00 - // 15:04:05.999999-07:00 - // 15:04:05.999+07:00 - // 15:04:05.999-07:00 - // timePeriod - // 17:24:37.3186369 - // 00:07:31.945167 - // 18:31:59.257000000 - // 00:00:00.000 - // timePeriodOffset - // 19:55:00.799+0100 - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // With Extra - // 00:00:00.000 +0300 +03 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // 22:18:00.001 +0000 UTC m=+0.000000001 - // timePeriodWsAlpha - // 06:20:00.000 UTC - switch r { - case ' ': - p.mslen = i - p.msi - p.stateTime = timePeriodWs - case '+', '-': - // This really shouldn't happen - p.mslen = i - p.msi - p.offseti = i - p.stateTime = timePeriodOffset - default: - if unicode.IsLetter(r) { - // 06:20:00.000 UTC - p.mslen = i - p.msi - p.stateTime = timePeriodWsAlpha - } - } - case timePeriodOffset: - // timePeriodOffset - // 19:55:00.799+0100 - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // 13:31:51.999-07:00 MST - if r == ':' { - p.stateTime = timePeriodOffsetColon - } - case timePeriodOffsetColon: - // timePeriodOffset - // timePeriodOffsetColon - // 15:04:05.999-07:00 - // 13:31:51.999 -07:00 MST - switch r { - case ' ': - p.set(p.offseti, "-07:00") - p.stateTime = timePeriodOffsetColonWs - p.tzi = i + 1 - } - case timePeriodOffsetColonWs: - // continue - case timePeriodWs: - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // timePeriodWsOffsetColon - // 13:31:51.999 -07:00 MST - // timePeriodWsAlpha - // 06:20:00.000 UTC - if p.offseti == 0 { - p.offseti = i - } - switch r { - case '+', '-': - p.mslen = i - p.msi - 1 - p.stateTime = timePeriodWsOffset - default: - if unicode.IsLetter(r) { - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - p.stateTime = timePeriodWsOffsetWsAlpha - break iterTimeRunes - } - } - - case timePeriodWsOffset: - // timePeriodWs - // timePeriodWsOffset - // 00:07:31.945167 +0000 - // 00:00:00.000 +0000 - // With Extra - // 00:00:00.000 +0300 +03 - // timePeriodWsOffsetAlpha - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // 03:02:00.001 +0300 MSK m=+0.000000001 - // timePeriodWsOffsetColon - // 13:31:51.999 -07:00 MST - // timePeriodWsAlpha - // 06:20:00.000 UTC - switch r { - case ':': - p.stateTime = timePeriodWsOffsetColon - case ' ': - p.set(p.offseti, "-0700") - case '+', '-': - // This really doesn't seem valid, but for some reason when round-tripping a go date - // their is an extra +03 printed out. seems like go bug to me, but, parsing anyway. - // 00:00:00.000 +0300 +03 - // 00:00:00.000 +0300 +0300 - p.extra = i - 1 - p.trimExtra() - break - default: - if unicode.IsLetter(r) { - // 00:07:31.945167 +0000 UTC - // 00:00:00.000 +0000 UTC - // 03:02:00.001 +0300 MSK m=+0.000000001 - p.stateTime = timePeriodWsOffsetWsAlpha - } - } - case timePeriodWsOffsetWsAlpha: - // 03:02:00.001 +0300 MSK m=+0.000000001 - // eff you golang - if r == '=' && datestr[i-1] == 'm' { - p.extra = i - 2 - p.trimExtra() - break - } - - case timePeriodWsOffsetColon: - // 13:31:51.999 -07:00 MST - switch r { - case ' ': - p.set(p.offseti, "-07:00") - default: - if unicode.IsLetter(r) { - // 13:31:51.999 -07:00 MST - p.tzi = i - p.stateTime = timePeriodWsOffsetColonAlpha - } - } - case timePeriodWsOffsetColonAlpha: - // continue - case timeZ: - // timeZ - // 15:04:05.99Z - // With a time-zone at end after Z - // 2006-01-02T15:04:05.999999999Z07:00 - // 2006-01-02T15:04:05Z07:00 - // RFC3339 = "2006-01-02T15:04:05Z07:00" - // RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" - if unicode.IsDigit(r) { - p.stateTime = timeZDigit - } - - } - } - - switch p.stateTime { - case timeWsAlpha: - switch len(p.datestr) - p.tzi { - case 3: - // 13:31:51.999 +01:00 CET - p.set(p.tzi, "MST") - case 4: - p.set(p.tzi, "MST") - p.extra = len(p.datestr) - 1 - p.trimExtra() - } - - case timeWsAlphaWs: - p.yearlen = i - p.yeari - p.setYear() - case timeWsYear: - p.yearlen = i - p.yeari - p.setYear() - case timeWsAlphaZoneOffsetWsExtra: - p.trimExtra() - case timeWsAlphaZoneOffset: - // 06:20:00 UTC-05 - if i-p.offseti < 4 { - p.set(p.offseti, "-07") - } else { - p.set(p.offseti, "-0700") - } - - case timePeriod: - p.mslen = i - p.msi - case timeOffset: - - switch len(p.datestr) - p.offseti { - case 0, 1, 2, 4: - return p, fmt.Errorf("TZ offset not recognized %q near %q (must be 2 or 4 digits optional colon)", datestr, string(datestr[p.offseti:])) - case 3: - // 19:55:00+01 - p.set(p.offseti, "-07") - case 5: - // 19:55:00+0100 - p.set(p.offseti, "-0700") - } - - case timeWsOffset: - p.set(p.offseti, "-0700") - case timeWsOffsetWs: - // 17:57:51 -0700 2009 - // 00:12:00 +0000 UTC - if p.tzi > 0 { - switch len(p.datestr) - p.tzi { - case 3: - // 13:31:51.999 +01:00 CET - p.set(p.tzi, "MST") - case 4: - // 13:31:51.999 +01:00 CEST - p.set(p.tzi, "MST ") - } - - } - case timeWsOffsetColon: - // 17:57:51 -07:00 - p.set(p.offseti, "-07:00") - case timeOffsetColon: - // 15:04:05+07:00 - p.set(p.offseti, "-07:00") - case timePeriodOffset: - // 19:55:00.799+0100 - p.set(p.offseti, "-0700") - case timePeriodOffsetColon: - p.set(p.offseti, "-07:00") - case timePeriodWsOffsetColonAlpha: - p.tzlen = i - p.tzi - switch p.tzlen { - case 3: - p.set(p.tzi, "MST") - case 4: - p.set(p.tzi, "MST ") - } - case timePeriodWsOffset: - p.set(p.offseti, "-0700") - } - p.coalesceTime(i) - } - - switch p.stateDate { - case dateDigit: - // unixy timestamps ish - // example ct type - // 1499979655583057426 19 nanoseconds - // 1499979795437000 16 micro-seconds - // 20180722105203 14 yyyyMMddhhmmss - // 1499979795437 13 milliseconds - // 1332151919 10 seconds - // 20140601 8 yyyymmdd - // 2014 4 yyyy - t := time.Time{} - if len(datestr) == len("1499979655583057426") { // 19 - // nano-seconds - if nanoSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(0, nanoSecs) - } - } else if len(datestr) == len("1499979795437000") { // 16 - // micro-seconds - if microSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(0, microSecs*1000) - } - } else if len(datestr) == len("yyyyMMddhhmmss") { // 14 - // yyyyMMddhhmmss - p.format = []byte("20060102150405") - return p, nil - } else if len(datestr) == len("1332151919000") { // 13 - if miliSecs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(0, miliSecs*1000*1000) - } - } else if len(datestr) == len("1332151919") { //10 - if secs, err := strconv.ParseInt(datestr, 10, 64); err == nil { - t = time.Unix(secs, 0) - } - } else if len(datestr) == len("20140601") { - p.format = []byte("20060102") - return p, nil - } else if len(datestr) == len("2014") { - p.format = []byte("2006") - return p, nil - } else if len(datestr) < 4 { - return nil, fmt.Errorf("unrecognized format, too short %v", datestr) - } - if !t.IsZero() { - if loc == nil { - p.t = &t - return p, nil - } - t = t.In(loc) - p.t = &t - return p, nil - } - case dateDigitSt: - // 171113 14:14:20 - return p, nil - - case dateYearDash: - // 2006-01 - return p, nil - - case dateYearDashDash: - // 2006-01-02 - // 2006-1-02 - // 2006-1-2 - // 2006-01-2 - return p, nil - - case dateYearDashDashOffset: - /// 2020-07-20+00:00 - switch len(p.datestr) - p.offseti { - case 5: - p.set(p.offseti, "-0700") - case 6: - p.set(p.offseti, "-07:00") - } - return p, nil - - case dateYearDashAlphaDash: - // 2013-Feb-03 - // 2013-Feb-3 - p.daylen = i - p.dayi - p.setDay() - return p, nil - - case dateYearDashDashWs: - // 2013-04-01 - return p, nil - - case dateYearDashDashT: - return p, nil - - case dateDigitDashAlphaDash: - // 13-Feb-03 ambiguous - // 28-Feb-03 ambiguous - // 29-Jun-2016 - length := len(datestr) - (p.moi + p.molen + 1) - if length == 4 { - p.yearlen = 4 - p.set(p.yeari, "2006") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } else if length == 2 { - // We have no idea if this is - // yy-mon-dd OR dd-mon-yy - // - // We are going to ASSUME (bad, bad) that it is dd-mon-yy which is a horible assumption - p.ambiguousMD = true - p.yearlen = 2 - p.set(p.yeari, "06") - // We now also know that part1 was the day - p.dayi = 0 - p.daylen = p.part1Len - p.setDay() - } - - return p, nil - - case dateDigitDot: - // 2014.05 - p.molen = i - p.moi - p.setMonth() - return p, nil - - case dateDigitDotDot: - // 03.31.1981 - // 3.31.2014 - // 3.2.1981 - // 3.2.81 - // 08.21.71 - // 2018.09.30 - return p, nil - - case dateDigitWsMoYear: - // 2 Jan 2018 - // 2 Jan 18 - // 2 Jan 2018 23:59 - // 02 Jan 2018 23:59 - // 12 Feb 2006, 19:17 - return p, nil - - case dateDigitWsMolong: - // 18 January 2018 - // 8 January 2018 - if p.daylen == 2 { - p.format = []byte("02 January 2006") - return p, nil - } - p.format = []byte("2 January 2006") - return p, nil // parse("2 January 2006", datestr, loc) - - case dateAlphaWsMonth: - p.yearlen = i - p.yeari - p.setYear() - return p, nil - - case dateAlphaWsMonthMore: - return p, nil - - case dateAlphaWsDigitMoreWs: - // oct 1, 1970 - p.yearlen = i - p.yeari - p.setYear() - return p, nil - - case dateAlphaWsDigitMoreWsYear: - // May 8, 2009 5:57:51 PM - // Jun 7, 2005, 05:57:51 - return p, nil - - case dateAlphaWsAlpha: - return p, nil - - case dateAlphaWsDigit: - return p, nil - - case dateAlphaWsDigitYearmaybe: - return p, nil - - case dateDigitSlash: - // 3/1/2014 - // 10/13/2014 - // 01/02/2006 - return p, nil - - case dateDigitSlashAlpha: - // 03/Jun/2014 - return p, nil - - case dateDigitYearSlash: - // 2014/10/13 - return p, nil - - case dateDigitColon: - // 3:1:2014 - // 10:13:2014 - // 01:02:2006 - // 2014:10:13 - return p, nil - - case dateDigitChineseYear: - // dateDigitChineseYear - // 2014年04月08日 - p.format = []byte("2006年01月02日") - return p, nil - - case dateDigitChineseYearWs: - p.format = []byte("2006年01月02日 15:04:05") - return p, nil - - case dateWeekdayComma: - // Monday, 02 Jan 2006 15:04:05 -0700 - // Monday, 02 Jan 2006 15:04:05 +0100 - // Monday, 02-Jan-06 15:04:05 MST - return p, nil - - case dateWeekdayAbbrevComma: - // Mon, 02-Jan-06 15:04:05 MST - // Mon, 02 Jan 2006 15:04:05 MST - return p, nil - - } - - return nil, unknownErr(datestr) -} - -type parser struct { - loc *time.Location - preferMonthFirst bool - retryAmbiguousDateWithSwap bool - ambiguousMD bool - stateDate dateState - stateTime timeState - format []byte - datestr string - fullMonth string - skip int - extra int - part1Len int - yeari int - yearlen int - moi int - molen int - dayi int - daylen int - houri int - hourlen int - mini int - minlen int - seci int - seclen int - msi int - mslen int - offseti int - offsetlen int - tzi int - tzlen int - t *time.Time -} - -// ParserOption defines a function signature implemented by options -// Options defined like this accept the parser and operate on the data within -type ParserOption func(*parser) error - -// PreferMonthFirst is an option that allows preferMonthFirst to be changed from its default -func PreferMonthFirst(preferMonthFirst bool) ParserOption { - return func(p *parser) error { - p.preferMonthFirst = preferMonthFirst - return nil - } -} - -// RetryAmbiguousDateWithSwap is an option that allows retryAmbiguousDateWithSwap to be changed from its default -func RetryAmbiguousDateWithSwap(retryAmbiguousDateWithSwap bool) ParserOption { - return func(p *parser) error { - p.retryAmbiguousDateWithSwap = retryAmbiguousDateWithSwap - return nil - } -} - -func newParser(dateStr string, loc *time.Location, opts ...ParserOption) *parser { - p := &parser{ - stateDate: dateStart, - stateTime: timeIgnore, - datestr: dateStr, - loc: loc, - preferMonthFirst: true, - retryAmbiguousDateWithSwap: false, - } - p.format = []byte(dateStr) - - // allow the options to mutate the parser fields from their defaults - for _, option := range opts { - option(p) - } - return p -} - -func (p *parser) nextIs(i int, b byte) bool { - if len(p.datestr) > i+1 && p.datestr[i+1] == b { - return true - } - return false -} - -func (p *parser) set(start int, val string) { - if start < 0 { - return - } - if len(p.format) < start+len(val) { - return - } - for i, r := range val { - p.format[start+i] = byte(r) - } -} -func (p *parser) setMonth() { - if p.molen == 2 { - p.set(p.moi, "01") - } else if p.molen == 1 { - p.set(p.moi, "1") - } -} - -func (p *parser) setDay() { - if p.daylen == 2 { - p.set(p.dayi, "02") - } else if p.daylen == 1 { - p.set(p.dayi, "2") - } -} -func (p *parser) setYear() { - if p.yearlen == 2 { - p.set(p.yeari, "06") - } else if p.yearlen == 4 { - p.set(p.yeari, "2006") - } -} -func (p *parser) coalesceDate(end int) { - if p.yeari > 0 { - if p.yearlen == 0 { - p.yearlen = end - p.yeari - } - p.setYear() - } - if p.moi > 0 && p.molen == 0 { - p.molen = end - p.moi - p.setMonth() - } - if p.dayi > 0 && p.daylen == 0 { - p.daylen = end - p.dayi - p.setDay() - } -} -func (p *parser) ts() string { - return fmt.Sprintf("h:(%d:%d) m:(%d:%d) s:(%d:%d)", p.houri, p.hourlen, p.mini, p.minlen, p.seci, p.seclen) -} -func (p *parser) ds() string { - return fmt.Sprintf("%s d:(%d:%d) m:(%d:%d) y:(%d:%d)", p.datestr, p.dayi, p.daylen, p.moi, p.molen, p.yeari, p.yearlen) -} -func (p *parser) coalesceTime(end int) { - // 03:04:05 - // 15:04:05 - // 3:04:05 - // 3:4:5 - // 15:04:05.00 - if p.houri > 0 { - if p.hourlen == 2 { - p.set(p.houri, "15") - } else if p.hourlen == 1 { - p.set(p.houri, "3") - } - } - if p.mini > 0 { - if p.minlen == 0 { - p.minlen = end - p.mini - } - if p.minlen == 2 { - p.set(p.mini, "04") - } else { - p.set(p.mini, "4") - } - } - if p.seci > 0 { - if p.seclen == 0 { - p.seclen = end - p.seci - } - if p.seclen == 2 { - p.set(p.seci, "05") - } else { - p.set(p.seci, "5") - } - } - - if p.msi > 0 { - for i := 0; i < p.mslen; i++ { - p.format[p.msi+i] = '0' - } - } -} -func (p *parser) setFullMonth(month string) { - if p.moi == 0 { - p.format = []byte(fmt.Sprintf("%s%s", "January", p.format[len(month):])) - } -} - -func (p *parser) trimExtra() { - if p.extra > 0 && len(p.format) > p.extra { - p.format = p.format[0:p.extra] - p.datestr = p.datestr[0:p.extra] - } -} - -// func (p *parser) remove(i, length int) { -// if len(p.format) > i+length { -// //append(a[:i], a[j:]...) -// p.format = append(p.format[0:i], p.format[i+length:]...) -// } -// if len(p.datestr) > i+length { -// //append(a[:i], a[j:]...) -// p.datestr = fmt.Sprintf("%s%s", p.datestr[0:i], p.datestr[i+length:]) -// } -// } - -func (p *parser) parse() (time.Time, error) { - if p.t != nil { - return *p.t, nil - } - if len(p.fullMonth) > 0 { - p.setFullMonth(p.fullMonth) - } - if p.skip > 0 && len(p.format) > p.skip { - p.format = p.format[p.skip:] - p.datestr = p.datestr[p.skip:] - } - - if p.loc == nil { - // gou.Debugf("parse layout=%q input=%q \ntx, err := time.Parse(%q, %q)", string(p.format), p.datestr, string(p.format), p.datestr) - return time.Parse(string(p.format), p.datestr) - } - //gou.Debugf("parse layout=%q input=%q \ntx, err := time.ParseInLocation(%q, %q, %v)", string(p.format), p.datestr, string(p.format), p.datestr, p.loc) - return time.ParseInLocation(string(p.format), p.datestr, p.loc) -} -func isDay(alpha string) bool { - for _, day := range days { - if alpha == day { - return true - } - } - return false -} -func isMonthFull(alpha string) bool { - for _, month := range months { - if alpha == month { - return true - } - } - return false -} diff --git a/vendor/github.com/jinzhu/now/Guardfile b/vendor/github.com/jinzhu/now/Guardfile new file mode 100644 index 000000000..0b860b065 --- /dev/null +++ b/vendor/github.com/jinzhu/now/Guardfile @@ -0,0 +1,3 @@ +guard 'gotest' do + watch(%r{\.go$}) +end diff --git a/vendor/github.com/araddon/dateparse/LICENSE b/vendor/github.com/jinzhu/now/License similarity index 95% rename from vendor/github.com/araddon/dateparse/LICENSE rename to vendor/github.com/jinzhu/now/License index f675ed313..037e1653e 100644 --- a/vendor/github.com/araddon/dateparse/LICENSE +++ b/vendor/github.com/jinzhu/now/License @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2017 Aaron Raddon +Copyright (c) 2013-NOW Jinzhu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/jinzhu/now/README.md b/vendor/github.com/jinzhu/now/README.md new file mode 100644 index 000000000..e81d31de6 --- /dev/null +++ b/vendor/github.com/jinzhu/now/README.md @@ -0,0 +1,137 @@ +## Now + +Now is a time toolkit for golang + +[![go report card](https://goreportcard.com/badge/github.com/jinzhu/now "go report card")](https://goreportcard.com/report/github.com/jinzhu/now) +[![test status](https://github.com/jinzhu/now/workflows/tests/badge.svg?branch=master "test status")](https://github.com/jinzhu/now/actions) +[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) + +## Install + +``` +go get -u github.com/jinzhu/now +``` + +## Usage + +Calculating time based on current time + +```go +import "github.com/jinzhu/now" + +time.Now() // 2013-11-18 17:51:49.123456789 Mon + +now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon +now.BeginningOfHour() // 2013-11-18 17:00:00 Mon +now.BeginningOfDay() // 2013-11-18 00:00:00 Mon +now.BeginningOfWeek() // 2013-11-17 00:00:00 Sun +now.BeginningOfMonth() // 2013-11-01 00:00:00 Fri +now.BeginningOfQuarter() // 2013-10-01 00:00:00 Tue +now.BeginningOfYear() // 2013-01-01 00:00:00 Tue + +now.EndOfMinute() // 2013-11-18 17:51:59.999999999 Mon +now.EndOfHour() // 2013-11-18 17:59:59.999999999 Mon +now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon +now.EndOfWeek() // 2013-11-23 23:59:59.999999999 Sat +now.EndOfMonth() // 2013-11-30 23:59:59.999999999 Sat +now.EndOfQuarter() // 2013-12-31 23:59:59.999999999 Tue +now.EndOfYear() // 2013-12-31 23:59:59.999999999 Tue + +now.WeekStartDay = time.Monday // Set Monday as first day, default is Sunday +now.EndOfWeek() // 2013-11-24 23:59:59.999999999 Sun +``` + +Calculating time based on another time + +```go +t := time.Date(2013, 02, 18, 17, 51, 49, 123456789, time.Now().Location()) +now.With(t).EndOfMonth() // 2013-02-28 23:59:59.999999999 Thu +``` + +Calculating time based on configuration + +```go +location, err := time.LoadLocation("Asia/Shanghai") + +myConfig := &now.Config{ + WeekStartDay: time.Monday, + TimeLocation: location, + TimeFormats: []string{"2006-01-02 15:04:05"}, +} + +t := time.Date(2013, 11, 18, 17, 51, 49, 123456789, time.Now().Location()) // // 2013-11-18 17:51:49.123456789 Mon +myConfig.With(t).BeginningOfWeek() // 2013-11-18 00:00:00 Mon + +myConfig.Parse("2002-10-12 22:14:01") // 2002-10-12 22:14:01 +myConfig.Parse("2002-10-12 22:14") // returns error 'can't parse string as time: 2002-10-12 22:14' +``` + +### Monday/Sunday + +Don't be bothered with the `WeekStartDay` setting, you can use `Monday`, `Sunday` + +```go +now.Monday() // 2013-11-18 00:00:00 Mon +now.Monday("17:44") // 2013-11-18 17:44:00 Mon +now.Sunday() // 2013-11-24 00:00:00 Sun (Next Sunday) +now.Sunday("18:19:24") // 2013-11-24 18:19:24 Sun (Next Sunday) +now.EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of next Sunday) + +t := time.Date(2013, 11, 24, 17, 51, 49, 123456789, time.Now().Location()) // 2013-11-24 17:51:49.123456789 Sun +now.With(t).Monday() // 2013-11-18 00:00:00 Mon (Last Monday if today is Sunday) +now.With(t).Monday("17:44") // 2013-11-18 17:44:00 Mon (Last Monday if today is Sunday) +now.With(t).Sunday() // 2013-11-24 00:00:00 Sun (Beginning Of Today if today is Sunday) +now.With(t).Sunday("18:19:24") // 2013-11-24 18:19:24 Sun (Beginning Of Today if today is Sunday) +now.With(t).EndOfSunday() // 2013-11-24 23:59:59.999999999 Sun (End of Today if today is Sunday) +``` + +### Parse String to Time + +```go +time.Now() // 2013-11-18 17:51:49.123456789 Mon + +// Parse(string) (time.Time, error) +t, err := now.Parse("2017") // 2017-01-01 00:00:00, nil +t, err := now.Parse("2017-10") // 2017-10-01 00:00:00, nil +t, err := now.Parse("2017-10-13") // 2017-10-13 00:00:00, nil +t, err := now.Parse("1999-12-12 12") // 1999-12-12 12:00:00, nil +t, err := now.Parse("1999-12-12 12:20") // 1999-12-12 12:20:00, nil +t, err := now.Parse("1999-12-12 12:20:21") // 1999-12-12 12:20:21, nil +t, err := now.Parse("10-13") // 2013-10-13 00:00:00, nil +t, err := now.Parse("12:20") // 2013-11-18 12:20:00, nil +t, err := now.Parse("12:20:13") // 2013-11-18 12:20:13, nil +t, err := now.Parse("14") // 2013-11-18 14:00:00, nil +t, err := now.Parse("99:99") // 2013-11-18 12:20:00, Can't parse string as time: 99:99 + +// MustParse must parse string to time or it will panic +now.MustParse("2013-01-13") // 2013-01-13 00:00:00 +now.MustParse("02-17") // 2013-02-17 00:00:00 +now.MustParse("2-17") // 2013-02-17 00:00:00 +now.MustParse("8") // 2013-11-18 08:00:00 +now.MustParse("2002-10-12 22:14") // 2002-10-12 22:14:00 +now.MustParse("99:99") // panic: Can't parse string as time: 99:99 +``` + +Extend `now` to support more formats is quite easy, just update `now.TimeFormats` with other time layouts, e.g: + +```go +now.TimeFormats = append(now.TimeFormats, "02 Jan 2006 15:04") +``` + +Please send me pull requests if you want a format to be supported officially + +## Contributing + +You can help to make the project better, check out [http://gorm.io/contribute.html](http://gorm.io/contribute.html) for things you can do. + +# Author + +**jinzhu** + +* +* +* + +## License + +Released under the [MIT License](http://www.opensource.org/licenses/MIT). diff --git a/vendor/github.com/jinzhu/now/main.go b/vendor/github.com/jinzhu/now/main.go new file mode 100644 index 000000000..8f78bc752 --- /dev/null +++ b/vendor/github.com/jinzhu/now/main.go @@ -0,0 +1,200 @@ +// Package now is a time toolkit for golang. +// +// More details README here: https://github.com/jinzhu/now +// +// import "github.com/jinzhu/now" +// +// now.BeginningOfMinute() // 2013-11-18 17:51:00 Mon +// now.BeginningOfDay() // 2013-11-18 00:00:00 Mon +// now.EndOfDay() // 2013-11-18 23:59:59.999999999 Mon +package now + +import "time" + +// WeekStartDay set week start day, default is sunday +var WeekStartDay = time.Sunday + +// TimeFormats default time formats will be parsed as +var TimeFormats = []string{ + "2006", "2006-1", "2006-1-2", "2006-1-2 15", "2006-1-2 15:4", "2006-1-2 15:4:5", "1-2", + "15:4:5", "15:4", "15", + "15:4:5 Jan 2, 2006 MST", "2006-01-02 15:04:05.999999999 -0700 MST", "2006-01-02T15:04:05Z0700", "2006-01-02T15:04:05Z07", + "2006.1.2", "2006.1.2 15:04:05", "2006.01.02", "2006.01.02 15:04:05", "2006.01.02 15:04:05.999999999", + "1/2/2006", "1/2/2006 15:4:5", "2006/01/02", "20060102", "2006/01/02 15:04:05", + time.ANSIC, time.UnixDate, time.RubyDate, time.RFC822, time.RFC822Z, time.RFC850, + time.RFC1123, time.RFC1123Z, time.RFC3339, time.RFC3339Nano, + time.Kitchen, time.Stamp, time.StampMilli, time.StampMicro, time.StampNano, +} + +// Config configuration for now package +type Config struct { + WeekStartDay time.Weekday + TimeLocation *time.Location + TimeFormats []string +} + +// DefaultConfig default config +var DefaultConfig *Config + +// New initialize Now based on configuration +func (config *Config) With(t time.Time) *Now { + return &Now{Time: t, Config: config} +} + +// Parse parse string to time based on configuration +func (config *Config) Parse(strs ...string) (time.Time, error) { + if config.TimeLocation == nil { + return config.With(time.Now()).Parse(strs...) + } else { + return config.With(time.Now().In(config.TimeLocation)).Parse(strs...) + } +} + +// MustParse must parse string to time or will panic +func (config *Config) MustParse(strs ...string) time.Time { + if config.TimeLocation == nil { + return config.With(time.Now()).MustParse(strs...) + } else { + return config.With(time.Now().In(config.TimeLocation)).MustParse(strs...) + } +} + +// Now now struct +type Now struct { + time.Time + *Config +} + +// With initialize Now with time +func With(t time.Time) *Now { + config := DefaultConfig + if config == nil { + config = &Config{ + WeekStartDay: WeekStartDay, + TimeFormats: TimeFormats, + } + } + + return &Now{Time: t, Config: config} +} + +// New initialize Now with time +func New(t time.Time) *Now { + return With(t) +} + +// BeginningOfMinute beginning of minute +func BeginningOfMinute() time.Time { + return With(time.Now()).BeginningOfMinute() +} + +// BeginningOfHour beginning of hour +func BeginningOfHour() time.Time { + return With(time.Now()).BeginningOfHour() +} + +// BeginningOfDay beginning of day +func BeginningOfDay() time.Time { + return With(time.Now()).BeginningOfDay() +} + +// BeginningOfWeek beginning of week +func BeginningOfWeek() time.Time { + return With(time.Now()).BeginningOfWeek() +} + +// BeginningOfMonth beginning of month +func BeginningOfMonth() time.Time { + return With(time.Now()).BeginningOfMonth() +} + +// BeginningOfQuarter beginning of quarter +func BeginningOfQuarter() time.Time { + return With(time.Now()).BeginningOfQuarter() +} + +// BeginningOfYear beginning of year +func BeginningOfYear() time.Time { + return With(time.Now()).BeginningOfYear() +} + +// EndOfMinute end of minute +func EndOfMinute() time.Time { + return With(time.Now()).EndOfMinute() +} + +// EndOfHour end of hour +func EndOfHour() time.Time { + return With(time.Now()).EndOfHour() +} + +// EndOfDay end of day +func EndOfDay() time.Time { + return With(time.Now()).EndOfDay() +} + +// EndOfWeek end of week +func EndOfWeek() time.Time { + return With(time.Now()).EndOfWeek() +} + +// EndOfMonth end of month +func EndOfMonth() time.Time { + return With(time.Now()).EndOfMonth() +} + +// EndOfQuarter end of quarter +func EndOfQuarter() time.Time { + return With(time.Now()).EndOfQuarter() +} + +// EndOfYear end of year +func EndOfYear() time.Time { + return With(time.Now()).EndOfYear() +} + +// Monday monday + +func Monday(strs ...string) time.Time { + return With(time.Now()).Monday(strs...) +} + +// Sunday sunday +func Sunday(strs ...string) time.Time { + return With(time.Now()).Sunday(strs...) +} + +// EndOfSunday end of sunday +func EndOfSunday() time.Time { + return With(time.Now()).EndOfSunday() +} + +// Quarter returns the yearly quarter +func Quarter() uint { + return With(time.Now()).Quarter() +} + +// Parse parse string to time +func Parse(strs ...string) (time.Time, error) { + return With(time.Now()).Parse(strs...) +} + +// ParseInLocation parse string to time in location +func ParseInLocation(loc *time.Location, strs ...string) (time.Time, error) { + return With(time.Now().In(loc)).Parse(strs...) +} + +// MustParse must parse string to time or will panic +func MustParse(strs ...string) time.Time { + return With(time.Now()).MustParse(strs...) +} + +// MustParseInLocation must parse string to time in location or will panic +func MustParseInLocation(loc *time.Location, strs ...string) time.Time { + return With(time.Now().In(loc)).MustParse(strs...) +} + +// Between check now between the begin, end time or not +func Between(time1, time2 string) bool { + return With(time.Now()).Between(time1, time2) +} diff --git a/vendor/github.com/jinzhu/now/now.go b/vendor/github.com/jinzhu/now/now.go new file mode 100644 index 000000000..2f524cc8d --- /dev/null +++ b/vendor/github.com/jinzhu/now/now.go @@ -0,0 +1,245 @@ +package now + +import ( + "errors" + "regexp" + "time" +) + +// BeginningOfMinute beginning of minute +func (now *Now) BeginningOfMinute() time.Time { + return now.Truncate(time.Minute) +} + +// BeginningOfHour beginning of hour +func (now *Now) BeginningOfHour() time.Time { + y, m, d := now.Date() + return time.Date(y, m, d, now.Time.Hour(), 0, 0, 0, now.Time.Location()) +} + +// BeginningOfDay beginning of day +func (now *Now) BeginningOfDay() time.Time { + y, m, d := now.Date() + return time.Date(y, m, d, 0, 0, 0, 0, now.Time.Location()) +} + +// BeginningOfWeek beginning of week +func (now *Now) BeginningOfWeek() time.Time { + t := now.BeginningOfDay() + weekday := int(t.Weekday()) + + if now.WeekStartDay != time.Sunday { + weekStartDayInt := int(now.WeekStartDay) + + if weekday < weekStartDayInt { + weekday = weekday + 7 - weekStartDayInt + } else { + weekday = weekday - weekStartDayInt + } + } + return t.AddDate(0, 0, -weekday) +} + +// BeginningOfMonth beginning of month +func (now *Now) BeginningOfMonth() time.Time { + y, m, _ := now.Date() + return time.Date(y, m, 1, 0, 0, 0, 0, now.Location()) +} + +// BeginningOfQuarter beginning of quarter +func (now *Now) BeginningOfQuarter() time.Time { + month := now.BeginningOfMonth() + offset := (int(month.Month()) - 1) % 3 + return month.AddDate(0, -offset, 0) +} + +// BeginningOfHalf beginning of half year +func (now *Now) BeginningOfHalf() time.Time { + month := now.BeginningOfMonth() + offset := (int(month.Month()) - 1) % 6 + return month.AddDate(0, -offset, 0) +} + +// BeginningOfYear BeginningOfYear beginning of year +func (now *Now) BeginningOfYear() time.Time { + y, _, _ := now.Date() + return time.Date(y, time.January, 1, 0, 0, 0, 0, now.Location()) +} + +// EndOfMinute end of minute +func (now *Now) EndOfMinute() time.Time { + return now.BeginningOfMinute().Add(time.Minute - time.Nanosecond) +} + +// EndOfHour end of hour +func (now *Now) EndOfHour() time.Time { + return now.BeginningOfHour().Add(time.Hour - time.Nanosecond) +} + +// EndOfDay end of day +func (now *Now) EndOfDay() time.Time { + y, m, d := now.Date() + return time.Date(y, m, d, 23, 59, 59, int(time.Second-time.Nanosecond), now.Location()) +} + +// EndOfWeek end of week +func (now *Now) EndOfWeek() time.Time { + return now.BeginningOfWeek().AddDate(0, 0, 7).Add(-time.Nanosecond) +} + +// EndOfMonth end of month +func (now *Now) EndOfMonth() time.Time { + return now.BeginningOfMonth().AddDate(0, 1, 0).Add(-time.Nanosecond) +} + +// EndOfQuarter end of quarter +func (now *Now) EndOfQuarter() time.Time { + return now.BeginningOfQuarter().AddDate(0, 3, 0).Add(-time.Nanosecond) +} + +// EndOfHalf end of half year +func (now *Now) EndOfHalf() time.Time { + return now.BeginningOfHalf().AddDate(0, 6, 0).Add(-time.Nanosecond) +} + +// EndOfYear end of year +func (now *Now) EndOfYear() time.Time { + return now.BeginningOfYear().AddDate(1, 0, 0).Add(-time.Nanosecond) +} + +// Monday monday +/* +func (now *Now) Monday() time.Time { + t := now.BeginningOfDay() + weekday := int(t.Weekday()) + if weekday == 0 { + weekday = 7 + } + return t.AddDate(0, 0, -weekday+1) +} +*/ + +func (now *Now) Monday(strs ...string) time.Time { + var parseTime time.Time + var err error + if len(strs) > 0 { + parseTime, err = now.Parse(strs...) + if err != nil { + panic(err) + } + } else { + parseTime = now.BeginningOfDay() + } + weekday := int(parseTime.Weekday()) + if weekday == 0 { + weekday = 7 + } + return parseTime.AddDate(0, 0, -weekday+1) +} + +func (now *Now) Sunday(strs ...string) time.Time { + var parseTime time.Time + var err error + if len(strs) > 0 { + parseTime, err = now.Parse(strs...) + if err != nil { + panic(err) + } + } else { + parseTime = now.BeginningOfDay() + } + weekday := int(parseTime.Weekday()) + if weekday == 0 { + weekday = 7 + } + return parseTime.AddDate(0, 0, (7 - weekday)) +} + +// EndOfSunday end of sunday +func (now *Now) EndOfSunday() time.Time { + return New(now.Sunday()).EndOfDay() +} + +// Quarter returns the yearly quarter +func (now *Now) Quarter() uint { + return (uint(now.Month())-1)/3 + 1 +} + +func (now *Now) parseWithFormat(str string, location *time.Location) (t time.Time, err error) { + for _, format := range now.TimeFormats { + t, err = time.ParseInLocation(format, str, location) + + if err == nil { + return + } + } + err = errors.New("Can't parse string as time: " + str) + return +} + +var hasTimeRegexp = regexp.MustCompile(`(\s+|^\s*|T)\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))(\s*$|[Z+-])`) // match 15:04:05, 15:04:05.000, 15:04:05.000000 15, 2017-01-01 15:04, 2021-07-20T00:59:10Z, 2021-07-20T00:59:10+08:00, 2021-07-20T00:00:10-07:00 etc +var onlyTimeRegexp = regexp.MustCompile(`^\s*\d{1,2}((:\d{1,2})*|((:\d{1,2}){2}\.(\d{3}|\d{6}|\d{9})))\s*$`) // match 15:04:05, 15, 15:04:05.000, 15:04:05.000000, etc + +// Parse parse string to time +func (now *Now) Parse(strs ...string) (t time.Time, err error) { + var ( + setCurrentTime bool + parseTime []int + currentLocation = now.Location() + onlyTimeInStr = true + currentTime = formatTimeToList(now.Time) + ) + + for _, str := range strs { + hasTimeInStr := hasTimeRegexp.MatchString(str) // match 15:04:05, 15 + onlyTimeInStr = hasTimeInStr && onlyTimeInStr && onlyTimeRegexp.MatchString(str) + if t, err = now.parseWithFormat(str, currentLocation); err == nil { + location := t.Location() + parseTime = formatTimeToList(t) + + for i, v := range parseTime { + // Don't reset hour, minute, second if current time str including time + if hasTimeInStr && i <= 3 { + continue + } + + // If value is zero, replace it with current time + if v == 0 { + if setCurrentTime { + parseTime[i] = currentTime[i] + } + } else { + setCurrentTime = true + } + + // if current time only includes time, should change day, month to current time + if onlyTimeInStr { + if i == 4 || i == 5 { + parseTime[i] = currentTime[i] + continue + } + } + } + + t = time.Date(parseTime[6], time.Month(parseTime[5]), parseTime[4], parseTime[3], parseTime[2], parseTime[1], parseTime[0], location) + currentTime = formatTimeToList(t) + } + } + return +} + +// MustParse must parse string to time or it will panic +func (now *Now) MustParse(strs ...string) (t time.Time) { + t, err := now.Parse(strs...) + if err != nil { + panic(err) + } + return t +} + +// Between check time between the begin, end time or not +func (now *Now) Between(begin, end string) bool { + beginTime := now.MustParse(begin) + endTime := now.MustParse(end) + return now.After(beginTime) && now.Before(endTime) +} diff --git a/vendor/github.com/jinzhu/now/time.go b/vendor/github.com/jinzhu/now/time.go new file mode 100644 index 000000000..52dd8b2a0 --- /dev/null +++ b/vendor/github.com/jinzhu/now/time.go @@ -0,0 +1,9 @@ +package now + +import "time" + +func formatTimeToList(t time.Time) []int { + hour, min, sec := t.Clock() + year, month, day := t.Date() + return []int{t.Nanosecond(), sec, min, hour, day, int(month), year} +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 254a1cd48..bcb4678af 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -78,9 +78,6 @@ github.com/alexedwards/argon2id # github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 ## explicit github.com/amoghe/go-crypt -# github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de -## explicit; go 1.12 -github.com/araddon/dateparse # github.com/armon/go-metrics v0.4.1 ## explicit; go 1.12 github.com/armon/go-metrics @@ -1172,6 +1169,9 @@ github.com/jellydator/ttlcache/v2 # github.com/jellydator/ttlcache/v3 v3.1.0 ## explicit; go 1.18 github.com/jellydator/ttlcache/v3 +# github.com/jinzhu/now v1.1.5 +## explicit; go 1.12 +github.com/jinzhu/now # github.com/jmespath/go-jmespath v0.4.0 ## explicit; go 1.14 github.com/jmespath/go-jmespath